前言
無論在面試還是在工作中,總會碰到 block 是什么?block 循環(huán)引用怎么辦?block 修飾符使用什么?等等這種類似的問題。
一、 什么是 block
1.1、Demo分析
1.1.1、Demo1
int main(int argc, const char * argv[]) {
@autoreleasepool {
/// 下面的代碼,就是一個簡單的 block
^{
NSLog(@"Hello Block");
};
}
return 0;
}
分析:上面的代碼,就是一個最簡單的 block 但是 NSLog 里面的代碼不會被打印出來,因為這個 block 沒人調(diào)用,所以永遠不會執(zhí)行。
1.1.2、Demo2
int main(int argc, const char * argv[]) {
@autoreleasepool {
int age = 18;
void (^block)(void) = ^{
NSLog(@"age--%d", age);
};
block();
}
return 0;
}
分析:如上,運行程序,控制臺會打印出 age--18。
1.1.3、分析 block 內(nèi)部實現(xiàn)
- 通過 clang 編譯可以將 OC 代碼轉(zhuǎn)化成 C++ 代碼,來查看 block 底層的實現(xiàn)原理
- 在終端上輸入
xcrun -sdk iphoneos clang -arch arm64 -rewrite-objc main.m會在main.m 這級目錄下生成一個 main.cpp 文件,就是我們想要的 轉(zhuǎn)化后的代碼
struct __block_impl {
void *isa;
int Flags;
int Reserved;
void *FuncPtr;
};
// 這個就是 block 內(nèi)部的結(jié)構(gòu),
struct __main_block_impl_0 {
struct __block_impl impl;
struct __main_block_desc_0* Desc;
int age;
// c++ 的構(gòu)造函數(shù)(類似于 OC 的 init 方法),返回一個結(jié)構(gòu)體對象。
// age(_age) 這句代碼,就是把 _age 賦值給 age
__main_block_impl_0(void *fp, struct __main_block_desc_0 *desc, int _age, int flags=0) : age(_age) {
impl.isa = &_NSConcreteStackBlock;
impl.Flags = flags;
impl.FuncPtr = fp;
Desc = desc;
}
};
// 封裝了 block 執(zhí)行邏輯的函數(shù),傳入到 fp,fp 在賦值到 impl.funcPtr
static void __main_block_func_0(struct __main_block_impl_0 *__cself) {
int age = __cself->age; // bound by copy
NSLog((NSString *)&__NSConstantStringImpl__var_folders_f__7ngz5gzx5sjgs4dlqrh7t58w0000gn_T_main_679898_mi_0, age);
}
static struct __main_block_desc_0 {
size_t reserved;
size_t Block_size;
} __main_block_desc_0_DATA = { 0, sizeof(struct __main_block_impl_0)};
// 這句代碼對應(yīng)我們上面的 main 函數(shù)
int main(int argc, const char * argv[]) {
/* @autoreleasepool */ { __AtAutoreleasePool __autoreleasepool;
int age = 18;
// 定義 block 變量
void (*block)(void) = ((void (*)())&__main_block_impl_0((void *)__main_block_func_0, &__main_block_desc_0_DATA, age));
// 執(zhí)行 block 內(nèi)部的代碼
((void (*)(__block_impl *))((__block_impl *)block)->FuncPtr)((__block_impl *)block);
}
return 0;
}
分析上面的代碼,在 main 函數(shù)里我們對比,轉(zhuǎn)化后的 C++ 代碼和 原生的 OC 代碼

