我有一个工作的应用程序,我正在把它转换成Xcode 4.2中的ARC。预检查警告之一涉及在块中强烈捕获self,从而导致保留循环。我做了一个简单的代码示例来说明这个问题。我相信我理解这意味着什么,但我不确定实现这类场景的“正确”或推荐方法。

self是MyAPI类的实例 下面的代码经过简化,只显示与我的问题相关的对象和块的交互 假设MyAPI从远程数据源获取数据,MyDataProcessor处理该数据并产生输出 处理器配置了块来通信进程和状态

代码示例:

// code sample
self.delegate = aDelegate;

self.dataProcessor = [[MyDataProcessor alloc] init];

self.dataProcessor.progress = ^(CGFloat percentComplete) {
    [self.delegate myAPI:self isProcessingWithProgress:percentComplete];
};

self.dataProcessor.completion = ^{
    [self.delegate myAPIDidFinish:self];
    self.dataProcessor = nil;
};

// start the processor - processing happens asynchronously and the processor is released in the completion block
[self.dataProcessor startProcessing];

问题:我做了什么“错误”和/或这应该如何修改,以符合ARC公约?


当前回答

我相信没有ARC的解决方案也适用于ARC,使用__block关键字:

编辑:根据过渡到ARC的发布说明,使用__block存储声明的对象仍然保留。使用__weak(首选)或__unsafe_unretained(向后兼容)。

// code sample
self.delegate = aDelegate;

self.dataProcessor = [[MyDataProcessor alloc] init];

// Use this inside blocks
__block id myself = self;

self.dataProcessor.progress = ^(CGFloat percentComplete) {
    [myself.delegate myAPI:myself isProcessingWithProgress:percentComplete];
};

self.dataProcessor.completion = ^{
    [myself.delegate myAPIDidFinish:myself];
    myself.dataProcessor = nil;
};

// start the processor - processing happens asynchronously and the processor is released in the completion block
[self.dataProcessor startProcessing];

其他回答

结合其他一些答案,这是我现在在块中使用的类型化弱自我:

__typeof(self) __weak welf = self;

我将其设置为在方法/函数中带有“welf”完成前缀的XCode代码片段,在只输入“we”后命中。

对于一个常见的解决方案,我在预编译头中定义了这些。避免捕获,并通过避免使用id启用编译器帮助

#define BlockWeakObject(o) __typeof(o) __weak
#define BlockWeakSelf BlockWeakObject(self)

然后在代码中你可以这样做:

BlockWeakSelf weakSelf = self;
self.dataProcessor.completion = ^{
    [weakSelf.delegate myAPIDidFinish:weakSelf];
    weakSelf.dataProcessor = nil;
};

如果你确定你的代码不会创建一个保留循环,或者这个循环稍后会被打破,那么最简单的方法是:

// code sample
self.delegate = aDelegate;

self.dataProcessor = [[MyDataProcessor alloc] init];

[self dataProcessor].progress = ^(CGFloat percentComplete) {
    [self.delegate myAPI:self isProcessingWithProgress:percentComplete];
};

[self dataProcessor].completion = ^{
    [self.delegate myAPIDidFinish:self];
    self.dataProcessor = nil;
};

// start the processor - processing happens asynchronously and the processor is released in the completion block
[self.dataProcessor startProcessing];

这样做的原因是Xcode的分析考虑到了属性的点访问,因此

x.y.z = ^{ block that retains x}

被视为保留x的y(在赋值的左边)和y的x(在右边),方法调用不受相同的分析,即使当它们是属性访问方法调用,等价于点访问,即使当这些属性访问方法是编译器生成的,那么在

[x y].z = ^{ block that retains x}

只有右侧被视为创建保留(通过y (x)),并且不会生成保留周期警告。

当你确信这个循环在未来会被打破时,你也可以选择抑制警告:

#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Warc-retain-cycles"

self.progressBlock = ^(CGFloat percentComplete) {
    [self.delegate processingWithProgress:percentComplete];
}

#pragma clang diagnostic pop

这样你就不必在__weak, self aliasing和显式的ivar前缀上瞎折腾了。

新的方法是使用@weakify和@ stronggify marco

@weakify(self);
[self methodThatTakesABlock:^ {
    @strongify(self);
    [self doSomething];
}];

更多关于@Weakify @ stronggify马可的信息