`typeof(self)self = weakSelf`构建合法的内部块?

iHu*_*ter 4 objective-c objective-c-blocks automatic-ref-counting

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

__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;
}
Run Code Online (Sandbox Code Playgroud)

此代码在Xcode 4.5.2中完美运行,仅发出警告Declaration shadows a local variable.

这个怪癖有什么意义:

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

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

Ste*_*her 5

这个答案分为两个部分:

  1. 您的声明的根本原因会影响本地变量警告GCC_WARN_SHADOW,以及为什么打开它可能是一个糟糕的警告.
  2. 建立替代品NSAssert并不需要这种技巧

GCC_WARN_SHADOW

你得到的警告来自GCC_WARN_SHADOW,一个更无用的警告(在我看来,无论如何).编译器在这里做了正确的事情,但是由于GCC_WARN_SHADOW它引起了你的注意,你可能做了一些非你想要的事情.

这是偏执狂编译器警告擅长的事情.缺点是很难(不是不可能)放弃一个特定的警告,表明你知道你在做什么.

使用GCC_WARN_SHADOWon,此代码将生成警告:

int value = MAX(1,MAX(2,3));
Run Code Online (Sandbox Code Playgroud)

尽管如此,它将完美运作.

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

({
   int __a = (1);
   int __b = (({
       int __a = (2);
       int __b = (3);
       __a < __b ? __b : __a;
   }));
   __a < __b ? __b : __a;
})
Run Code Online (Sandbox Code Playgroud)

所以你提出的建议会很好.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)
Run Code Online (Sandbox Code Playgroud)

这实际上是一个复制粘贴NSAssert,但它使用strongSelf而不是self.在使用该strongSelf模式的地方,它会起作用; 在strongSelf未定义的地方,您将收到编译器错误:使用未声明的标识符:'strongSelf'.我认为这是一个非常好的暗示.