__main_block_impl_0 這個結(jié)構(gòu)體就是 block 的本來面目
__main_block_impl_0
struct __main_block_impl_0 {
struct __block_impl impl;
struct __main_block_desc_0* Desc;
int age;
// c++ 的構(gòu)造函數(shù)(類似于 OC 的 init 方法),返回一個結(jié)構(gòu)體對象。
// age(_age) 這句代碼,就是把 _age 賦值給 age
__main_block_impl_0(void *fp, struct __main_block_desc_0 *desc, int _age, int flags=0) : age(_age) {
impl.isa = &_NSConcreteStackBlock;
impl.Flags = flags;
impl.FuncPtr = fp;
Desc = desc;
}
};
上面這個結(jié)構(gòu)體的構(gòu)造函數(shù),傳入的參數(shù):void *fp、struct __main_block_desc_0 *desc、int _age、int flags=0
由于 int flags=0 傳入的是常亮數(shù)值 0 因此可以忽略,int _age 這個參數(shù)是因為外面定義的 局部變量,也可以忽略,所以 這個構(gòu)造函數(shù)的必須參數(shù)有兩個 void *fp、struct __main_block_desc_0 *desc
上面的 C++ 代碼中在 可以看出 void *fp 對應(yīng)的是 &__main_block_impl_0、struct __main_block_desc_0 *desc 對應(yīng)的是 &__main_block_desc_0_DATA ,fp又賦值到了imp里面的 FuncPtr,desc 賦值到 Desc 就是 當前結(jié)構(gòu)體的 Desc。
__main_block_func_0
static void __main_block_func_0(struct __main_block_impl_0 *__cself) {
int age = __cself->age; // bound by copy
NSLog((NSString *)&__NSConstantStringImpl__var_folders_f__7ngz5gzx5sjgs4dlqrh7t58w0000gn_T_main_679898_mi_0, age);
}
上面是 block 執(zhí)行邏輯的函數(shù),傳入到 fp,fp 在賦值到 impl.funcPtr
__block_impl
struct __block_impl {
void *isa;
int Flags;
int Reserved;
void *FuncPtr;
};
__main_block_desc_0
static struct __main_block_desc_0 {
size_t reserved;
size_t Block_size;
} __main_block_desc_0_DATA = { 0, sizeof(struct __main_block_impl_0)};
這個結(jié)構(gòu)體存了 block 的大小。
1.1.4、Demo3 捕獲 auto 變量的 block
int main(int argc, const char * argv[]) {
@autoreleasepool {
int age = 18;
void (^block)(void) = ^{
NSLog(@"age--%d", age);
};
age = 20;
block();
}
return 0;
}
如上,我們修改了局部變量的 age 修改為20,但是運行后,block 里面打印的結(jié)果仍為 18.
- 這是因為 block 捕獲了 age = 18,進行了值傳遞,相當于直接把 age= 18 賦值給了 age, 無論外面怎么修改,都不會改變 age 的值,
- C 語言會在我們定義局部變量的時候,自動給我們的屬性加上 auto 修飾
1.1.5、Demo4 捕獲 static 變量的 block
int main(int argc, const char * argv[]) {
@autoreleasepool {
int age = 18;
static int height = 170;
void (^block)(void) = ^{
NSLog(@"age--%d,height--%d", age, height);
};
age = 20;
height = 180;
block();
}
return 0;
}
修改 height 的值,運行代碼,結(jié)果為 age--18,height--180
這是為什么呢,再次編譯運行生成 C++ 代碼
int main(int argc, const char * argv[]) {
/* @autoreleasepool */ { __AtAutoreleasePool __autoreleasepool;
int age = 18;
static int height = 170;
void (*block)(void) = ((void (*)())&__main_block_impl_0((void *)__main_block_func_0, &__main_block_desc_0_DATA, age, &height));
age = 20;
height = 180;
((void (*)(__block_impl *))((__block_impl *)block)->FuncPtr)((__block_impl *)block);
}
return 0;
}
從上面的 C++ 代碼可以看出傳遞給block 的height 是地址傳遞,指針傳遞
- block 內(nèi)部捕獲的是 *height 而不是 height。
1.1.6、Demo5 全局變量
int age = 22;
int height = 170;
int main(int argc, const char * argv[]) {
@autoreleasepool {
void (^block)(void) = ^{
NSLog(@"age--%d,height--%d", age, height);
};
age = 20;
height = 180;
block();
}
return 0;
}
運行上面的代碼,打印結(jié)果為 age--20,height--180,同樣,我們分析 生成的 C++ 代碼
int main(int argc, const char * argv[]) {
/* @autoreleasepool */ { __AtAutoreleasePool __autoreleasepool;
void (*block)(void) = ((void (*)())&__main_block_impl_0((void *)__main_block_func_0, &__main_block_desc_0_DATA));
age = 20;
height = 180;
((void (*)(__block_impl *))((__block_impl *)block)->FuncPtr)((__block_impl *)block);
}
return 0;
}
- block 并沒有捕獲 任何全局變量
1.2、總結(jié)
1.2.1、block 的內(nèi)存結(jié)構(gòu)


