在dispatch_async函数中使用弱自我 [英] Using weak self in dispatch_async function
问题描述
我在 dispatch_async
里面读了很多关于使用 __弱自我
的帖子,现在我有点困惑。
I read a lot of posts about using __weak self
inside dispatch_async
, and now I am a litle bit confused.
如果我有:
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];
});
});
我需要使用 __弱自我
。因为我读过在某些情况下 dispatch_async
不需要 __弱自我
。
do I need to use __weak self
. Because I read that in some cases dispatch_async
not need a __weak self
.
推荐答案
假设, self 是一个对象指针到 UIViewController
。
Assuming, self is an object pointer to a UIViewController
.
需要考虑的事项:
-
A
UIViewController
是一个UIKit对象。 UIKit对象不应该在非主线程上发送方法,即 - 那些方法只能在主线程上执行!
A
UIViewController
is a "UIKit" object. UIKit objects shall not be sent methods on non-main threads, that is - those methods must execute on the main thread only!
已经入队的块在队列中 - 无论是同步还是异步 - 最终将执行 - 无论如何!好吧,除非程序在此之前终止。
A block that has been enqueued in a queue - whether this was synchronously or asynchronously - will eventually be executed -- no matter what! Well, unless the program terminates before this can happen.
捕获的强指针将保留当块被复制时(例如,异步调度时),以及释放时块将被销毁(完成后)。
Captured retainable strong pointers will be retained when the block will be copied (for example, when dispatched asynchronously), and again released when the block will be destroyed (after it finished).
捕获的可保留弱指针不会被保留而不会被释放。
Captured retainable weak pointers will NOT be retained and not released.
在您的方案中,您在主队列中调度的块中捕获 self ,您无需担心会发生不良事件。
In your scenario, where you capture self in the block which is dispatched on the main queue, you don't need to worry that bad things happen.
由于 self 将捕获在异步调度的块中 , self 将隐式保留,并在块完成后再次释放。
Since self will be captured in the block which is dispatched asynchronously, self will be implicitly retained, and released again when the block has been finished.
这意味着, self 的生命周期将扩展,直到块结束。请注意,您的第二个块将在主线程上调度,并确保 self 在执行该块时仍处于活动状态。
That means, the life-time of self will be extended up until after the block finishes. Notice that your second block is dispatched on the main thread, and it's guaranteed that self is still alive when that block gets executed.
上面的延长寿命可能是您计划的理想功能。
This "extended life" above, might be a desired feature of your program.
如果显式不想延长 UIViewController
object,而不是想要块 - 当它最终执行时 - 检查这个 UIViewController
对象是否仍然存在,你可以使用__weak自我的指针。请注意,无论 UIViewController
是否仍处于活动状态或同时已取消分配,该块最终都会被执行。
If you explicitly don't want to extend the life-time of the UIViewController
object, and instead want the block - when it finally executes - check whether this UIViewController
object does still exist at all, you can use a __weak pointer of self. Note that the block gets eventually executed, no matter whether the UIViewController
is still alive or has been deallocated in the mean time.
如果 UIViewController
在之前被解除分配,你可能希望该块做无 >该块将被执行:
You might want the block doing "nothing" if the UIViewController
has been deallocated before the block will get executed:
MyController* __weak weakSelf = self;
dispatch_async(queue, ^{
MyController* strongSelf = weakSelf;
if (strongSelf) {
...
}
else {
// self has been deallocated in the meantime.
}
});
参见:转换到ARC发行说明
可能会发生另一个微妙的错误UIKit
对象只能在主线程上执行方法。
One other subtle error may occur do to the fact that UIKit
objects shall execute methods only on the main thread.
如果一个块捕获 UIKit,这可能会被违反
异步调度的对象,并在非主线程上执行。然后可能会发生该块持有最后对该 UIKit
对象的强引用。现在,当块最终被执行时,块将被销毁,并且将释放 UIKit
对象。由于这是对 UIKit
对象的最后一个强引用,因此将对其进行解除分配。但是,这发生在已执行块的线程上 - 这不是主线程!现在,坏事可以(并且通常会)发生,因为 dealloc
方法仍然是发送到 UIKit的方法
对象。
This can be violated, if a block captures a UIKit
object which is dispatched asynchronously, and executes on a non-main thread. It then may happen that the block holds the last strong reference to that UIKit
object. Now, when the block gets eventually executed, the block will be destroyed and the UIKit
object will be released. Since this is the last strong reference to the UIKit
object, it will be deallocated. However, this happens on the thread where the block has been executed - and this is not the main thread! Now, bad things can (and will usually) happen, since the dealloc
method is still a method sent to a UIKit
object.
您可以通过调度捕获指向该UIKit对象的强指针的块来避免此错误,并向其发送虚拟方法:
You can avoid this error, by dispatching a block capturing a strong pointer to that UIKit object, and send it a dummy method:
UIViewController* strongUIKitPointer = ...
dispatch_async(non_main_queue, ^{
... // do something
dispatch(dispatch_get_main_queue(), ^{
[strongUIKitPointer self]; // note: self is a method, too - doing nothing
});
});
在您的方案中, last strong 引用可能只在在主线程上执行的块。所以,你可以避免这种微妙的错误。 ;)
In your scenario though, the last strong reference could be only in the block which executes on the main thread. So, you are safe from this subtle error. ;)
在您的设置中,您永远不会有保留周期。如果可保留对象A强引用另一个可保留对象B,并且对象B强引用A,则会发生保留周期。请注意,块也是可保留对象。
In your setup, you never have a retain cycle. A retain cycle occurs if a retainable object A strongly references another retainable object B, and object B strongly references A. Note that a "Block" is also a retainable object.
一个带有循环引用的人为例子:
A contrived example with a cyclic reference:
typedef void(^my_completion_block_t)(NSArray* result);
@interface UsersViewController : UIViewController
@property (nonatomic, copy) my_completion_block_t completion;
@property (nonatomic) NSArray* users;
@end
在这里,我们有一个属性 completion 值类型是块。也就是说,我们得到一个名称为 _completion
的ivar,其类型为Block。
Here, we have a property completion whose value type is a Block. That is, we get an ivar with name _completion
whose type is a Block.
客户可以设置完成某个操作完成后应该调用的处理程序。假设,该操作从远程服务器获取用户列表。计划是在操作完成后设置属性 users :
A client may set a completion handler which should be called when a certain operation has finished. Suppose, the operation fetches a list of Users from a remote server. The plan is to set the property users once the operation finished:
粗心的方法会不小心引入循环引用:
The careless approach would accidentally introduce a cyclic reference:
UsersViewController.m中的某处
Somewhere in "UsersViewController.m"
self.completion = ^(NSArray* users){
self.users = users;
}
[self fetchUsers]; // start asynchronous task
此处, self 拥有对ivar _completion
,这是一个块。并且块本身捕获 self ,这导致在调度块被复制时保留 self 。这是一个经典的参考周期。
Here, self holds a strong reference to the ivar _completion
, which is a block. And the block itself captures self, which causes to retain self when the block gets copied when it is dispatched. This is a classic reference cycle.
为了避免这种循环引用,我们有几个选择:
In order to avoid that cyclic reference, we have a few alternatives:
-
使用
__弱
self 的合格指针
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];
使用 __ block
限定指针 self 并最终在块中完成设置 nil
:
Using a __block
qualified pointer of self and eventually setting it nil
in the block when it finishes:
UsersViewController* __block blockSelf = self;
self.completion = ^(NSArray* users) {
blockSelf.users = users;
blockSelf = nil;
}
[usersViewController fetchUsers];
参见:过渡到ARC发行说明
这篇关于在dispatch_async函数中使用弱自我的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!