【问题标题】:Circular references in Objective-C and clangObjective-C 和 clang 中的循环引用
【发布时间】:2015-10-21 20:32:36
【问题描述】:

为什么静态分析器不检测带有块的循环引用?我记得当我保留我的代表而不是分配、预块介绍时,它曾经这样做过。我记得它用来在我的代码上绘制的漂亮的小线条(我认为......)

如果我这样做,不使用weakSelf,我知道我会得到一个循环引用。

// Note 1: myObject is 'retained' by self. 
// Note 2: myObject retains the block for the future

[self.myObject registerBlockOfCodeForFutureExectution:^{
    [self doSomething];
}];

Sample Project Exploiting Issue

现在,如果我知道这一点,而且我是一个愚蠢的人,那为什么我的智能计算机不知道这是坏事并警告我我很愚蠢?

它无法检测到它一定有一个合乎逻辑的原因,我想知道那个原因是什么。

这个问题是关于 clang 和静态分析的,请不要建议我如何修复循环引用 - 我知道该怎么做。

【问题讨论】:

  • 可能不会创建retain循环,self会保留block吗?看起来不像。
  • “我知道我会得到一个循环引用” - 错误的假设。
  • 在这种情况下块是内联的,只有作用域持有块,没有保留循环。一个简单的测试是将 NSLog() 放在类的 dealloc 方法中,并查看当类实例失去它的保留时它会被调用。
  • @zaph 谢谢,但这是理论问题和示例代码,我告诉你它确实创建了一个保留周期。我会让问题更清楚。
  • @zaph 谢谢。我知道如何解决循环引用。问题是关于检测它们的铿锵声 - 而不是我检测它们。如果你看一下我给方法“saveThisBlockInMyObject”的名称,我希望暗示该块被接收者保留。

标签: objective-c circular-reference


【解决方案1】:

如果您在块内使用self,这并不意味着您会自动获得保留周期。仅当块的生命周期取决于 self 对象的生命周期时,您才会获得保留周期。如果self 具有对myObject 的强引用或者一些更复杂的依赖关系也是可能的(我假设它确实“保存”块传递给一个方法,所以你已经在那里有强引用)。

因此,要在您的示例中保留循环,您需要满足以下两个条件(它们都不是来自您发布的代码),并且编译器需要了解它们: 1. myObject 的生命周期与 self 相关 - 让我们假设 self 对它有很强的引用 2. saveThisBlockInMyObject: 保留传递给它的块

我制作了一个小示例,向编译器发出有关捕获自我的警告 - 为了解决第一点,我将 myObject 声明为某个类的强属性:

@property (strong) MyTestClass* myObj;
...
self.myObj = [MyTestClass new];

对于第二点,我找不到指定该方法保留其参数的方法(返回值有 source annotations,但方法参数没有相关注释)。但是您将块声明为测试类的强属性,然后编译器很乐意警告您可能的保留周期:

typedef void (^MyVoidBlock)();
// MyTestClass
@property (nonatomic, copy) MyVoidBlock voidBlock;

self.voidBlock = ^{
    [self doSomething]; // Warning!
};

希望这是有道理的:)

【讨论】:

  • 但在 OP 的情况下,块不是属性,它是内联的,只有范围持有块。一个简单的测试是将NSLog() 放在类的dealloc 方法中,并查看当类实例失去它的保留时它会被调用。
  • 这就是我所说的 - 我们需要告诉编译器块被保留在方法中,但我能找到指定它的唯一方法是属性(参见源注释链接)跨度>
  • 是的,并且正在解释很多,添加一个带有由属性保留的块的案例,问题中不是这种情况。我的解释是,如果块是内联的,则不能有指向块的指针,因此在内联块中使用 self 不会导致保留循环。
  • @zaph,好点,看来我回答错了问题:) 将删除
  • 不要删除答案,稍微清理一下。
【解决方案2】:

发布到 github 的代码确实会导致保留周期。

当前github代码:

@interface MyObject ()
@property (nonatomic, copy) dispatch_block_t codeToRunInFuture;
@end

@implementation MyObject

- (void) registerBlockForFuture:(dispatch_block_t)block {
    self.codeToRunInFuture = block;
}

// Call in ViewController
self.myObject = [MyObject.alloc init];
[self.myObject registerBlockForFuture:^{
    [self runThisInFuture];
}];

我可以看到这在哪里难以捕捉,因为分析器无法知道block 可能是什么,因此无法判断是否存在强或弱自引用。它必须检查调用registerBlockForFuture: 的所有实例以及每种情况下的block

答案可能是向 Apple 提交 bugreport

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2012-08-20
    • 1970-01-01
    • 2011-05-09
    • 1970-01-01
    • 2011-08-28
    • 2015-04-24
    • 2011-03-20
    相关资源
    最近更新 更多