block通常使用過程當中都是對對象變量的捕獲,那麼對象變量的捕獲同基本數據類型變量相同嗎? 當在block中訪問的爲對象類型時,對象何時會銷燬?ios
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 --- dealloc
}
return 0;
}
複製代碼
大括號執行完畢以後,person不會被釋放。,person爲auto變量,傳入的block的變量一樣爲person,即block有一個強引用引用person,因此block不被銷燬的話,person也不會銷燬。c++
查看源代碼確實如此bash
將上述代碼轉移到MRC環境下,在MRC環境下即便block還在,person卻被釋放掉了。由於MRC環境下block在棧上,棧上的block對外面的person不會進行強引用。iphone
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 release];
// Person --- dealloc
}
NSLog(@"--------");
}
return 0;
}
block調用copy操做以後,person不會被釋放。
block = [^{
NSLog(@"------block內部%d",person.age);
} copy];
複製代碼
只須要對棧空間的block進行一次copy操做,將棧空間的block會拷貝到堆中,person就不會被釋放,說明堆空間的block可能會對person進行一次retain操做,以保證person不會被銷燬。堆空間的block本身銷燬以後也會對持有的對象進行release操做。函數
也就是說棧空間上的block不會對對象強引用,堆空間的block有能力持有外部調用的對象,即對對象進行強引用或去除強引用的操做。ui
__weak添加以後,person在做用域執行完畢以後就被銷燬了。spa
typedef void (^Block)(void);
int main(int argc, const char * argv[]) {
@autoreleasepool {
Block block;
{
Person *person = [[Person alloc] init];
person.age = 10;
__weak Person *waekPerson = person;
block = ^{
NSLog(@"------block內部%d",waekPerson.age);
};
}
NSLog(@"--------");
}
return 0;
}
複製代碼
將代碼轉化爲c++來看一下上述代碼之間的差異。 __weak修飾變量,須要告知編譯器使用ARC環境及版本號不然會報錯,添加說明-fobjc-arc -fobjc-runtime=ios-8.0.03d
xcrun -sdk iphoneos clang -arch arm64 -rewrite-objc -fobjc-arc -fobjc-runtime=ios-8.0.0 main.m
指針
當block中捕獲對象類型的變量時,咱們發現block結構體__main_block_impl_0的描述結構體__main_block_desc_0中多了兩個參數copy和dispose函數,查看源碼:code
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_object_dispose函數調用時機及做用
當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變量。
複製代碼
一、
- (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");
}
複製代碼
二、
- (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)(3.0 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
NSLog(@"%@",waekP);
});
NSLog(@"touchBegin----------End");
}
複製代碼
三、
- (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(@"weakP ----- %@",waekP);
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(3.0 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
NSLog(@"person ----- %@",person);
});
});
NSLog(@"touchBegin----------End");
}
複製代碼
四、
- (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");
}
複製代碼
一、block做爲GCD API的方法參數時會自動進行copy操做,所以block在堆空間,而且使用強引用訪問person對象,所以block內部copy函數會對person進行強引用。當block執行完畢須要被銷燬時,調用dispose函數釋放對person對象的引用,person沒有強指針指向時纔會被銷燬。
故:在3秒後銷燬person對象
二、block中對waekP爲__weak弱引用,所以block內部copy函數會對person一樣進行弱引用,當大括號執行完畢時,person對象沒有強指針引用就會被釋放。所以block塊執行的時候打印null。
故:立馬銷燬person對象
第三個和第四個你們可自行驗證。