[英]Block in block, with __weak self
我想知道我是否做得對:
如果我有一個區塊,我會這樣做:
__weak MyClass *weakSelf = self;
[self performBlock:^{ //<< Should I use self, or weakSelf here?
[weakSelf doSomething];
} afterDelay:delay];
但是如果塊中存在塊,會發生什么? 這是正確的嗎?
__weak MyClass *weakSelf = self;
[self performBlock:^{
[weakSelf doSomething];
[self performBlock:^{
[weakSelf doSomething];
} afterDelay:1.0f];
} afterDelay:delay];
另外,在下面的功能中,我是否需要使用[塊復制]?
- (void)performBlock:(void (^)(void))block afterDelay:(float)delay
{
if (block)
{
if (delay > 0)
{
[self performSelector:@selector(executeBlockAfterDelay:) withObject:[block copy] afterDelay:delay];
}
else
{
[self executeBlockAfterDelay:[block copy]];
}
}
}
- (void)executeBlockAfterDelay:(void(^)(void))block
{
if (block)
block();
}
而不是實現-performBlock:afterDelay:
只需使用dipatch_after()
。 除此之外,這不是傳遞給對象的消息,因此毫無疑問接收器的目標是什么。
實際上,這里根本沒有內存管理問題。 當一個對象保留一個塊並且該塊(可能是隱含的)保留該同一個對象時,通常只需要執行“弱自我”方法。 但是,該對象不保留該塊。 它被框架保留,直到-performSelector:withObject:afterDelay:
fires,但這不是一個保留周期。
如果存在保留周期,則不應在塊中引用self
。 所以,你的嵌套案例在調用self
而不是weakSelf
上的消息時是錯誤的。
最后,是的,只要在執行后保留一個塊離開其聲明的范圍或者將其傳遞給非特定於塊的API,就需要[block copy]
。 也就是說,當你將它傳遞給dispatch_async()
時,你不需要復制一個塊,因為這是一個塊知道的API,它知道在必要時制作自己的副本。 但是-performSelector:withObject:afterDelay:
不是塊感知的。 它只是將其參數視為通用對象並保留它。 因此,您必須在將塊傳遞給它時復制該塊。
在這種情況下(下面) 使用強大的 self
,因為塊被復制只是幾秒鍾。 通常如果你想要self
執行阻止,你希望它保持活着直到那個時候,所以強大的參考是完全可以的。
[self performBlock:^{
[self doSomething]; // strong is OK
} afterDelay:delay];
塊內阻塞? 在你的情況下,這兩個塊只是延遲一次性塊,所以與上面相同,使用強。 但是塊之間存在差異。 如果您將塊存儲較長時間 ,也許對於多次調用,您應該避免保留周期。
例:
self.callback = ^{
[self doSomething]; // should use weakSelf
};
這可能會導致保留周期。 實際上,它取決於塊的使用方式。 我們看到該塊被存儲(復制)在屬性中供以后使用。 但是,您可以通過使不再使用的塊無效來阻止保留周期。 在這種情況下:
self.callback(); //invoke
self.callback = nil; //release
使用ARC時,您不必自己復制塊。 在添加塊之后,早期版本中存在錯誤,但現在ARC下的編譯器知道何時復制塊。 在這種情況下復制它很聰明:
[self performSelector:@selector(executeBlockAfterDelay:) withObject:block afterDelay:delay];
理解塊最重要的一點是它們在抽象實體中捕獲一段代碼(包括值),可以作為原子對象進行操作(將其保存在某處,傳遞,復制等等)。 實際上,它的實現方式可以保證默認情況下,您的塊將保持有效並在以后安全地執行。
然后捕獲並保留塊內所需的依賴項是必要的。
不幸的是,在某些情況下(實際上經常是),塊由創建它的實例保留,並且它自己保留該實例。 這被稱為保留循環,除非你自己打破其中一個保留關系,否則你的對象和你的塊不可能被釋放。 例如,如果您使用實例變量引用塊,並且未手動將其枚舉,則會發生這種情況。
這可能是塊的主要問題,特別是因為有時候,你不知道你的塊保留了你的自我實例(例如你的塊中的NSAssert)。 然后:
如果你立即執行你的塊並釋放它(使用你的塊並在執行后釋放它就發送它)沒有風險,因為你確定你自己引用的對象仍然存在。
但是如果執行被延遲,那么將對象保留在塊中是很重要的。 但在這種情況下,你的對象不應該保留你的塊以避免保留循環(A保留B和B保留A)。 如果您在方法的私有范圍內定義並可選地引用塊,則它是完美的。
關於副本。 是的,如果您的塊作為方法參數傳遞以確保在此范圍內具有+1 retainCount的干凈獨占塊,則使用副本會更安全。 但也許ARC已經為你做了。 不確定。 例如,performWithSelector似乎是免費的,然后復制並不危險。 只是一個無用的。 有時編譯器可以通過刪除它來優化它,但必須進行檢查。
我經常這樣做:
__unsafe_unretained __block id blockSelf = self;
然后在我的塊中使用它沒有問題。
所以在你的情況下:
__unsafe_unretained __block MyClass *blockSelf = self;
[self performBlock:^{
[weakSelf doSomething];
[self performBlock:^{
[weakSelf doSomething];
} afterDelay:1.0f];
} afterDelay:delay];
還要讓你的生活更輕松 - 制作一個實用程序類並將其放在標題中
void RunAfterDelay(NSTimeInterval delayInSeconds, dispatch_block_t block);
然后在.m文件中
void RunAfterDelay(NSTimeInterval delayInSeconds, dispatch_block_t block)
{
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, delayInSeconds * NSEC_PER_SEC), dispatch_get_main_queue(), block);
}
將實用程序導入前綴,您可以:
__unsafe_unretained __block MyClass *blockSelf = self;
RunAfterDelay(1.0f,^{
[blockSelf doSomething];
RunAfterDelay(delay,^{
[blockSelf doSomething];
})
});
我發現閱讀比詳細的默認值更好。
希望這可以幫助 :)
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.