1.2.2、block 本質(zhì)
- block 本質(zhì)上也是一個 OC 對象,它內(nèi)部也有一個 isa 指針
- block 是封裝了函數(shù)調(diào)用以及函數(shù)調(diào)用環(huán)境的 OC 對象
二、block 的變量捕獲
為了保證 block 內(nèi)部能正常訪問外部的變量,block 有一個變量捕獲機制
- 如果變量的類型是局部變量,無論是 auto 還是 static 修飾,都會被捕獲到 block 內(nèi)部,但是 auto 變量是值傳遞,static 是指針傳遞。
- 如果變量類型是全局變量,不會被 block 捕獲到內(nèi)部,直接使用。
| 變量類型 | 是否能捕獲到 block 內(nèi)部 | 訪問方式 |
|---|---|---|
| 局部變量 auto | 是 | 值傳遞 |
| 局部變量 static | 是 | 指針傳遞 |
| 全局變量 | 否 | 直接訪問 |
為什么 block 要捕獲局部變量的值呢,auto:自動變量,離開作用域就銷毀
三、block 的類型
3.1、block 的對象特性
我們知道 block 的本質(zhì)就是OC 對象,所以O(shè)C對象的一些方法同樣適用于 block
int main(int argc, const char * argv[]) {
@autoreleasepool {
void (^block)(void) = ^{
NSLog(@"Hello");
};
NSLog(@"%@", [block class]);
NSLog(@"%@", [[block class] superclass]);
NSLog(@"%@", [[[block class] superclass] superclass]);
NSLog(@"%@", [[[[block class] superclass] superclass] superclass]);
}
return 0;
}
2020-12-20 16:55:50.239327+0800 MyBlock[18424:5457366] __NSGlobalBlock__
2020-12-20 16:55:50.239799+0800 MyBlock[18424:5457366] __NSGlobalBlock
2020-12-20 16:55:50.239839+0800 MyBlock[18424:5457366] NSBlock
2020-12-20 16:55:50.239871+0800 MyBlock[18424:5457366] NSObject
得出繼承關(guān)系為 NSGlobalBlock -> __NSGlobalBlock -> NSBlock ->NSObject
3.2、block 的類型查詢
int main(int argc, const char * argv[]) {
@autoreleasepool {
int a = 10;
// 堆:動態(tài)分配內(nèi)存,需要程序員申請申請,也需要程序員自己管理內(nèi)存
void (^block1)(void) = ^{
NSLog(@"Hello");
};
int age = 10;
void (^block2)(void) = ^{
NSLog(@"Hello - %d", age);
};
NSLog(@"%@ %@ %@", [block1 class], [block2 class], [^{
NSLog(@"%d", age);
} class]);
}
return 0;
}
在ARC 環(huán)境下:
2020-12-20 17:00:31.886757+0800 MyBlock[18515:5460672] __NSGlobalBlock__ __NSMallocBlock__ __NSStackBlock__
在 MRC 環(huán)境下:
2020-12-20 17:26:28.303551+0800 MyBlock[19030:5476820] __NSGlobalBlock__ __NSStackBlock__ __NSStackBlock__
在 MRC 環(huán)境下使用 copy
int main(int argc, const char * argv[]) {
@autoreleasepool {
int age = 10;
void (^block2)(void) = [^{
NSLog(@"Hello - %d", age);
} copy];
}
return 0;
}
同樣在 MRC 環(huán)境下,進行 copy 操作后打印結(jié)果和 ARC 環(huán)境下一樣
2020-12-20 17:27:42.096753+0800 MyBlock[19064:5478650] __NSMallocBlock__
如上,我們看打印結(jié)果得知 block 有 3 種類型,可以通過調(diào)用 class 方法或者 isa 指針查看具體類型,最終都是繼承自 NSBlock 類型
__NSGlobalBlock__(_NSConcreteGlobalBlock)沒有訪問 auto 變量
__NSMallocBlock__(_NSConcreteMallocBlock)__NSStackBlock__ 調(diào)用了 copy
__NSStackBlock__(_NSConcreteStackBlock)訪問了 auto 變量,(在ARC環(huán)境下顯示NSMallocBlock,在 MRC 環(huán)境下,顯示 NSStackBlock)
每一種類型的 block 調(diào)用 copy 后的結(jié)果如下:
| Block 的類 | 副本源的配置存儲域 | 復制效果 |
|---|---|---|
| _NSConcreteStackBlock | 棧 | 從棧復制到堆 |
| _NSConcreteGlobalBlock | 程序的數(shù)據(jù)區(qū)域 | 什么也不做 |
| _NSConcreteMallocBlock | 堆 | 引用計數(shù) + 1 |
內(nèi)存分布

