美文网首页
iOS内存管理(三)ARC内存泄漏

iOS内存管理(三)ARC内存泄漏

作者: 朽木自雕也 | 来源:发表于2017-10-09 17:26 被阅读75次

    最近在对一个项目的内存泄漏进行排查,发现存在很多内存泄漏,还有第三方类库里面存在对象释放不掉的情况

    AFNetWorking

    作为一位iOS开发者,网络请求类AFNetWorking是再熟悉不过了,如果你不知道,那你就太奥特了。对于AFNetWorking的使用通常会对用参数、网址环境切换、网络状态监测、请求错误信息等进行封装。在封装请求类时需要注意的是需要请求队列管理者AFHTTPSessionManager声明为单例创建形式。对于这个问题,AFNetWorking的作者在github上也指出建议使用者在相同配置下保证AFHTTPSessionManager只有一个,进行全局管理,因此我们可以通过单例形式进行解决。

    @implementation MXHTTPRequst
    + (AFHTTPSessionManager *)manager {
        static AFHTTPSessionManager *manager = nil;
        static dispatch_once_t onceToken;
        dispatch_once(&onceToken, ^{
            manager = [AFHTTPSessionManager manager];
            manager.responseSerializer = [AFHTTPResponseSerializer serializer];
        });
        return manager;
    }
    + (void)GET:(NSString *)url parameters:(NSDictionary *)getDictionary returnData:(void (^)(NSData * resultData,NSError * error))returnBlock {
        //请求队列管理者 单例创建形式 防止内存泄漏
        AFHTTPSessionManager *manager = [self manager];
        [manager GET:url parameters:getDictionary progress:^(NSProgress * _Nonnull downloadProgress) {
        } success:^(NSURLSessionDataTask * _Nonnull task, id  _Nullable responseObject) {
            returnBlock(responseObject,nil);
        } failure:^(NSURLSessionDataTask * _Nullable task, NSError * _Nonnull error) {
            returnBlock(nil,error);
        }];
    }
    @end
    

    Block循环引用

    Block循环引用的问题是老生常谈了,至今网上已经有很多文章解释原理及造成循环引用的原因,在下就不逼逼了。总之记住一句话“对象之间不要出现封闭的环”,就能防止Block循环引用。我就拿MJRefresh列举一下吧,其他的代码太多了😆:

    self.tableView.mj_header = [MJRefreshNormalHeader headerWithRefreshingBlock:^{
            self.page = 1;
            [self.dataArr removeAllObjects];
            [self loadData];
    }];
    

    若在MJRefresh的执行Block中调用当前self或者属性,一定要注意循环引用问题

    #pragma mark - 构造方法
    + (instancetype)headerWithRefreshingBlock:(MJRefreshComponentRefreshingBlock)refreshingBlock
    {
        MJRefreshHeader *cmp = [[self alloc] init];
        cmp.refreshingBlock = refreshingBlock;
        return cmp;
    }
    

    上面为MJRefreshNormalHeader的构造方法,注意看一下这一行代码“cmp.refreshingBlock = refreshingBlock”,refreshingBlock是属于MJRefreshHeader的强引用属性,最后header会成为我们自己tableView的强引用属性mj_header,也就是说self.tableView强引用header, header强引用refreshingBlock,如果refreshingBlock里面强引用self,就成了循环引用,如图:


    image

    所以需要破掉这个环就得使用weakSelf

    __weak id weakSelf = self;
    self.tableView.mj_header = [MJRefreshNormalHeader headerWithRefreshingBlock:^{
              __strong typeof(self) strongSelf = weakSelf;
            strongSelf.page = 1;
            [strongSelf.dataArr removeAllObjects];
            [strongSelf loadData];
    }];
    
    结果为 image

    delegate循环引用

    delegate是一个非常基础的问题了,吧修饰符改成weak就👌:@property (nonatomic,weak)id delegate;
    这里就不再赘述
    比较形象的事UITextView和UIViewController

    image

    NSTimer循环引用

    对于定时器NSTimer,使用不正确也会造成内存泄漏问题,在初始化传入target参数时记得用weak,还有当不需要定时器时需要执行这两行代码:

     [_timer invalidate];
     _timer = nil;
    

    非OC对象内存处理

    对于iOS开发,ARC模式已发扬光大多年,可能很多人早已忘记当年retain、release的年代,但ARC的出现并不是说我们完全可以忽视内存泄漏的问题。对于一些非OC对象,使用完毕后其内存仍需要我们手动释放。

    举个例子,比如常用的滤镜操作调节图片亮度

    CIImage *beginImage = [[CIImage alloc]initWithImage:[UIImage imageNamed:@"yourname.jpg"]];
    
    CIFilter *filter = [CIFilter filterWithName:@"CIColorControls"];
    
    [filter setValue:beginImage forKey:kCIInputImageKey];
    
    [filter setValue:[NSNumber numberWithFloat:.5] forKey:@"inputBrightness"];//亮度-1~1
    
    CIImage *outputImage = [filter outputImage];
    
    //GPU优化
    
    EAGLContext * eaglContext = [[EAGLContext alloc] initWithAPI:kEAGLRenderingAPIOpenGLES3];
    
    eaglContext.multiThreaded = YES;
    
    CIContext *context = [CIContext contextWithEAGLContext:eaglContext];
    
    [EAGLContext setCurrentContext:eaglContext];
    
    CGImageRef ref = [context createCGImage:outputImage fromRect:outputImage.extent];
    
    UIImage *endImg = [UIImage imageWithCGImage:ref];
    
    _imageView.image = endImg;
    
    CGImageRelease(ref);//非OC对象需要手动内存释放
    

    在如上代码中的CGImageRef类型变量非OC对象,其需要手动执行释放操作CGImageRelease(ref),否则会造成大量的内存泄漏导致程序崩溃。其他的对于CoreFoundation框架下的某些对象或变量需要手动释放、C语言代码中的malloc等需要对应free等都需要注意。

    地图类处理

    如果项目中使用抵触相关类,一定要检测内存情况,因为地图是相当耗费内存的,因此根据文档实现地图相关功能的同时,还必须注意内存的正确释放,大体需要注意的有需在使用完毕时将地图、代理赋值为nil,注意地图中的大头针的服用,并且在使用完毕时清空大头针数组等:

    - (void)clearMapView{
        self.mapView = nil;
        self.mapView.delegate =nil;
        self.mapView.showsUserLocation = NO;
        [self.mapView removeAnnotations:self.annotations];
        [self.mapView removeOverlays:self.overlays];
        [self.mapView setCompassImage:nil];
    }
    

    大次数循环内存暴涨问题

    for (int i = 0; i < 100000; i++) {
            NSString *string = @"Abc";
            string = [string lowercaseString];
            string = [string stringByAppendingString:@"xyz"];
            NSLog(@"%@", string);
    }
    

    该循环内产生大量的临时对象,直至循环结束才释放,可能导致内存泄漏,解决方法在循环体内加入自己的@autoreleasepool,用来及时释放大量的临时变量,减少内存占用峰值。

    for (int i = 0; i < 100000; i++) {
        @autoreleasepool{
            NSString *string = @"Abc";
            string = [string lowercaseString];
            string = [string stringByAppendingString:@"xyz"];
            NSLog(@"%@", string);
        }
    }
    

    相关文章

      网友评论

          本文标题:iOS内存管理(三)ARC内存泄漏

          本文链接:https://www.haomeiwen.com/subject/chbuyxtx.html