2016年8月19日 星期五

[Objective-C] __weak與__block與__strong的介紹

在使用block時,會看到這三個東西,這三個又是做什麼的呢?

在這邊用一些例子來做解釋。

__block

在block裡面,要存取外面的變數時,要在變數前面加上這個關鍵字。

舉例來說。

int i = 0;
void(^myBlock)() = ^(){
i = i + 1;
};
//執行block
myBlock();
//印出i值
NSLog(@"%d", i);
view raw .m hosted with ❤ by GitHub


你想要在block去修改外面的變數,如果這樣寫的話會有錯誤,如果要在block裡面去修改變數,則要在前面加上__block關鍵字。

__block int i = 0;
void(^myBlock)() = ^(){
i = i + 1;
};
//執行block
myBlock();
//印出i值
NSLog(@"%d", i);
view raw .m hosted with ❤ by GitHub


__weak與__strong

這兩個會一起做說明,你可以先創一個class,在裡面加入一個方法來做測試,

就像這樣。

@interface MyObject : NSObject
-(void)test;
@end
view raw .h hosted with ❤ by GitHub


@implementation MyObject
-(void)test{
NSLog(@"A");
}
@end
view raw .m hosted with ❤ by GitHub


此時,先做第一個實驗,在block之中去執行剛才那個class的方法,

執行block後,馬上將此物件設為nil,在執行一次block。

MyObject *obj = [MyObject new];
void(^myBlock)() = ^(){
//執行test方法
[obj test];
};
//執行block
myBlock();
//將obj設為nil
obj = nil;
//執行block
myBlock();
view raw .m hosted with ❤ by GitHub


你會發現,明明設為nil了,在block裡面卻還會執行,因為這個block產生了循環引用的問題,那麼該怎麼解決呢,你可以加入__weak關鍵字。

MyObject *obj = [MyObject new];
//使用weak關鍵字
__weak MyObject *weakObj = obj;
void(^myBlock)() = ^(){
//使用weakObj執行test方法
[weakObj test];
};
//執行block
myBlock();
//將obj設為nil
obj = nil;
//執行block
myBlock();
view raw .m hosted with ❤ by GitHub


此時,就可以解決循環引用的問題,在這個block裡面並不是強引用,因此當外面設為nil時,block裡面就會變空了。

這樣看起來挺美好的,但是其實還有一個問題,當你的block在執行時,外面有人將設為nil時,你會發現裡面也會變成nil。

MyObject *obj = [MyObject new];
__weak MyObject *weakObj = obj;
void(^myBlock)() = ^(){
//先等五秒
sleep(5);
NSLog(@"5秒了");
[weakObj test];
};
dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
//用GCD執行你的Block
dispatch_async(queue, myBlock);
//設為nil
obj = nil;
view raw .m hosted with ❤ by GitHub


在block裡面等五秒,然後同時,外面設為nil,五秒後你會發現並沒有執行這個方法,因為已經被設為nil了,你只要在裡面加上__strong關鍵字,在block中就會是強參考

MyObject *obj = [MyObject new];
__weak MyObject *weakObj = obj;
void(^myBlock)() = ^(){
//設為強參考
__strong MyObject *strongObj = weakObj;
//先等五秒
sleep(5);
NSLog(@"5秒了");
[strongObj test];
};
dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
//用GCD執行你的Block
dispatch_async(queue, myBlock);
//先等一秒,以免還沒進block就設了
sleep(1);
//設為nil
obj = nil;
view raw .m hosted with ❤ by GitHub


不過要在設nil之前稍微等一下,以免還沒進去block就被設了XD。

此時你就不怕在block執行時,外面設nil會發生一些怪問題。

可是也許外面設nil,block執行時也會一起變成nil是你需要的效果,你就可以不用加入strong關鍵字。

透過這些實驗應該能稍微了解三者的差異了。

如果這篇文章有任何錯誤或者問題,歡迎提出。

沒有留言:

張貼留言