我对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);
    }
}

当前回答

它有助于不关注讨论的强或弱部分。相反,专注于周期部分。

保留循环是当对象A保留对象B,对象B保留对象A时发生的循环。在这种情况下,如果任何一个对象被释放:

对象A不会被释放,因为对象B持有对它的引用。 但是只要对象A有对对象B的引用,对象B就永远不会被释放。 但是对象A永远不会被释放,因为对象B持有对它的引用。 无限

因此,这两个对象将在程序的整个生命周期内一直驻留在内存中,即使它们应该(如果一切正常)被释放。

所以,我们担心的是保留循环,而block本身并没有创建这些循环。这不是问题,例如:

[myArray enumerateObjectsUsingBlock:^(id obj, NSUInteger idx, BOOL *stop){
   [self doSomethingWithObject:obj];
}];

block保留self,但self不保留block。如果一个或另一个被释放,就不会创建任何循环,所有的东西都应该被释放。

你遇到麻烦的地方是这样的:

//In the interface:
@property (strong) void(^myBlock)(id obj, NSUInteger idx, BOOL *stop);

//In the implementation:
[self setMyBlock:^(id obj, NSUInteger idx, BOOL *stop) {
  [self doSomethingWithObj:obj];     
}];

现在,您的对象(self)具有对块的显式强引用。block有一个隐含的对self的强引用。这是一个循环,现在这两个对象都将被正确地释放。

因为,在这种情况下,self根据定义已经有一个对块的强引用,通常最容易解决的方法是显式地对self进行弱引用以供块使用:

__weak MyObject *weakSelf = self;
[self setMyBlock:^(id obj, NSUInteger idx, BOOL *stop) {
  [weakSelf doSomethingWithObj:obj];     
}];

但这不应该是处理调用self的块时遵循的默认模式!这应该只用于打破原本是self和块之间的保留循环。如果在所有地方都采用这种模式,就会冒着将一个块传递给某个在self被释放后才执行的对象的风险。

//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];
}];

其他回答

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

我完全同意@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];
}];

下面是如何在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中:

它有助于不关注讨论的强或弱部分。相反,专注于周期部分。

保留循环是当对象A保留对象B,对象B保留对象A时发生的循环。在这种情况下,如果任何一个对象被释放:

对象A不会被释放,因为对象B持有对它的引用。 但是只要对象A有对对象B的引用,对象B就永远不会被释放。 但是对象A永远不会被释放,因为对象B持有对它的引用。 无限

因此,这两个对象将在程序的整个生命周期内一直驻留在内存中,即使它们应该(如果一切正常)被释放。

所以,我们担心的是保留循环,而block本身并没有创建这些循环。这不是问题,例如:

[myArray enumerateObjectsUsingBlock:^(id obj, NSUInteger idx, BOOL *stop){
   [self doSomethingWithObject:obj];
}];

block保留self,但self不保留block。如果一个或另一个被释放,就不会创建任何循环,所有的东西都应该被释放。

你遇到麻烦的地方是这样的:

//In the interface:
@property (strong) void(^myBlock)(id obj, NSUInteger idx, BOOL *stop);

//In the implementation:
[self setMyBlock:^(id obj, NSUInteger idx, BOOL *stop) {
  [self doSomethingWithObj:obj];     
}];

现在,您的对象(self)具有对块的显式强引用。block有一个隐含的对self的强引用。这是一个循环,现在这两个对象都将被正确地释放。

因为,在这种情况下,self根据定义已经有一个对块的强引用,通常最容易解决的方法是显式地对self进行弱引用以供块使用:

__weak MyObject *weakSelf = self;
[self setMyBlock:^(id obj, NSUInteger idx, BOOL *stop) {
  [weakSelf doSomethingWithObj:obj];     
}];

但这不应该是处理调用self的块时遵循的默认模式!这应该只用于打破原本是self和块之间的保留循环。如果在所有地方都采用这种模式,就会冒着将一个块传递给某个在self被释放后才执行的对象的风险。

//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];
}];