查看一下代碼思考:當在block中訪問的爲對象類型時,對象何時會銷燬?html
// ARC環境下代碼 typedef void (^Block)(void); int main(int argc, const char * argv[]) { @autoreleasepool { Block block; { Person *person = [[Person alloc] init]; person.age = 10; block = ^{ NSLog(@"------block內部%d",person.age); }; } // 執行完畢,person沒有被釋放 NSLog(@"--------"); } // person 釋放 return 0; }
大括號執行完畢以後,person依然不會被釋放。上一篇文章提到過,person爲aotu變量,傳入的block的變量一樣爲person,即block有一個強引用引用person,因此block不被銷燬的話,peroson也不會銷燬。ios
struct __main_block_impl_0 { struct __block_impl impl; struct __main_block_desc_0* Desc; Person * person; __main_block_impl_0(void *fp, struct __main_block_desc_0 *desc, Person *_person, int flags=0) : person(_person) { impl.isa = &_NSConcreteStackBlock; impl.Flags = flags; impl.FuncPtr = fp; Desc = desc; } };
在MRC環境下即便block還在,person卻被釋放掉了。由於MRC環境下block在棧空間,棧空間對外面的person不會進行強引用。c++
//MRC環境下代碼 int main(int argc, const char * argv[]) { @autoreleasepool { Block block; { Person *person = [[Person alloc] init]; person.age = 10; block = ^{ NSLog(@"------block內部%d",person.age); }; [person release]; } // person被釋放 NSLog(@"--------"); } return 0; }
block調用copy操做以後,person不會被釋放。iphone
block = [^{ NSLog(@"------block內部%d",person.age); } copy];
上文中也提到過,只須要對棧空間的block進行一次copy操做,將棧空間的block拷貝到堆中,person就不會被釋放,說明堆空間的block可能會對person進行一次retain操做,以保證person不會被銷燬。堆空間的block本身銷燬以後也會對持有的對象進行release操做。函數
也就是說棧空間上的block不會對對象強引用,堆空間的block有能力持有外部調用的對象,即對對象進行強引用或去除強引用的操做。spa
在__weak修飾以後,person在做用域執行完畢以後就被銷燬了。指針
typedef void (^Block)(void); int main(int argc, const char * argv[]) { @autoreleasepool { Block block; { Person *person = [[Person alloc] init]; person.age = 10; __weak Person *weakPerson = person; block = ^{ NSLog(@"------block內部%d",waekPerson.age); }; } NSLog(@"--------"); } return 0; }
-fobjc-arc -fobjc-runtime=ios-8.0.0
xcrun -sdk iphoneos clang -arch arm64 -rewrite-objc -fobjc-arc -fobjc-runtime=ios-8.0.0 main.m
struct __main_block_impl_0 { struct __block_impl impl; struct __main_block_desc_0* Desc; Person *__weak person; __main_block_impl_0(void *fp, struct __main_block_desc_0 *desc, Person *__weak _person, int flags=0) : person(_weakPerson) { impl.isa = &_NSConcreteStackBlock; impl.Flags = flags; impl.FuncPtr = fp; Desc = desc; } };
用__weak修飾的變量,在生成的__main_block_impl_0中也是使用__weak修飾捕獲。code
__main_block_copy\_0
和 __main_block_dispose_0
函數分析:當block中捕獲對象類型的變量時,咱們發現block結構體__main_block_impl_0的描述結構體__main_block_desc_0中多了兩個參數copy和dispose函數,查看源碼:
htm
copy和dispose函數中傳入的都是__main_block_impl_0結構體自己。對象
copy本質就是__main_block_copy_0函數,__main_block_copy_0函數內部調用_Block_object_assign函數,_Block_object_assign中傳入的是person對象的地址,person對象,以及8。
dispose本質就是__main_block_dispose_0函數,__main_block_dispose_0函數內部調用_Block_object_dispose函數,_Block_object_dispose函數傳入的參數是person對象,以及8。
當block進行copy操做的時候就會自動調用__main_block_desc_0內部的__main_block_copy_0函數,__main_block_copy_0函數內部會調用_Block_object_assign函數。
_Block_object_assign函數會自動根據__main_block_impl_0結構體內部的person是什麼類型的指針,對person對象產生強引用或者弱引用。能夠理解爲_Block_object_assign函數內部會對person進行引用計數器的操做,若是__main_block_impl_0結構體內person指針是__strong類型,則爲強引用,引用計數+1,若是__main_block_impl_0結構體內person指針是__weak類型,則爲弱引用,引用計數不變。
當block從堆中移除時就會自動調用__main_block_desc_0中的__main_block_dispose_0函數,__main_block_dispose_0函數內部會調用_Block_object_dispose函數。
_Block_object_dispose會對person對象作釋放操做,相似於release,也就是斷開對person對象的引用,而person到底是否被釋放仍是取決於person對象本身的引用計數。
一旦block中捕獲的變量爲對象類型,block結構體中的__main_block_desc_0會出兩個參數copy和dispose。由於訪問的是個對象,block但願擁有這個對象,就須要對對象進行引用,也就是進行內存管理的操做。好比說對對象進行retarn操做,所以一旦block捕獲的變量是對象類型就會會自動生成copy和dispose來對內部引用的對象進行內存管理。
當block內部訪問了對象類型的auto變量時,若是block是在棧上,block內部不會對person產生強引用。不論block結構體內部的變量是__strong修飾仍是__weak修飾,都不會對變量產生強引用。
若是block被拷貝到堆上。copy函數會調用_Block_object_assign函數,根據auto變量的修飾符(__strong,__weak,unsafe_unretained)作出相應的操做,造成強引用或者弱引用
若是block從堆中移除,dispose函數會調用_Block_object_dispose函數,自動釋放引用的auto變量。
下列代碼person在什麼時候銷燬?在person 類中重寫 delloc 方法
- (void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event { Person *person = [[Person alloc] init]; dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(3.0 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{ NSLog(@"%@",person); }); NSLog(@"touchBegin----------End"); } // Log : 打印結果 : touchBegin----------End // Log :打印結果 : <Person: 0x6080000080d0> // Log :打印結果 : person 對象調用 delloc
下列代碼person在什麼時候銷燬 ?
- (void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event { Person *person = [[Person alloc] init]; __weak Person *weakPerson = person; dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(3.0 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{ NSLog(@"%@",weakPerson); }); NSLog(@"touchBegin----------End"); } // Log : 打印結果 : touchBegin----------End // Log :打印結果 : person 對象調用 delloc // Log :打印結果 : null // 結果是 person 先銷燬才執行block 打印結果爲 null
經過示例代碼進行總結。
- (void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event { Person *person = [[Person alloc] init]; __weak Person *weakPerson = person; dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(1.0 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{ NSLog(@"weakPerson ----- %@",weakPerson); dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(3.0 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{ NSLog(@"person ----- %@",person); }); }); NSLog(@"touchBegin----------End"); } // Log : 打印結果 : touchBegin----------End // Log :1s 後 打印結果 : weakPerson -> <Person: 0x608000006050> // Log :3s 後 打印結果 : person -> <Person: 0x608000006050> // Log :打印結果 : person 對象調用 delloc
- (void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event { Person *person = [[Person alloc] init]; __weak Person *waekP = person; dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(1.0 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{ NSLog(@"person ----- %@",person); dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(3 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{ NSLog(@"weakP ----- %@",waekP); }); }); NSLog(@"touchBegin----------End"); } // Log : 打印結果 : touchBegin----------End // Log :1s 後 打印結果 : person -> <Person: 0x608000006050> // Log :打印結果 : person 對象調用 delloc // Log :3s 後 打印結果 : weakPerson -> null