在dispatch_async函数中使用弱自我

Mar*_*vec 52 weak-references ios objective-c-blocks retain-cycle

我读了很多关于__weak self在里面使用的帖子dispatch_async,现在我有点困惑.

如果我有 :

self.myQueue = dispatch_queue_create("com.biview.core_data", NULL);

dispatch_async(self.myQueue, ^(void){
    if (!self.var1) {
        self.var1 = ...;
    }
    dispatch_async(dispatch_get_main_queue(), ^(void) {
        if ([self.var2 superview]) {
            [self.var2 removeFromSuperview];
        }

        [self.Label setText:text];
    });
});
Run Code Online (Sandbox Code Playgroud)

我需要使用吗__weak self?因为我读过在某些情况下dispatch_async 不需要__weak self.

请参阅此处的最新评论

Cou*_*per 137

假设,self是指向a的对象指针UIViewController.

需要考虑的事项:

  • A UIViewController是"UIKit"对象.UIKit对象不应该在非主线程上发送方法,即 - 那些方法只能在主线程上执行!

  • 已排队的块 - 无论是同步还是异步 - 最终将被执行 - 无论如何!好吧,除非程序在此之前终止.

  • 当复制块时(例如,异步调度时)将保留捕获的可保留指针,并在块被销毁时(在完成后)再次释放.

  • 捕获的可保留指针不会被保留而不会被释放.

在您的方案中,您在主队列中调度的块中捕获self,您不必担心会发生不好的事情.

所以为什么?实际上会发生什么?

因为捕获其中被分派块异步,自我会被隐保留,并发布当块已经完成一次.

这意味着,自我的生命期将延长至阻止完成之后.请注意,您的第二个块是在主线程上调度的,并且当该块执行时,它保证self仍处于活动状态.

上面的"延长寿命"可能是您程序的理想功能.

如果你明确地不想延长UIViewController对象的生命周期,而是想要块 - 当它最终执行时 - 检查这个UIViewController对象是否仍然存在,你可以使用self的__weak指针.请注意,无论块是否UIViewController仍处于活动状态或已在同一时间释放,该块最终都会被执行.

如果在块执行之前UIViewController已经解除分配,您可能希望块执行"无任何操作" :

MyController* __weak weakSelf = self;
dispatch_async(queue, ^{
    MyController* strongSelf = weakSelf;
    if (strongSelf) {
        ...
    }
    else {
       // self has been deallocated in the meantime.
    }
});
Run Code Online (Sandbox Code Playgroud)

另请参阅:转换到ARC发行说明

记住:UIKit对象不应该在非主线程上发送方法!

UIKit对象只能在主线程上执行方法时,可能会发生另一个细微错误.

如果块捕获UIKit异步调度的对象并在非主线程上执行,则可能违反此规则.然后可能发生该块保存对该对象的最后一个强引用UIKit.现在,当块最终被执行时,块将被销毁并且UIKit对象将被释放.由于这是对象的最后一个引用UIKit,因此将取消分配.但是,这发生在已执行块的线程上 - 这不是主线程!现在,坏事可以(并且通常会)发生,因为该dealloc方法仍然是发送给UIKit对象的方法.

您可以通过调度捕获指向该UIKit对象的强指针的块来避免此错误,并向其发送一个虚拟方法:

UIViewController* strongUIKitPointer = ... 
dispatch_async(non_main_queue, ^{
    ... // do something 
    dispatch(dispatch_get_main_queue(), ^{
        [strongUIKitPointer self];  // note: self is a method, too - doing nothing
    });
});
Run Code Online (Sandbox Code Playgroud)

但是,在您的方案中,最后一个强引用可能只在主线程上执行的块中.因此,您可以避免这种微妙的错误.;)

编辑:

在您的设置中,您永远不会有保留周期.如果可保留对象A强引用另一个可保留对象B,并且对象B强引用A,则发生保留周期.请注意,"块"也是可保留对象.

一个带有循环引用的人为例子:

typedef void(^my_completion_block_t)(NSArray* result);

@interface UsersViewController : UIViewController
@property (nonatomic, copy) my_completion_block_t completion;
@property (nonatomic) NSArray* users;
@end
Run Code Online (Sandbox Code Playgroud)

在这里,我们有一个属性完成,其值类型是块.也就是说,我们得到一个名称_completion类型为Block 的ivar .

客户端可以设置完成处理程序,当某个操作完成时应该调用该处理程序.假设,该操作从远程服务器获取用户列表.计划是在操作完成后设置属性用户:

不小心的方法会不小心引入循环引用:

在"UsersViewController.m"中的某处

self.completion = ^(NSArray* users){
    self.users = users;
}

[self fetchUsers];  // start asynchronous task
Run Code Online (Sandbox Code Playgroud)

在这里,自我强烈地提到了ivar _completion,这是一个块.并且块本身捕获self,这导致在块被调度时复制块时保留self.这是一个经典的参考周期.

为了避免这种循环引用,我们有几个选择:

  1. 使用自定义__weak限定指针

    UsersViewController* __weak weakSelf = self;
    self.completion = ^(NSArray* users) {
        UsersViewController* strongSelf = weakSelf;
        if (strongSelf) {
            strongSelf.users = users;
        }
        else {
            // the view controller does not exist anymore
        }
    }   
    [usersViewController fetchUsers];
    
    Run Code Online (Sandbox Code Playgroud)
  2. 使用自定义__block限定指针,并在完成时最终将其设置在块中:nil

    UsersViewController* __block blockSelf = self;
    self.completion = ^(NSArray* users) {
        blockSelf.users = users;
        blockSelf = nil;
    }   
    [usersViewController fetchUsers];
    
    Run Code Online (Sandbox Code Playgroud)

另请参阅:转换到ARC发行说明


War*_*shi 28

Swift更新:

在swift中这种所谓的强弱舞蹈的一个例子:

Swift 4.2:

func doSomeThingAsynchronously() {
    DispatchQueue.global().async {
        // Do task in default queue
        DispatchQueue.main.async { [weak self] in
            // Do task in main queue
            guard let self = self else { return }
            self.updateView()
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

Swift 3&4:

func doSomeThingAsynchronously() {
    DispatchQueue.global().async {
        // Do task in default queue
        DispatchQueue.main.async { [weak self] in
            // Do task in main queue
            guard let strongSelf = self else { return }
            strongSelf.updateView()
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

斯威夫特2:

func doSomeThingAsynchronously() {
    dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0)) { () -> () in
        // Do task in default queue
        dispatch_async(dispatch_get_main_queue(), { [weak self] () -> () in
            guard let strongSelf = self else { return }
            // Do task in main queue
            strongSelf.updateView()
        })
    }
}
Run Code Online (Sandbox Code Playgroud)

流行的开源项目Alamofire使用这种方法.

使用扩展对象的生命周期[薄弱的自我]守护让 strongSelf =自其他{}回报成语.

有关更多信息,请查看swift-style-guide

  • 有趣的事实:你可以迅速避免`strongSelf`syntax与```guard let`self` = self else {return}``` (10认同)
  • 使用``self``实际上是编译器错误,可能会在将来的Swift版本中删除.查看完整的解释,从斯威夫特的克里斯·拉特纳,在这里:https://lists.swift.org/pipermail/swift-evolution/Week-of-Mon-20160118/007425.html (5认同)