简体   繁体   English

使用块,ARC和非ARC进行目标C内存管理

[英]Objective C memory management with blocks, ARC and non-ARC

I have been using blocks for some time now, but I feel that there are things I miss about memory management in both ARC and non-ARC environments. 我一直在使用块一段时间,但我觉得在ARC和非ARC环境中都有一些我想念的内存管理。 I feel that deeper understanding will make me void many memory leaks. 我觉得更深刻的理解会让我失去许多内存泄漏。

AFNetworking is my main use of Blocks in a particular application. AFNetworking是我在特定应用程序中主要使用Blocks。 Most of the time, inside a completion handler of an operation, I do something like "[self.myArray addObject]". 大多数情况下,在操作的完成处理程序中,我执行类似“[self.myArray addObject]”的操作。

In both ARC and non-ARC enabled environments, "self" will be retained according to this article from Apple . 在ARC和非ARC启用的环境中,根据Apple的这篇文章,将保留“self”。

That means that whenever a completion block of an AFNetworking network operation is called, self is retained inside that block, and released when that block goes out of scope. 这意味着每当调用AFNetworking网络操作的完成块时,self将保留在该块内,并在该块超出范围时释放。 I believe that this applies to both ARC and non-ARC. 我相信这适用于ARC和非ARC。 I have ran both the Leaks tool and the Static Analyzer so that I may find any memory leaks. 我已经运行了泄漏工具和静态分析器,以便我可以发现任何内存泄漏。 None showed any. 没有显示任何。

However, it wasn't until recently that I stumbled upon a warning which I couldn't figure out. 然而,直到最近,我偶然发现了一个我无法弄清楚的警告。 I am using ARC in this particular example. 我在这个特定的例子中使用ARC。

I have two instance variables that indicate the completion and failure of a network operation 我有两个实例变量,表明网络操作的完成和失败

@property (nonatomic, readwrite, copy) SFCompletionBlock completionBlock;
@property (nonatomic, readwrite, copy) SFFailureBlock failureBlock;
@synthesize failureBlock = _failureBlock;
@synthesize operation = _operation;

Somewhere in the code, I do this: 在代码的某处,我这样做:

[self.operation setCompletionBlockWithSuccess:^(AFHTTPRequestOperation *operation, id
                                                    responseObject) {
NSError *error = [NSError errorWithDomain:@"com.test" code:100 userInfo:@{@"description": @"zero results"}];
            _failureBlock(error);
        } failure:^(AFHTTPRequestOperation *operation, NSError *error) {
            NSLog(@"nothing");
        }];

Xcode complains about the line that calls the failureBlock, with the message "Capturing "self" strongly in this block is likely to result in a retain cycle. I believe Xcode is right: the failure block retains self, and self holds its own copy of the block, so none of the two will be deallocated. Xcode抱怨调用failureBlock的行,在此块中强烈“捕获”自我“消息”很可能导致保留周期。我相信Xcode是正确的:失败块保留自己,并且自己拥有自己的副本块,所以两者都不会被解除分配。

However, I have the following questions/observations. 但是,我有以下问题/意见。

1) If i change _failureBlock(error) to "self.failureBlock(error)" (without quotes) the compiler stops complaining. 1)如果我将_failureBlock(错误)更改为“self.failureBlock(error)”(不带引号),编译器将停止抱怨。 Why is that? 这是为什么? Is this a memory leak the compiler misses? 这是编译器错过的内存泄漏吗?

2) In general, what is the best practice to work with blocks in both ARC and non-ARC enabled environments when using blocks that are instance variables ? 2)通常,在使用作为实例变量的块时,在ARC和非ARC启用的环境中使用的最佳实践什么? Seems that in the case of completion and failure blocks in AFNetworking, those two blocks are not instance variables so they probably don't fall into the category of retain cycles that I described above. 似乎在AFNetworking中的完成和故障块的情况下,这两个块不是实例变量,因此它们可能不属于我上面描述的保留周期类别。 But when using progress blocks into AFNetworking, what can be done to avoid retain cycles like the one above? 但是当将进度块用于AFNetworking时,可以采取哪些措施来避免像上面那样的保留周期?

I would love to hear other people's thoughts on ARC and non-ARC with blocks and issues/solutions with memory management. 我很想听听其他人对ARC和非ARC的看法,包括带有内存管理的块和问题/解决方案。 I find these situations error-prone and I feel some discussion on this is necessary to clear things up. 我发现这些情况容易出错,我觉得有必要对此进行一些讨论以澄清问题。

I don't know if it matters, but I use Xcode 4.4 with the latest LLVM. 我不知道它是否重要,但我使用Xcode 4.4和最新的LLVM。

