AFNetwork 使用中遇到的坑,循环引用

2016-01-29  本文已影响2007人  yww

最近在写一个 SDK ,用到了 AFNetwork,其中有一段代码如下:

...
@property(nonatomic,strong)AFHTTPSessionManager *request;
....
-(void)excute{
    ...
    @weakify(self);
    [self.request POST:self.api.APIPath
            parameters:self.api.params
              progress:nil
               success:^(NSURLSessionDataTask * _Nonnull task, id  _Nullable responseObject) {
                   @strongify(self);
                   [self handelResponseComplete:responseObject];
               }
               failure:^(NSURLSessionDataTask * _Nullable task, NSError * _Nonnull error) {
                   @strongify(self);
                   [self handelResponseError:error];
               }];
    ...
}

本以为没问题,结果每次使用才发现,回调中的 self 已经被释放了.想了一下,还真是,由于这个类在使用中是被作为临时变量来使用的,生命周期一结束,没有强引用,就完蛋了.由于想要先写一点功能,就干脆直接把@strongify(self);去掉了,直接上,这次倒不会被释放,倒是老觉得会循环引用.
始终还是不放心,在-dealoc方法中加上打印输出,看看代码.
突然发现,居然会正常释放!
这还了得,赶紧看看是不是哪里弄错了.找了半天好像也没找到我哪里写错了,还是看看 AFNetwork 源代码吧.
,先来看看这个post方法

- (NSURLSessionDataTask *)POST:(NSString *)URLString
                    parameters:(id)parameters
                      progress:(void (^)(NSProgress * _Nonnull))uploadProgress
                       success:(void (^)(NSURLSessionDataTask * _Nonnull, id _Nullable))success
                       failure:(void (^)(NSURLSessionDataTask * _Nullable, NSError * _Nonnull))failure
{
    NSURLSessionDataTask *dataTask = [self dataTaskWithHTTPMethod:@"POST" URLString:URLString parameters:parameters uploadProgress:uploadProgress downloadProgress:nil success:success failure:failure];

    [dataTask resume];

    return dataTask;
}

再一路跟进,到了AFURLSessionManager的这个方法

- (void)addDelegateForDataTask:(NSURLSessionDataTask *)dataTask
                uploadProgress:(nullable void (^)(NSProgress *uploadProgress)) uploadProgressBlock
              downloadProgress:(nullable void (^)(NSProgress *downloadProgress)) downloadProgressBlock
             completionHandler:(void (^)(NSURLResponse *response, id responseObject, NSError *error))completionHandler
{
    AFURLSessionManagerTaskDelegate *delegate = [[AFURLSessionManagerTaskDelegate alloc] init];
    delegate.manager = self;
    delegate.completionHandler = completionHandler;

    dataTask.taskDescription = self.taskDescriptionForSessionTasks;
    [self setDelegate:delegate forTask:dataTask];

    delegate.uploadProgressBlock = uploadProgressBlock;
    delegate.downloadProgressBlock = downloadProgressBlock;
}

这里创建了一个代理类,然后把 block 保存在代理类中,而代理类本身通过这个方法

- (void)setDelegate:(AFURLSessionManagerTaskDelegate *)delegate
            forTask:(NSURLSessionTask *)task
{
    NSParameterAssert(task);
    NSParameterAssert(delegate);

    [self.lock lock];
    self.mutableTaskDelegatesKeyedByTaskIdentifier[@(task.taskIdentifier)] = delegate;
    [delegate setupProgressForTask:task];
    [self addNotificationObserverForTask:task];
    [self.lock unlock];
}

存放在了一个字典属性中.
来捋一捋
self(我自己代码) -持有> requst(AFHTTPSessionManager)-持有>mutableTaskDelegatesKeyedByTaskIdentifier(字典)-持有>delegate-持有>block-持有>self(我自己代码中的)
好大一个环,不过这不还是一个环吗?
不理解.
又来打断点调试

调试

看到一个这个代码

-URLSession:task:didCompleteWithError:

有一句[self removeDelegateForTask:task];这不刚刚和那个-setDelegate:forTask:对应吗?去看看代码

- (void)removeDelegateForTask:(NSURLSessionTask *)task {
    NSParameterAssert(task);

    AFURLSessionManagerTaskDelegate *delegate = [self delegateForTask:task];
    [self.lock lock];
    [delegate cleanUpProgressForTask:task];
    [self removeNotificationObserverForTask:task];
    [self.mutableTaskDelegatesKeyedByTaskIdentifier removeObjectForKey:@(task.taskIdentifier)];
    [self.lock unlock];
}

果然, AFNetwork 在请求结束后会自动释放掉 delegate,打破那个环,所以循环引用也就不存在了.

上一篇 下一篇

猜你喜欢

热点阅读