四、block 的 copy
4.1、自動復制
在 ARC 環(huán)境下,編譯器會根據(jù)情況自動將棧上的 block 復制到堆上
4.1.1、block 作為函數(shù)返回值
typedef void (^MyBlock)(void);
MyBlock myblock() {
return ^{
NSLog(@"我被調(diào)用了");
};
}
int main(int argc, const char * argv[]) {
@autoreleasepool {
MyBlock block = myblock();
block();
}
return 0;
}
會打印出結(jié)果,如果沒有進行 copy
4.1.2、將 block賦值給 __strong 指針時
typedef void (^MyBlock)(void);
int main(int argc, const char * argv[]) {
@autoreleasepool {
int age = 10;
MyBlock block = ^{
NSLog(@", %d", age);
};
block();
NSLog(@"%@", [block class]);
}
return 0;
}
打印 [block class] 顯示為NSMallocBlock ,證明是NSStackBlock copy 之后的類型。
4.1.3、block 作為 Cocoa API 中方法名含有 usingBlock的方法參數(shù)時
// 如 NSArray 里面的函數(shù), 此時的 block 都是在堆上的,都是進行了 copy 的
NSArray *arr = @[];
[arr sortedArrayUsingComparator:^NSComparisonResult(id _Nonnull obj1, id _Nonnull obj2) {
}];
4.1.4、block 作為GCD API 的方法參數(shù)時
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
});
4.2、block屬性的建議寫法
4.2.1、MRC 環(huán)境下
- @property (copy, nonatomic) void (^block)(void);
4.2.2、ARC 環(huán)境下
- @property (strong, nonatomic) void (^block)(void);
- @property (copy, nonatomic) void (^block)(void);
4.3、對象類型的 auto 變量
4.3.1、當 block內(nèi)部訪問了對象類型的 auto 變量時
#import "MyPerson.h"
typedef void (^MyBlock)(void);
int main(int argc, const char * argv[]) {
@autoreleasepool {
MyBlock block;
{
MyPerson *person = [[MyPerson alloc] init];
person.age = 18;
block = ^{
NSLog(@"-----person.age===%d", person.age);
};
}
NSLog(@"-----");
}
return 0;
}
轉(zhuǎn)化C++ 代碼
struct __main_block_impl_0 {
struct __block_impl impl;
struct __main_block_desc_0* Desc;
MyPerson *person;
__main_block_impl_0(void *fp, struct __main_block_desc_0 *desc, MyPerson *_person, int flags=0) : person(_person) {
impl.isa = &_NSConcreteStackBlock;
impl.Flags = flags;
impl.FuncPtr = fp;
Desc = desc;
}
};
static void __main_block_copy_0(struct __main_block_impl_0*dst, struct __main_block_impl_0*src) {_Block_object_assign((void*)&dst->person, (void*)src->person, 3/*BLOCK_FIELD_IS_OBJECT*/);}
static void __main_block_dispose_0(struct __main_block_impl_0*src) {_Block_object_dispose((void*)src->person, 3/*BLOCK_FIELD_IS_OBJECT*/);}
static struct __main_block_desc_0 {
size_t reserved;
size_t Block_size;
void (*copy)(struct __main_block_impl_0*, struct __main_block_impl_0*);
void (*dispose)(struct __main_block_impl_0*);
} __main_block_desc_0_DATA = { 0, sizeof(struct __main_block_impl_0), __main_block_copy_0, __main_block_dispose_0};
- 如果 block 是在棧上
- 將不會對 auto 變量產(chǎn)生強引用。
- 如果 block 被拷貝到堆上
- 會調(diào)用 block 內(nèi)部的 copy 函數(shù)
- copy 函數(shù)內(nèi)部會調(diào)用 _Block_object_assign 函數(shù)
- _Block_object_assign 函數(shù)會根據(jù) auto 變量的修飾符(__strong、__weak、__unsafe_unretained)作出相對應(yīng)的操作,形成強引用或者弱引用
- 如果 block 從堆中移除
- 會調(diào)用 block 內(nèi)部的dispose 函數(shù)
- dispose 函數(shù)內(nèi)部會調(diào)用 _Block_object_dispose 函數(shù)
- _Block_object_dispose 函數(shù)會自動釋放引用的 auto 變量
| 函數(shù) | 調(diào)用時機 |
|---|---|
| copy 函數(shù) | 棧上的 block 復制到堆時 |
| dispose 函數(shù) | 堆上的 block 被廢棄時 |
把上面的 person 使用 __weak 修飾
__weak MyPerson *weakPerson = person;
block = ^{
NSLog(@"-----person.age===%d", weakPerson.age);
};
使用 __weak 修飾時,在使用clang轉(zhuǎn)換OC為C++代碼時,可能會遇到以下問題
/var/folders/f_/7ngz5gzx5sjgs4dlqrh7t58w0000gn/T/main-643d6e.mi:28880:28: error:
cannot create __weak reference because the current deployment target does
not support weak references
__attribute__((objc_ownership(weak))) MyPerson *weakPerson = person;
^
1 error generated.
針對 cannot create __weak reference in file using manual reference 這個問題,解決方案:支持ARC、指定運行時系統(tǒng)版本,比如增加 -fobjc-arc -fobjc-runtime=ios-8.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;
MyPerson *__weak weakPerson;
__main_block_impl_0(void *fp, struct __main_block_desc_0 *desc, MyPerson *__weak _weakPerson, int flags=0) : weakPerson(_weakPerson) {
impl.isa = &_NSConcreteStackBlock;
impl.Flags = flags;
impl.FuncPtr = fp;
Desc = desc;
}
};
五、__block 修飾符
執(zhí)行 下面的代碼,顯然會報錯,從上面的代碼中得知,block 中的 age 是block內(nèi)部的,想要main 函數(shù)中的 age 是不可能的。
typedef void (^MyBlock)(void);
int main(int argc, const char * argv[]) {
@autoreleasepool {
int age = 14;
MyBlock block = ^{
age = 18;//報錯 Variable is not assignable (missing __block type specifier)
NSLog(@"%d", age);
};
}
return 0;
}
如果 block 想要修改內(nèi)部的變量,可以使用 static 或者 全局變量,但是,
- 這種做法會一直占用內(nèi)存的空間
- 使用 static 修飾,會修改 block的類型,使用 static 修飾后,block 的類型變成了 ”NSGlobalBlock“,之前為”NSMallocBlock“
5.1、使用__block 修飾,修改變量
int main(int argc, const char * argv[]) {
@autoreleasepool {
__block int age = 14;
MyBlock block = ^{
age = 18;
NSLog(@"%d", age);
};
block();
NSLog(@"%@", [block class]);
}
return 0;
}
使用 __block 修飾后,block 的類型 依舊為 ”NSMallocBlock“,使用后發(fā)現(xiàn) block 內(nèi)部 有一個 __Block_byref_age_0 引用這 age這個指針,__Block_byref_age_0 里面有一個 age 變量,我們修改 age 其實就是修改 __Block_byref_age_0 里面的 age這個值。__forwarding 這個指針時指向自己的一個 指針。
struct __Block_byref_age_0 {
void *__isa;
__Block_byref_age_0 *__forwarding;
int __flags;
int __size;
int age;
};
struct __main_block_impl_0 {
struct __block_impl impl;
struct __main_block_desc_0* Desc;
__Block_byref_age_0 *age; // by ref
__main_block_impl_0(void *fp, struct __main_block_desc_0 *desc, __Block_byref_age_0 *_age, int flags=0) : age(_age->__forwarding) {
impl.isa = &_NSConcreteStackBlock;
impl.Flags = flags;
impl.FuncPtr = fp;
Desc = desc;
}
};
static void __main_block_func_0(struct __main_block_impl_0 *__cself) {
__Block_byref_age_0 *age = __cself->age; // bound by ref
(age->__forwarding->age) = 20; // 真正修改 age 的地方
NSLog((NSString *)&__NSConstantStringImpl__var_folders_f__7ngz5gzx5sjgs4dlqrh7t58w0000gn_T_main_f41d15_mi_0, (age->__forwarding->age));
}
5.2、__block 修飾符 原理
- __block 可以用于解決 block 內(nèi)部無法修改 auto 變量值得問題
- __block 不能修飾全局變量、靜態(tài)變量
- 使用 ____block 修飾時,編譯器會將 ____block 變量包裝成一個對象。
5.3、___block 的內(nèi)存管理
- 當 block 在棧上時,并不對 __block 變量產(chǎn)生強引用
- 當 block 在堆上時
- 會調(diào)用 block 內(nèi)部的 copy 函數(shù)
- copy 函數(shù)內(nèi)部會調(diào)用 _Block_object_assign 函數(shù)
- _Block_object_assign 函數(shù)會對 __block 變量形成強引用

