iOS正确使用@weakify(self)和@strongify(self)

我开始将libextobjc( https://github.com/jspahrsummers/libextobjc )集成到我的iOS应用程序中,主要是为了利用EXTScope的@strongify@weakify ,但在深入研究这个过程之前,有几个问题。

下面是一个故意过于复杂的例子,试图解决如何处理这个问题:

 - (void)someMethod { if (self.someBOOL) { _someObjectInstanceVar = [Object objectWithCompletionHandler:^{ // self reference #1 if (self.someProperty) { // self reference #2 [[Async HTTPRequest] sendAWithID:self.property.id completionHandler:^(void (^)(NewObject *newObject) { // self reference #3 [self showViewWithObject:newObject handler:^{ // self reference #4 [self reloadData]; }]; }]; } }]; else { [[Async HTTPRequest] sendBWithID:self.property.id completionHandler:^{ // self reference #5 [self reloadData]; }]; } } 

我的理解是,如果我想做asynchronousHTTP请求,并在完成处理程序引用自我,像[self reloadData] ,我不需要做强/弱的任何东西,因为请求块本身不是保留完成块,所以在那里保留循环没有问题。 在上面的代码示例中,我认为#5是一个保留周期不是问题的情况。

主要关心的是所有将块作为属性/初始参数的对象,这些对象在内部保持在块属性上。 在objectWithCompletionHandler方法中, someObject作为一个实例variables保存在completionHandler块中,在那里有多个对自身的引用,我知道会引起泄漏。 我的主要问题就是在这种情况下,你如何处理weakifystrongify ,使之更“安全”? 一个@weakify和@strongify调用都是足够的,如下所示:

 - (void)someMethod { @weakify (self); _someObjectInstanceVar = [Object objectWithCompletionHandler:^{ @strongify(self); ... } 

上面的@strongify(self)引用是否足够用于自引用#1,2,3和4,还是我必须(甚至会工作)在sendAWithID使用一个新的弱/强引用方法和嵌套的reloadData

编辑:固定代码有问题更有意义,并修复一些语法错误。

@strongify如何工作

@strongify之后, self将在块内部具有与在块外部不同的指针地址。 这是因为@strongify声明一个新的名为self局部variables。 (这就是为什么它会抑制-Wshadow警告,它会“在局部variables影响另一个局部variables时发出警告”)值得阅读和理解这些函数的实现 。 所以即使名称是相同的,把它们当作单独的strong引用。

在你的代码中使用@strongify

假设(这不是真的) 每个块的使用都会创build一个参考周期 ,您可以:

 - (void)someMethod { if (self.someBOOL) { @weakify(self); _someObjectInstanceVar = [Object objectWithCompletionHandler:^{ @strongify(self); // self reference #1 if (self.someProperty) { @weakify(self); // self reference #2 [[Async HTTPRequest] sendAWithID:self.property.id completionHandler:^(void (^)(NewObject *newObject) { @strongify(self); // self reference #3 @weakify(self); [self showViewWithObject:newObject handler:^{ // self reference #4 @strongify(self); [self reloadData]; }]; }]; } }]; // etc… } 

但是,请记住, 首次使用@strongifyself将引用本地堆栈variables 。 当它们被定义的范围结束时(只要你没有将它们存储到属性中或在嵌套块中使用它们),这些通常会被销毁。 所以根据你所显示的代码,你只需要// self reference #1

也可以看看

阅读包含@weakify@strongify的unit testing将有助于澄清这些函数的正确用法。

要回答你的问题,是否在你的块的每个嵌套级别的weakify / strongify的多个实例工作,那么是的。 但是没有必要这样做,因为你的第一个@strongify定义已经为块的所有内部范围(以及嵌套在其中的块)定义了self。

但是,考虑到你的块有不同的生命周期,你可能要为每个嵌套块添加@strongify,以确保它们都保留自己的保留周期到它们的内部范围。

下面是解释这种情况的github问题线程: https : //github.com/jspahrsummers/libextobjc/issues/45

在“自我”保持的块内调用“自我”会导致“保留周期”,从而导致内存泄漏。 所以理想情况是这样的:

 @interface A: NSObject // Some interface A @property (nonatomic, copy, readwrite) dispatch_block_t someBlock; // See block is strongly retained here. @end ******************************************************* @implementation A - (void) someAPI { __weak A * weakSelf = self; // Assign self to weakSelf and use it // enter code here inside block to break retain cycles. self.someBlock = ^{ A * strongSelf = weakSelf; // Assign weak self to strongSelf before // using it. This is because weakSelf can go nil anytime and it may happen // that only few lines from block get executed before weakSelf goes nil, // and hence code may be in some bad state. if (strongSelf != nil) { // Use strongSelf. [strongSelf doSomethingAwesome]; [strongSelf doSomethingAwesomeAgain]; } }; } @end 

如果块没有被“自我”保留,那么它的安全就是在块内部使用“自我”,他们不会创build保留周期。

注:内存pipe理概念与“libextobjc”库的使用保持一致。