我对Objective-C中的块用法有点困惑。我目前使用ARC,我有相当多的块在我的应用程序,目前总是引用自我,而不是它的弱引用。这可能是这些块保留自我并阻止它被释放的原因吗?问题是,我应该总是在块中使用self的弱引用吗?

-(void)handleNewerData:(NSArray *)arr
{
    ProcessOperation *operation =
    [[ProcessOperation alloc] initWithDataToProcess:arr
                                         completion:^(NSMutableArray *rows) {
        dispatch_async(dispatch_get_main_queue(), ^{
            [self updateFeed:arr rows:rows];
        });
    }];
    [dataProcessQueue addOperation:operation];
}

进程操作.h

@interface ProcessOperation : NSOperation
{
    NSMutableArray *dataArr;
    NSMutableArray *rowHeightsArr;
    void (^callback)(NSMutableArray *rows);
}

ProcessOperation.m

-(id)initWithDataToProcess:(NSArray *)data completion:(void (^)(NSMutableArray *rows))cb{

    if(self =[super init]){
        dataArr = [NSMutableArray arrayWithArray:data];
        rowHeightsArr = [NSMutableArray new];
        callback = cb;
    }
    return self;
}

- (void)main {
    @autoreleasepool {
        ...
        callback(rowHeightsArr);
    }
}

当前回答

As Leo points out, the code you added to your question would not suggest a strong reference cycle (a.k.a., retain cycle). One operation-related issue that could cause a strong reference cycle would be if the operation is not getting released. While your code snippet suggests that you have not defined your operation to be concurrent, but if you have, it wouldn't be released if you never posted isFinished, or if you had circular dependencies, or something like that. And if the operation isn't released, the view controller wouldn't be released either. I would suggest adding a breakpoint or NSLog in your operation's dealloc method and confirm that's getting called.

你说:

我理解保留周期的概念,但我不太确定在块中会发生什么,所以这让我有点困惑

The retain cycle (strong reference cycle) issues that occur with blocks are just like the retain cycle issues you're familiar with. A block will maintain strong references to any objects that appear within the block, and it will not release those strong references until the block itself is released. Thus, if block references self, or even just references an instance variable of self, that will maintain strong reference to self, that is not resolved until the block is released (or in this case, until the NSOperation subclass is released.

有关更多信息,请参见“使用Objective-C编程:使用块”文档中的“在捕获自我时避免强引用循环”部分。

如果你的视图控制器仍然没有被释放,你只需要确定未解决的强引用驻留在哪里(假设你确认NSOperation正在被释放)。一个常见的例子是使用重复的NSTimer。或错误地维护强引用的某个自定义委托或其他对象。你可以经常使用工具来追踪对象在哪里获得它们的强引用,例如:

或者在Xcode 5中:

其他回答

你不必总是使用弱引用。如果你的块没有被保留,而是被执行然后被丢弃,你可以强捕获self,因为它不会创建一个保留周期。在某些情况下,你甚至希望block持有self直到block完成,这样它就不会过早释放。但是,如果您强力捕获块,并且在capture self内部,它将创建一个保留循环。

一些解释忽略了保留循环的条件[如果一组对象由一个强关系的圈连接,即使没有来自组外的强引用,它们也会使彼此存活。要了解更多信息,请阅读文档

下面是如何在block中使用self:

//调用块

 NSString *returnedText= checkIfOutsideMethodIsCalled(self);

NSString* (^checkIfOutsideMethodIsCalled)(*)=^NSString*(id obj)
{
             [obj MethodNameYouWantToCall]; // this is how it will call the object 
            return @"Called";


};

As Leo points out, the code you added to your question would not suggest a strong reference cycle (a.k.a., retain cycle). One operation-related issue that could cause a strong reference cycle would be if the operation is not getting released. While your code snippet suggests that you have not defined your operation to be concurrent, but if you have, it wouldn't be released if you never posted isFinished, or if you had circular dependencies, or something like that. And if the operation isn't released, the view controller wouldn't be released either. I would suggest adding a breakpoint or NSLog in your operation's dealloc method and confirm that's getting called.

你说:

我理解保留周期的概念,但我不太确定在块中会发生什么,所以这让我有点困惑

The retain cycle (strong reference cycle) issues that occur with blocks are just like the retain cycle issues you're familiar with. A block will maintain strong references to any objects that appear within the block, and it will not release those strong references until the block itself is released. Thus, if block references self, or even just references an instance variable of self, that will maintain strong reference to self, that is not resolved until the block is released (or in this case, until the NSOperation subclass is released.

有关更多信息,请参见“使用Objective-C编程:使用块”文档中的“在捕获自我时避免强引用循环”部分。

如果你的视图控制器仍然没有被释放,你只需要确定未解决的强引用驻留在哪里(假设你确认NSOperation正在被释放)。一个常见的例子是使用重复的NSTimer。或错误地维护强引用的某个自定义委托或其他对象。你可以经常使用工具来追踪对象在哪里获得它们的强引用,例如:

或者在Xcode 5中:

我完全同意@jemmons的观点:

But this should not be the default pattern you follow when dealing with blocks that call self! This should only be used to break what would otherwise be a retain cycle between self and the block. If you were to adopt this pattern everywhere, you'd run the risk of passing a block to something that got executed after self was deallocated. //SUSPICIOUS EXAMPLE: __weak MyObject *weakSelf = self; [[SomeOtherObject alloc] initWithCompletion:^{ //By the time this gets called, "weakSelf" might be nil because it's not retained! [weakSelf doSomething]; }];

为了克服这个问题,可以在块内的weakSelf上定义一个强引用:

__weak MyObject *weakSelf = self;
[[SomeOtherObject alloc] initWithCompletion:^{
  MyObject *strongSelf = weakSelf;
  [strongSelf doSomething];
}];