1. block如何引用外部基本數據類型變量?
block內引用基本數據類型,會直接將基本數據類型在內存中復制出另一個新的基本數據類型變量供block使用,相當于對象的深復制。
所以在執行block前改變了基本數據類型變量的值時,不會影響block內部已經進行過“深復制”的值。這里用引號包括深復制一詞,是因為深復制一詞的含義時對于對象來說的, 這里只是引申了深復制的含義。
// 若在執行block前,改變基本數據類型的值,不改變block內部已經引用的基本數據的值
inta =10;
void(^block1)(void) = ^{
NSLog(@"log: 引用的基本數據a:%d", a);
};
a =20;
block1();
// 打印結果:
// log: 引用的基本數據a:10
2. block如何引用外部對象?
block內引用對象, block會通過復制對象的地址來實現訪問對象,進行一次強引用,相當于一次淺復制,但由于在block內部不能對對象進行賦值操作,所以相當于強引用了一個readonly的對象。
// block可以調用對象的對象方法
// block內雖然不能對對象進行賦值操作, 但可以調用對象的對象方法。
NSMutableString * aString = [@"abc" mutableCopy];
void(^block2)(void) = ^{
NSLog(@"log:引用了NSMutableString對象 %@", aString);
[aStringappendString:@"--abc"];
NSLog(@"log:引用了NSMutableString對象, 并在block內部調用對象的對象方法 %@", aString);
};
block2();
// 打印結果:
// log:引用了NSMutableString對象 abc
// log:引用了NSMutableString對象, 并在block內部調用對象的對象方法 abc--abc
// block會強用對象
// 由于block會強引用對象, 所以即使在執行block前, 在block外部將對象指針置為空,
對象也不會立即銷毀, 在block執行完成后, block才會解除與對象的強引用關系。
// 在上面代碼的基礎上, 接著這樣做:
aString =nil;
block2();
// 打印結果:
// log:引用了NSMutableString對象 abc--abc
// log:引用了NSMutableString對象, 并在block內部調用對象的對象方法 abc--abc--abc
// 在上面代碼的基礎上,再接著打印aString的值, 這個是才會發現aString被銷毀。
aString =nil;
NSLog(@"log: 執行完block后的aString的值: %@", aString);
// 打印結果: log: 執行完block后的aString的值: (null)
// 正是由于block會強引用對象的關系, 所以需要避免block造成的循環引用問題。
// block執行完畢后, 對象sString不再被任何一個指針強引用, 所以aString被釋放。
3.__block的含義
先解釋一下block不被允許修改外部變量值的含義:這里所說的外部變量的值,指的是棧中指針的內存地址。對于對象,對象的指針地址是記錄在棧中(對象在堆中),我們不能修改的就是棧中的對象指針。
被block引用的外部變量在block內部是不能進行操作的,所以想要在block內部對外部變量賦值時,需要使用__block去修飾變量。
被__block修飾的對象,block會對對象類型的指針從棧中copy到堆中,這不會改變該指針所指向的堆中的地址。
被__block修飾的基本數據變量,block會將基本數據從棧上直接copy到堆上。
4.__weak修飾對象
由于block默認強引用對象,可能會造成block與對象間的循環引用,使用__weak弱引用修飾對象可以解決循環引用的問題。但由于weak指針可能會被自動置為空,所以如果block內部需要保證對象存活時,需要在block內部,使用strong對weak指針進行一次強引用,防止weak指針變為空。