- 當block 從堆中移除時
- 會調(diào)用 block 內(nèi)部的dispose 函數(shù)
- dispose 函數(shù)內(nèi)部會調(diào)用 _Block_object_dispose 函數(shù)
- _Block_object_dispose 函數(shù)會自動釋放引用的 __block 變量

5.4、__block 的 forwarding 指針
[圖片上傳失敗...(image-a5af69-1608559220453)]
5.5、對象類型的 auto 變量、__block 變量
當 block 在棧上時,對它們都不會產(chǎn)生強引用
-
當 block 拷貝到堆上時,都會通過 copy 函數(shù)來處理它們
- __block變量(假設(shè)變量名叫做a)
- _Block_object_assign((void)&dst->a, (void)src->a, 8/BLOCK_FIELD_IS_BYREF/);
-
對象類型的 auto 變量(假設(shè)變量名叫做p)
- _Block_object_assign((void)&dst->p, (void)src->p, 3/BLOCK_FIELD_IS_OBJECT/);
-
當 block 從堆上移除時,都會通過 dispose 函數(shù)來釋放它們
- __block變量(假設(shè)變量名叫做a)
- _Block_object_dispose((void)src->a, 8/BLOCK_FIELD_IS_BYREF*/);
-
對象類型的auto變量(假設(shè)變量名叫做p)
- _Block_object_dispose((void)src->p, 3/BLOCK_FIELD_IS_OBJECT*/);
-
對象 BLOCK_FIELD_IS_OBJECT __block 變量 BLOCK_FIELD_IS_BYREF
六、被__block 修飾的對象類型
- 當__block變量在棧上時,不會對指向的對象產(chǎn)生強引用
- 當__block變量被copy到堆時
- 會調(diào)用__block變量內(nèi)部的copy函數(shù)
- copy函數(shù)內(nèi)部會調(diào)用_Block_object_assign函數(shù)
- _Block_object_assign函數(shù)會根據(jù)所指向?qū)ο蟮男揎椃╛_strong、__weak、__unsafe_unretained)做出相應(yīng)的操作,形成強引用(retain)或者弱引用(注意:這里僅限于ARC時會retain,MRC時不會retain)
- 如果__block變量從堆上移除
- 會調(diào)用__block變量內(nèi)部的dispose函數(shù)
- dispose函數(shù)內(nèi)部會調(diào)用_Block_object_dispose函數(shù)
- _Block_object_dispose函數(shù)會自動釋放指向的對象(release)
六、循環(huán)引用
6.1、什么是循環(huán)引用

