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

时间:2013-01-07 11:04:12

标签: objective-c automatic-ref-counting objective-c-blocks

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

__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. 块中的
  3. NSAssert不再导致保留周期。
  4. 更新 我发现libextobjc用于@weakify/@strongify宏的这种技术。

2 个答案:

答案 0 :(得分:5)

这个答案分为两部分:

  1. 声明影响本地变量警告{* 1}}的根本原因,以及为什么打开它可能是一个糟糕的警告。
  2. 构建不需要此诡计的GCC_WARN_SHADOW的替代
  3. GCC_WARN_SHADOW

    你得到的警告来自NSAssert,这是一个更无用的警告(无论如何,在我看来)。编译器在这里做了正确的事情,但是感谢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

    更换NSAssert

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

    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。在你use the strongSelf pattern的地方,它会起作用;在未定义self的地方,您将收到编译器错误:使用未声明的标识符:'strongSelf'。我认为这是一个非常好的提示。

答案 1 :(得分:3)

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

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

出于这个原因,我会毫不犹豫地在我要分享的库中使用此代码。代码可能会破坏当时编译器的新版本,因为实际上你会比运行时更明显地攻击运行时。另外,它是古怪的代码,正如你所指出的那样:)我不确定阅读它的其他人会立即明白发生了什么。