1) If i change _failureBlock(error) to "self.failureBlock(error)" (without quotes) the compiler stops complaining. 1)如果我将_failureBlock(错误)更改为“self.failureBlock(error)”(不带引号),编译器将停止抱怨。 Why is that? 这是为什么? Is this a memory leak the compiler misses? 这是编译器错过的内存泄漏吗?

The retain cycle exists in both cases. 两种情况都存在保留周期。 If you're targeting iOS 5+, you can pass in a weak reference to self: 如果您的目标是iOS 5+,则可以向自己传递一个弱引用:

__weak MyClass *weakSelf;
[self.operation setCompletionBlockWithSuccess:^(AFHTTPRequestOperation *operation, id responseObject) {
    NSError *error = [NSError errorWithDomain:@"com.test" code:100 userInfo:@{@"description": @"zero results"}];
    if (weakSelf.failureBlock) weakSelf.failureBlock(error);
} failure:^(AFHTTPRequestOperation *operation, NSError *error) {
    NSLog(@"nothing");
}];

Now self won't be retained, and if it's deallocated before the callback is invoked, the callback is a no-op. 现在self不会被保留,如果在调用回调之前它被释放,则回调是no-op。 However, it's possible that it could be undergoing deallocation while the callback is invoked on a background thread, so this pattern can create occasional crashes. 但是,当在后台线程上调用回调时,它可能正在进行释放,因此这种模式可能会偶尔发生崩溃。

2) In general, what is the best practice to work with blocks in both ARC and non-ARC enabled environments when using blocks that are instance variables? 2)通常,在使用作为实例变量的块时,在ARC和非ARC启用的环境中使用块的最佳实践是什么? Seems that in the case of completion and failure blocks in AFNetworking, those two blocks are not instance variables so they probably don't fall into the category of retain cycles that I described above. 似乎在AFNetworking中的完成和故障块的情况下,这两个块不是实例变量,因此它们可能不属于我上面描述的保留周期类别。 But when using progress blocks into AFNetworking, what can be done to avoid retain cycles like the one above? 但是当将进度块用于AFNetworking时,可以采取哪些措施来避免像上面那样的保留周期?

Most of the time, I think it's better not to store blocks in instance variables . 大多数时候,我认为最好不要在实例变量中存储块 If you instead return the block from a method in your class, you'll still have a retain cycle, but it only exists from the time the method is invoked to the time the block is released. 如果你从类中的方法返回块,你仍然会有一个保留周期,但它只存在从调用方法到释放块的时间。 So it will prevent your instance from being deallocated during the block execution, but the retain cycle ends when the block is released: 因此,它会阻止您的实例在块执行期间被释放,但保留周期在块释放时结束:

-(SFCompletionBlock)completionBlock {
    return ^(AFHTTPRequestOperation *operation , id responseObject ) {
        [self doSomethingWithOperation:operation];
    };
}

[self.operation setCompletionBlockWithSuccess:[self completionBlock]
                                      failure:[self failureBlock]
];

That means that whenever a completion block of an AFNetworking network operation is called, self is retained inside that block, and released when that block goes out of scope. 这意味着每当调用AFNetworking网络操作的完成块时,self将保留在该块内,并在该块超出范围时释放。

No, self is retained by the block when the block is created. 不,创建块时,块会保留self And it is released when the block is deallocated. 并且在释放块时释放它。

I believe Xcode is right: the failure block retains self, and self holds its own copy of the block, so none of the two will be deallocated. 我相信Xcode是正确的:失败块保留自己,并且self拥有自己的块副本,所以两者都不会被释放。

The block in question that retains self is the completion block passed to setCompletionBlockWithSuccess . 保留self问题块是传递给setCompletionBlockWithSuccess的完成块。 self does not hold a reference to this block. self没有对此块的引用。 Rather, self.operation (presumably some kind of NSOperation ) retains the blocks while it is executing. 相反, self.operation (可能是某种NSOperation )在执行时保留了块。 So there is temporarily a cycle. 所以暂时有一个循环。 However, when the operation is done executing, the cycle will be broken. 但是,当操作完成执行时,循环将被中断。

1) If i change _failureBlock(error) to "self.failureBlock(error)" (without quotes) the compiler stops complaining. 1)如果我将_failureBlock(错误)更改为“self.failureBlock(error)”(不带引号),编译器将停止抱怨。 Why is that? 这是为什么? Is this a memory leak the compiler misses? 这是编译器错过的内存泄漏吗?

There should be no difference. 应该没有区别。 self is captured in both cases. 在这两种情况下都会捕获self The compiler is not guaranteed to catch all cases of retain cycles. 不保证编译器能够捕获所有保留周期的情况。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM