我使用块已经有一段时间了,但我觉得在 ARC 和非 ARC 环境中,我都怀念一些关于内存管理的事情。我觉得更深层次的理解会让我避免很多内存泄漏。

AFNetworking 是我在特定应用程序中对 Blocks 的主要使用。大多数时候,在操作的完成处理程序中,我会执行类似“[self.myArray addObject]”的操作。

在 ARC 和非 ARC 启用环境中,“self”将根据 这篇文章来自苹果.

这意味着每当调用 AFNetworking 网络操作的完成块时, self 都会保留在该块内,并在该块超出范围时释放。我相信这对于 ARC 和非 ARC 都适用。我已经运行了泄漏工具和静态分析器,以便我可以发现任何内存泄漏。没有任何显示。

然而,直到最近我才偶然发现了一个我无法理解的警告。我在这个特定示例中使用 ARC。

我有两个实例变量,分别指示网络操作的完成和失败

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

在代码中的某个地方,我这样做:

[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 抱怨调用 failureBlock 的行,并显示消息“在此块中强烈捕获“自我”可能会导致保留周期”。我相信 Xcode 是对的:失败的块保留 self,而 self 持有它自己的块副本,因此两者都不会被释放。

但是,我有以下问题/观察结果。

1)如果我将 _failureBlock(error) 更改为“self.failureBlock(error)”(不带引号),编译器将停止抱怨。这是为什么?这是编译器漏掉的内存泄漏吗?

2) 一般来说,在使用 ARC 和非 ARC 环境中使用块的最佳实践是什么? 作为实例变量的块?似乎在 AFNetworking 中的完成和失败块的情况下,这两个块是 不是 实例变量,因此它们可能不属于我上面描述的保留周期的类别。但是,当在 AFNetworking 中使用进度块时,可以采取什么措施来避免像上面那样的保留循环呢?

我很想听听其他人对 ARC 和非 ARC 块的想法以及内存管理的问题/解决方案。我发现这些情况很容易出错,我觉得有必要对此进行一些讨论以澄清问题。

我不知道这是否重要,但我使用带有最新 LLVM 的 Xcode 4.4。

有帮助吗?

解决方案

1)如果我将_failureblock(错误)更改为“self.failureblock(错误)” (没有引号)编译器停止抱怨。这是为什么?这是 记忆泄漏编译器未命中?

在这两种情况下都存在保留循环。如果您定位了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");
}];
.

现在自我不会被保留,如果在调用回调之前被解除分配,则回调是一个no-op。但是,它可能会在后台线程上调用回调时,它可能正在进行分配,因此此模式可以临时崩溃。

2)一般来说,在两者中使用块的最佳做法是什么 使用块时的弧和非弧启用的环境 实例变量?似乎在完成和失败的情况下 在afnetworking中的块,这两个块不是实例变量所以 他们可能不会陷入我的保留周期类别 如上所述。但是在将进度块使用进入AFnetworking时, 可以做些什么来避免像上面一样保持周期?

大多数时候,我认为它更好在实例变量中存储块。如果您而从课堂中的方法返回块,则您仍然有一个保留周期,但它只存在于将方法调用到释放块时的时间。因此它将阻止您的实例在块执行期间被解除分配,但保留周期在释放块时结束:

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

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

其他提示

这意味着,每当称为afnetworking网络操作的完整块时,就会在该块内部保留自身,并在该块脱离范围时释放。

不, self 在创建块时由块保留。当块被释放时它被释放。

我相信 Xcode 是对的:故障块保留了自我,并且自我保留了自己的块副本,因此两者都不会被划分。

有问题的块保留 self 是传递给的完成块 setCompletionBlockWithSuccess. self 不持有对此块的引用。相当, self.operation (大概是某种 NSOperation) 在执行时保留块。所以暂时有一个循环。然而,当操作执行完毕后,循环就会被打破。

1)如果我将_failureBlock(错误)更改为“ self.failureblock(error)”(无引号)编译器停止抱怨。这是为什么?这是编译器错过的记忆泄漏吗?

应该没有区别。 self 在这两种情况下都会被捕获。编译器不能保证捕获所有保留周期的情况。

许可以下: CC-BY-SA归因
不隶属于 StackOverflow
scroll top