如何避免在Objective-C中保留块

时间:2014-03-29 11:25:06

标签: ios objective-c automatic-ref-counting objective-c-blocks

我有一个执行webservice调用的块队列。问题是在块结束后没有释放下载的数据。我读了很多关于保留的内容,但我无法让ARC成为记忆。

以下是代码:

  1. 创建下载数据的块队列

    - (void)syncData
    {
        dispatch_semaphore_t sema = dispatch_semaphore_create(0);
        dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT,0);
    
        dispatch_async(queue, ^{
    
            [Model syncAziende:^(id response, NSError *error) {
                dispatch_semaphore_signal(sema);
            }];
            dispatch_semaphore_wait(sema, DISPATCH_TIME_FOREVER);
    
            [Model syncContatti:^(id response, NSError *error) {
                dispatch_semaphore_signal(sema);
            }];
            dispatch_semaphore_wait(sema, DISPATCH_TIME_FOREVER);
    
            [Model syncDestinazioni:^(id response, NSError *error) {
                dispatch_semaphore_signal(sema);
            }];
            dispatch_semaphore_wait(sema, DISPATCH_TIME_FOREVER);
    
            and so on...
    
        });
    }
    
  2. 在Model.m

    + (void)syncAziende:(RequestFinishBlock)completation
    {
        __weak typeof(self)selfObject = self;
    
        [selfObject syncData:^(id response, NSError *error) {
            completation(response,error);
        } wsEndPoint:kCDCEndPointGetAziende tableName:kCDCDBAziendeTableName];
    }
    
    + (void)syncContatti:(RequestFinishBlock)completation
    {
        __weak typeof(self)selfObject = self;
    
        [selfObject syncData:^(id response, NSError *error) {
            completation(response,error);
        } wsEndPoint:kCDCEndPointGetContatti tableName:kCDCDBContattiTableName];
    }
    
    // and so on...
    
  3. syncData的位置:

    + (void)syncData:(RequestFinishBlock)completation wsEndPoint:(NSString*) url tableName:(NSString *)table
    {
        __weak typeof(self)selfObject = self;
    
        [selfObject getDataFromWS:^(id WSresponse, NSError* WSError)
         {
             completation(nil,nil);
         }WSUrl:url];
    }
    
  4. getDataFromWS的位置:

    + (void)getDataFromWS:(RequestFinishBlock)completation WSUrl:(NSString *)svcUrl
    {
        __weak typeof(self)selfObject = self;
        [selfObject getJsonDataFromURL:^(id response, NSError *error)
         {
             completation(response,error);
         }url:svcUrl];
    }
    
  5. getJsonDataFromURL的位置:

    +(void)getJsonDataFromURL:(RequestFinishBlock)completation url:(NSString*)url
    {
        __weak typeof(self)selfObject = self;
    
        __weak AFHTTPRequestOperationManager *manager = [selfObject getAuthorizedRequestionOperationManager];
    
        [manager.requestSerializer setValue:@"application/json" forHTTPHeaderField:@"Accept"];
        [manager.requestSerializer setValue:@"application/json" forHTTPHeaderField:@"Content-Type"];
        [manager.requestSerializer setValue:@"gzip" forHTTPHeaderField:@"Accept-Encoding"];
    
        [manager GET:url parameters:nil success:^(AFHTTPRequestOperation *operation, __weak id responseObject) {
            completation([responseObject objectForKey:@"d"],nil);
        } failure:^(AFHTTPRequestOperation *operation, NSError *error) {
            completation(nil,error);
        }];
    }
    

2 个答案:

答案 0 :(得分:0)

这些__weak引用在类方法的上下文中并不真正有意义。在处理实例方法时使用weak - self模式才有意义。

所以,这就引出了一个问题,即你担心哪个对象没有被释放。我在这里看不到会导致保留syncData作为实例方法的对象的任何内容。

话虽如此,通过使用信号量使您同步的一系列网络请求的这个例程将(a)保留在整个过程中实例化的任何自动释放对象(特别是操作本身); (b)一旦开始,不允许您取消该过程; (c)连续执行请求(对此您将支付严重的性能损失,除非您绝对必须,否则应该避免)。

我建议简化代码。我将使用信号量消除调度代码。我也在类方法中删除了weakself的所有引用。

您只需发出GET次请求即可。如果您确实需要它们以串行方式运行(正如您使用信号量所暗示的那样),那么只需将maxConcurrentOperationCount设置为1 operationQueue实例的AFHTTPRequestOperationManager即可。但是,除非你必须这样做,否则不要让它们连续运行,因为你这样做会给性能带来很大的损失。但是可以利用AFNetworking提供的NSOperationQueue,而不是使用自己的GCD代码(更糟糕的是,使用该GCD代码中的信号量)。

但是这样,如果您要取消请求,则可以为cancelAllOperations的{​​{1}}致电operationQueue。您还可以使用AFHTTPRequestOperationManager来控制并发度。

答案 1 :(得分:0)

鉴于你的描述并基于评论,一个天真 - 但仍然不足 - 解决方案可能如下所示:

//通用完成处理程序:     typedef void(^ completion_t)(id result,NSError * error);

- (void) fetchJSONFromURL:(NSURL*)url completion:(completion_t)completion;

- (void) syncAziendeWithInput:(id)input completion:(completion_t)completion;

- (void) syncContattiWithInput:(id)input completion:(completion_t)completion;

- (void) syncDestinazioniWithInput:(id)input completion:(completion_t)completion;



- (void) syncData 
{
    dispatch_queue_t sync_queue = dispatch_queue_create("sync_queue", 0); // serial queue

    NSURL* url = ...;
    [self fetchJSONFromURL:url completion:^(id result, NSError*error){
        dispatch_async(sync_queue, ^{
            [self syncAziendeWithInput:result completion:^(id result, NSError* error){
                ...
            }];
        });
    }];

    url = ...;
    [self fetchJSONFromURL:url completion:^(id result, NSError*error){
        dispatch_async(sync_queue, ^{
            [self syncContattiWithInput:result completion:^(id result, NSError* error){
                ...
            }];
        });
    }];

    url = ...;
    [self fetchJSONFromURL:url completion:^(id result, NSError*error){
        dispatch_async(sync_queue, ^{
            [self syncDestinazioniWithInput:result completion:^(id result, NSError* error){
                ...
            }];
        });
    }];


    ...
}

<强>注意事项:

简单化。一个实用的解决方案变得更加精细:

  • 尚无错误处理。

  • 无法取消异步任务。

  • syncData是异步的,但没有完成处理程序。也就是说,我们不知道什么时候完成。我们可以利用dispatch_group来实现一种方法来表示许多异步方法的完成。

在第三方库的帮助下,我们可以实现一个完成上述所有工作的解决方案,看起来基本上更简单。