weak-strong dance 的注意事项

Aug 13, 2022 • 预计阅读时间 2 分钟

在使用逃逸 Block 的时候,为了防止 self 被循环引用,一般使用以下方式:

- (void)foo {
    __weak typeof(self) wself = self;
    [self auth:^(NSString *token) {
        typeof(self) self = wself;
        if (!self) { return; }
    }];
}

在 Block 外部定义一个 __weak 类型的 self 引用,在 Block 内部使用同名的局部 self 变量,以上的技巧就叫做 weak-strong dance

在 Block 里对实例变量都要通过局部定义的变量 self 来访问,否则就会发生循环引用的情况:

- (void)foo {
    __weak typeof(self) wself = self;
    [self auth:^(NSString *token) {
        typeof(self) self = wself;
        if (!self) { return; }

        _token = token; // 这里会使用隐式 self->_token
    }];
}

在 Block 里直接使用实例变量 _token 会在编译期间把隐式 self 转换为使用真正的 self 而不是局部变量 self,就会导致循环引用。

所以在使用 weak-strong dance 技巧的时候,Block 块内的实例变量都要使用 self-> 来访问。

Swift 中的 weak-strong dance

作为 Objective-C 的继任者,Swift 在语言特性上就支持 weak-strong dance ,而且用起来简单多了:

func foo() {
    auth { [weak self] (token) in
        guard let self = self else {  return }

        self.token = token
    }
}

什么情况下不需要 weak-strong dance ?

只有逃逸闭包才需要 weak-strong dance,非逃逸闭包没有必要使用 weak-strong dance,因为非逃逸闭包是即时执行而且超出作用范围后就被释放了。

定义非逃逸闭包:

Objective-C 使用 NS_NOESCAPE 标识闭包类型,当然实际是否逃逸还是需要开发者来保证,声明只是让使用者知道它是非逃逸的:

void foo:(void (^NS_NOESCAPE)(void))block {
}

Swift 默认非可选类型的闭包都是非逃逸闭包,如果需要把一个非可选类型闭包声明为逃逸闭包,需要增加 @escaping 声明:

func foo(block: @escaping () -> Void) {
}
iOS
版权声明:如果转发请带上本文链接和注明来源。

lvv.me

iOS/macOS Developer

双队列(Two Queue)优化 LRU 缓存算法

ObjC 中的黑魔法 Swizzling