__block修飾符
- __block可以用于解決block內部無法修改auto變量值的問題
- __block不能修飾全局變量、靜態變量(static)
- 編譯器會將__block變量包裝成一個對象
結構圖如下
結構圖.png
__block的本質
代碼佐證
// 定義一個block
typedef void (^CSBlock) (void);
// block內部實現
struct __Block_byref_age_0 {
void *__isa;
struct __Block_byref_age_0 *__forwarding;
int __flags;
int __size;
int age;
};
struct __main_block_desc_0 {
size_t reserved;
size_t Block_size;
void (*copy)(void);
void (*dispose)(void);
};
struct __block_impl {
void *isa;
int Flags;
int Reserved;
void *FuncPtr;
};
struct __main_block_impl_0 {
struct __block_impl impl;
struct __main_block_desc_0* Desc;
struct __Block_byref_age_0 *age;
};
int main(int argc, const char * argv[]) {
@autoreleasepool {
__block int age = 10;
CSBlock block = ^{
age = 20;
NSLog(@"age is %d", age);
};
struct __main_block_impl_0 *blockImpl = (__bridge struct __main_block_impl_0 *)block;
NSLog(@"%p", &age);
}
return 0;
}
image.png
可知外界訪問age,實際上在block內部是訪問結構體
__Block_byref_age_0
中的age
變量
三種變量在block中的表現形式
image.png
image.png
通過查看源碼可知,不同屬性修飾的變量,在block中表現形式不同
__block的內存管理
1.當block在棧上時,并不會對__block變量產生強引用
-
2.當block被copy到堆時
- 會調用block內部的copy函數
- copy函數內部會調用_Block_object_assign函數
- _Block_object_assign函數會對__block變量形成強引用(retain)
image.png
image.png
- 3.當block從堆中移除時
- 會調用block內部的dispose函數
- dispose函數內部會調用_Block_object_dispose函數
- _Block_object_dispose函數會自動釋放引用的__block變量(release)
image.png
對象類型的auto變量、__block變量 異同
1.當block在棧上時,對它們都不會產生強引用
2.當block拷貝到堆上時,都會通過copy函數來處理它們
__block變量
(假設變量名叫做a)
_Block_object_assign((void*)&dst->a, (void*)src->a, 8/*BLOCK_FIELD_IS_BYREF*/);
對象類型的auto變量
(假設變量名叫做p)
_Block_object_assign((void*)&dst->p, (void*)src->p, 3/*BLOCK_FIELD_IS_OBJECT*/);
- 3.當block從堆上移除時,都會通過dispose函數來釋放它們
__block
變量(假設變量名叫做a)
_Block_object_dispose((void*)src->a, 8/*BLOCK_FIELD_IS_BYREF*/);
對象類型的auto變量
(假設變量名叫做p)
_Block_object_dispose((void*)src->p, 3/*BLOCK_FIELD_IS_OBJECT*/);
image.png
__block的__forwarding指針
被__block修飾的對象類型
1.當__block變量在棧上時,不會對指向的對象產生強引用
-
2.當__block變量被copy到堆時
- 會調用__block變量內部的copy函數
- copy函數內部會調用_Block_object_assign函數
- _Block_object_assign函數會根據所指向對象的修飾符(__strong、__weak、__unsafe_unretained)做出相應的操作,形成強引用(retain)或者弱引用
(注意:這里僅限于ARC時會retain,MRC時不會retain)
-
3.如果__block變量從堆上移除
- 會調用__block變量內部的dispose函數
- dispose函數內部會調用_Block_object_dispose函數
- _Block_object_dispose函數會自動釋放指向的對象(release)
代碼實現如下
- ARC環境下,有__weak修飾
int main(int argc, const char * argv[]) {
@autoreleasepool {
// 演示被__block修飾的變量類型
// 1.有__weak修飾
CSBlock block;
{
Person *person = [[Person alloc] init];
__block __weak Person *weakPerson = person;
block = ^ {
NSLog(@"%p",weakPerson);
};
}
block();
}
return 0;
}
運行結果
image.png
- ARC環境下,沒有__weak修飾
int main(int argc, const char * argv[]) {
@autoreleasepool {
// 演示被__block修飾的變量類型
// 1.有__weak修飾
CSBlock block;
{
Person *person = [[Person alloc] init];
__block Person *weakPerson = person;
block = ^ {
NSLog(@"%p",weakPerson);
};
}
block();
}
return 0;
}
運行結果
image.png
- MRC環境下,沒有__weak修飾
int main(int argc, const char * argv[]) {
@autoreleasepool {
// 3.MRC環境下
Person *person = [[Person alloc] init];
CSBlock block = [^{
NSLog(@"%p", person);
} copy];
[person release];
block();
[block release];
}
return 0;
}
image.png
本文參考借鑒MJ的教程視頻,非常感謝.
項目演示代碼
iOS-block-__block的本質
更多block相關文章