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

当前回答

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

其他回答

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

//调用块

 NSString *returnedText= checkIfOutsideMethodIsCalled(self);

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


};

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

保留循环是当对象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];
}];

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

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

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