int main(int argc, const char * argv[]) {
@autoreleasepool {
MyPerson *p = [[MyPerson alloc] init];
p.age = 18;
p.block = ^{
NSLog(@"age is %d", p.age);
};
NSLog(@"-------");
}
return 0;
}
// MyPerson
typedef void (^MyBlock) (void);
@interface MyPerson : NSObject
@property (copy, nonatomic) MyBlock block;
@property (assign, nonatomic) int age;
@end
上面的代碼產(chǎn)生了循環(huán)引用,block 里面的代碼 無法執(zhí)行
6.2、解決循環(huán)引用
6.2.1、ARC 環(huán)境
1、使用 __weak
- 不會產(chǎn)生強引用,指向的對象銷毀時,會自動讓指針置為nil

MyPerson *p = [[MyPerson alloc] init];
__weak typeof(p) weakP = p;
p.age = 18;
p.block = ^{
NSLog(@"age is %d", weakP.age);
};
2、使用 __unsafe_unretained
- 不會產(chǎn)生強引用,不安全,指向的對象銷毀時,指針存儲的地址值不變
MyPerson *p = [[MyPerson alloc] init];
__unsafe_unretained typeof(p) weakP = p;
p.age = 18;
p.block = ^{
NSLog(@"age is %d", weakP.age);
};
3、使用 __block
- 使用 __block 必須 調(diào)用 block,并把對象置為 nil

__block MyPerson *p = [[MyPerson alloc] init];
p.age = 18;
p.block = ^{
NSLog(@"age is %d", p.age);
p = nil;
};
p.block();
6.2.2、MRC 環(huán)境
由于 MRC 環(huán)境不支持 __weak,所以只有兩種情況
1、__unsafe_unretained
- 和 ARC 一樣
2、__block
- 不需要置為 nil 和 手動調(diào)用 block