4

我想知道问题主题的变量声明是否合法。想象一下下面的代码:

__weak typeof(self) weakSelf = self;
[self doSomethingThatMayCauseRetainCycleWithBlock:^{
    typeof(self) self = weakSelf; // <---- !!!!
    if (self == nil) return;

    NSAssert(self.someProperty != nil, @"This doesn't lead to retain cycle!");

    [self doSomething];
    self.someProperty = someValue;

    // even
    self->someIvar = anotherValue;
}

这段代码在 Xcode 4.5.2 中完美运行,只给出一个警告,即Declaration shadows a local variable.

这个怪癖有什么意义:

  1. 重新声明self为对弱变量的强引用后,您可以安全地在块内部/外部复制/移动代码,而不会有偶尔创建保留循环的风险(除了 ivars,但它们是邪恶的)。
  2. NSAssert在一个块中不再导致保留周期。

更新 我发现这种技术libextobjc用于@weakify/@strongify宏。

4

2 回答 2

5

这个答案有两个部分:

  1. 您的声明的根本原因掩盖了局部变量警告,GCC_WARN_SHADOW以及为什么打开它可能是一个不好的警告。
  2. 建立一个替代品NSAssert不需要这个诡计

GCC_WARN_SHADOW

您收到的警告来自GCC_WARN_SHADOW,这是更无用的警告之一(无论如何,在我看来)。编译器在这里做的是正确的事情,但是由于GCC_WARN_SHADOW它引起了您的注意,您可能会做一些超出您预期的事情。

这是偏执的编译器警告擅长的事情。缺点是很难(并非不可能)删除特定警告以表明您知道自己在做什么。

启用后,此代码将GCC_WARN_SHADOW生成警告:

int value = MAX(1,MAX(2,3));

尽管如此,它将完美运行。

这样做的原因是它编译成这样,这很丑陋但(对于编译器)非常清楚:

({
   int __a = (1);
   int __b = (({
       int __a = (2);
       int __b = (3);
       __a < __b ? __b : __a;
   }));
   __a < __b ? __b : __a;
})

所以你的提议会很好用。NSAssert使用使用self变量的宏来实现。如果你self在你的范围内定义,它会self取而代之。

替换 NSAssert

但是就是说,如果您认为GCC_WARN_SHADOW有用,还有另一种解决方案。无论如何它可能会更好:提供您自己的宏。

#define BlockAssert(condition, desc, ...) \
    do { \
        __PRAGMA_PUSH_NO_EXTRA_ARG_WARNINGS \
        if (!(condition)) { \
            [[NSAssertionHandler currentHandler] handleFailureInMethod:_cmd \
            object:strongSelf file:[NSString stringWithUTF8String:__FILE__] \
                lineNumber:__LINE__ description:(desc), ##__VA_ARGS__]; \
        } \
        __PRAGMA_POP_NO_EXTRA_ARG_WARNINGS \
    } while(0)

这本质上是 的复制粘贴NSAssert,但它使用strongSelf而不是self. 在您使用该strongSelf模式的地方,它会起作用;strongSelf在未定义的地方,你会得到一个编译器错误: Use of undeclared identifier: 'strongSelf'。我认为这是一个很好的暗示。

于 2013-02-21T22:10:30.240 回答
3

严格来说,您的代码没有任何问题——变量声明是合法的。但是,您可能会收到关于局部变量遮蔽实例的编译器警告。

GCD 块实际上是 C 函数,而不是 Objective-C 方法。编译时,每个 Objective-C 实例方法都添加了一个额外的参数,即self指针。self不像其他变量那样存储在对象结构中。

出于这个原因,我会犹豫在我将要共享的库中使用此代码。代码可能会与较新版本的 then 编译器中断,因为您实际上对运行时的攻击比立即显而易见的要多一些。此外,正如您所指出的那样,它是古怪的代码:) 我不确定其他阅读它的人会立即理解发生了什么。

于 2013-01-07T13:09:04.730 回答