iOS開(kāi)發(fā)中block隨處可見(jiàn),什么是block呢仰坦?block是一個(gè)匿名函數(shù)履植,是一個(gè)代碼塊,把代碼放在這個(gè)代碼塊中悄晃,在需要使用的時(shí)候進(jìn)行調(diào)用玫霎。block會(huì)封裝函數(shù)以及函數(shù)的調(diào)用環(huán)境:
-封裝函數(shù):是指block會(huì)把block內(nèi)部的參數(shù)返回值、執(zhí)行體封裝成一個(gè)函數(shù)妈橄,并且存儲(chǔ)該函數(shù)的內(nèi)存地址庶近。
-封裝函數(shù)的調(diào)用環(huán)境:是指block會(huì)捕獲變量,并把這些變量存儲(chǔ)起來(lái)眷蚓。
將OC上層的代碼還原成c++代碼的方式:
- clang -rewrite-objc Input.m -o Output.cpp
- xcrun -sdk iphoneos clang -arch arm64e -rewrite-objc Input.m -o Output.cpp
一鼻种、block的類型和使用
iOS
開(kāi)發(fā)中有三種類型的block
,他們分別是全局block
沙热,堆block
媒鼓、棧block
侥加。
全局block(__NSGlobalBlock__)
:沒(méi)有訪問(wèn)外界局部普通變量的block
就是全局block
,存儲(chǔ)在全局區(qū)。
堆block(__NSMallocBlock__)
:對(duì)棧block進(jìn)行copy操作返回的block
就是堆block
究流,存儲(chǔ)在堆區(qū)馍惹。
棧block(__NSStackBlock__)
:訪問(wèn)了外界普通局部變量的block
就是棧block
策添,存儲(chǔ)在棧區(qū)白修。
注意:本章節(jié)的代碼均在
MRC
環(huán)境下開(kāi)啟的調(diào)試,MRC
下新建的僅訪問(wèn)局部普通變量的block
是棧block
寝贡,進(jìn)行copy
之后變成堆block
扒披。ARC
下編譯器會(huì)自動(dòng)將創(chuàng)建的棧block轉(zhuǎn)換成堆block
,如果不做轉(zhuǎn)換可以在block
前添加__weak
關(guān)鍵字圃泡。例如void(^__weak testBlock)() = ^{}
這樣定義即可碟案。
測(cè)試代碼
- (void)main{
int sValue = 20;
static int gValue = 20;
void(^gBlock)(void) = ^(){
NSLog(@"value=%d", gValue);
};
id cgBlock = [gBlock copy];
void(^sBlock)(void) = ^(){
NSLog(@"value=%d", sValue);
};
id csBlock = [sBlock copy];
NSLog(@"gBlock=%@,copy之后%@",gBlock,cgBlock);
NSLog(@"sBlock=%@,copy之后%@",sBlock,csBlock);
}
運(yùn)行結(jié)果:
gBlock=<__NSGlobalBlock__: 0x107728490>,copy之后<__NSGlobalBlock__: 0x107728490>
sBlock=<__NSStackBlock__: 0x7ffee84e1b68>,copy之后<__NSMallocBlock__: 0x600002e20960>
通過(guò)如上代碼,我們知道如果僅訪問(wèn)的局部靜態(tài)變量颇蜡,那么他仍然是個(gè)全局block
蟆淀,全局block copy
之后仍然是個(gè)全局block
(仍指向原來(lái)的內(nèi)存區(qū)域)。如果訪問(wèn)了局部普通變量澡匪,那么他就是棧block
,拷貝之后成了一個(gè)全新的堆block
(指向的內(nèi)存區(qū)域地址發(fā)生了變化)褒链。
在使用的過(guò)程中唁情,包括三個(gè)部分block的聲明(定義)、block的實(shí)現(xiàn)甫匹,block的調(diào)用:
block
的聲明的完整寫(xiě)法:返回值類型
+ (
+ ^
+ 屬性名稱
+ )
+ 參數(shù)列表
:
//作為屬性
@property (nonatomic, copy) int (^calcHashValue)(id value);
//作為函數(shù)參數(shù):
- (void)getHashValue:(int(^)(id value)) calcHashValue
//可以采用別名的方式簡(jiǎn)化block的定義:
typeof int (^CalcHashValue)(id value);
//作為屬性
@property (nonatomic, copy) CalcHashValue calcHashValue;
//作為函數(shù)參數(shù):
- (void)getHashValue:(CalcHashValue)calcHashValue;
block的實(shí)現(xiàn)完整寫(xiě)法:^
+ 返回值類型
+ 參數(shù)列表
+ 函數(shù)體
+;
:
//返回值類型:沒(méi)有的話可以不寫(xiě)甸鸟,也可以寫(xiě)`void`惦费;
//參數(shù)列表 :用小括號(hào)把參數(shù)列表包裹起來(lái),參數(shù)之間用逗號(hào)分隔抢韭。沒(méi)有參數(shù)的話可以寫(xiě)成`(void)`或者不寫(xiě)薪贫。
self.calcHashValue = ^int(id value){ return 22222222;};
//無(wú)參數(shù),無(wú)返回值案例:
typedef void(^TodoValue)(void);
@property (nonatomic, copy) TodoValue todoValue;
self.todoValue = ^void(void){};或者self.todoValue = ^{};
block的調(diào)用:
//需要注意的是在不能保證block有實(shí)現(xiàn)的時(shí)候刻恭,一定要檢查是否為空
if(self.calcHashValue) self.calcHashValue(self);
block在使用的時(shí)候瞧省,會(huì)有一些需要注意的問(wèn)題:
- 1.block內(nèi)部不能修改局部變量(直接報(bào)錯(cuò):
Variable is not assignable (missing __block type specifier)
)。如果需要修改則需要在局部變量的定義時(shí)加上__block
關(guān)鍵字鳍贾。
__block int localValue = 10;
void(^sBlock)(void) = ^(){
NSLog(@"localValue=%d", localValue);//localValue=10
localValue = 20;
};
sBlock();
NSLog(@"localValue=%d", localValue);//localValue=20
- 2.block實(shí)現(xiàn)之后調(diào)用之前修改局部變量鞍匾,block中拿到的仍然是舊值。如果要獲得最新值則需要在局部變量定義時(shí)加上
__block
關(guān)鍵字骑科。
//不加block關(guān)鍵字
int localValue = 10;
void(^sBlock)(void) = ^(){
NSLog(@"localValue=%d", localValue); //localValue=10
};
localValue = 20;
sBlock();
//加block關(guān)鍵字
__block int localValue = 10;
void(^sBlock)(void) = ^(){
NSLog(@"localValue=%d", localValue);//localValue=20
};
localValue = 20;
sBlock();
這個(gè)block關(guān)鍵字到底是做了什么呢橡淑?
二、block的底層實(shí)現(xiàn)
我們通過(guò)clang看下block底層是如何實(shí)現(xiàn)的
xcrun -sdk iphoneos clang -arch arm64e -rewrite-objc NXBlock.m
其中原始代碼如下:
@interface NXBlock : NSObject
- (void)t1;
- (void)t2;
@end
@implementation NXBlock
- (void)t1{
int localValue = 10;
void(^noneBlock)(void) = ^(){
NSLog(@"localValue=%d", localValue);
};
noneBlock();
}
- (void)t2{
__block int localValue = 10;
void(^withBlock)(void) = ^(){
NSLog(@"localValue=%d", localValue);
};
withBlock();
}
@end
生成.cpp文件后我們拷貝出相關(guān)的代碼片段:
如下本段是localValue沒(méi)有使用__block修飾的:
struct __NXBlock__t1_block_impl_0 {
struct __block_impl impl;
struct __NXBlock__t1_block_desc_0* Desc;
int localValue;
__NXBlock__t1_block_impl_0(void *fp, struct __NXBlock__t1_block_desc_0 *desc, int _localValue, int flags=0) : localValue(_localValue) {
impl.isa = &_NSConcreteStackBlock;
impl.Flags = flags;
impl.FuncPtr = fp;
Desc = desc;
}
};
static void __NXBlock__t1_block_func_0(struct __NXBlock__t1_block_impl_0 *__cself) {
int localValue = __cself->localValue; // bound by copy
NSLog((NSString *)&__NSConstantStringImpl__var_folders_fz_f2dd5f4545ggwp26d9pyjfwc0000gn_T_NXBlock_7c795f_mi_0, localValue);
}
static struct __NXBlock__t1_block_desc_0 {
size_t reserved;
size_t Block_size;
} __NXBlock__t1_block_desc_0_DATA = { 0, sizeof(struct __NXBlock__t1_block_impl_0)};
static void _I_NXBlock_t1(NXBlock * self, SEL _cmd) {
int localValue = 10;
void(*noneBlock)(void) = ((void (*)())&__NXBlock__t1_block_impl_0((void *)__NXBlock__t1_block_func_0, &__NXBlock__t1_block_desc_0_DATA, localValue));
((void (*)(__block_impl *))((__block_impl *)noneBlock)->FuncPtr)((__block_impl *)noneBlock);
}
如下本段是localValue使用__block修飾的:
struct __Block_byref_localValue_0 {
void *__isa;
__Block_byref_localValue_0 *__forwarding;
int __flags;
int __size;
int localValue;
};
struct __NXBlock__t2_block_impl_0 {
struct __block_impl impl;
struct __NXBlock__t2_block_desc_0* Desc;
__Block_byref_localValue_0 *localValue; // by ref
__NXBlock__t2_block_impl_0(void *fp, struct __NXBlock__t2_block_desc_0 *desc, __Block_byref_localValue_0 *_localValue, int flags=0) : localValue(_localValue->__forwarding) {
impl.isa = &_NSConcreteStackBlock;
impl.Flags = flags;
impl.FuncPtr = fp;
Desc = desc;
}
};
static void __NXBlock__t2_block_func_0(struct __NXBlock__t2_block_impl_0 *__cself) {
__Block_byref_localValue_0 *localValue = __cself->localValue; // bound by ref
NSLog((NSString *)&__NSConstantStringImpl__var_folders_fz_f2dd5f4545ggwp26d9pyjfwc0000gn_T_NXBlock_7c795f_mi_1, (localValue->__forwarding->localValue));
}
static void __NXBlock__t2_block_copy_0(struct __NXBlock__t2_block_impl_0*dst, struct __NXBlock__t2_block_impl_0*src) {
_Block_object_assign((void*)&dst->localValue, (void*)src->localValue, 8/*BLOCK_FIELD_IS_BYREF*/);
}
static void __NXBlock__t2_block_dispose_0(struct __NXBlock__t2_block_impl_0*src) {
_Block_object_dispose((void*)src->localValue, 8/*BLOCK_FIELD_IS_BYREF*/);
}
static struct __NXBlock__t2_block_desc_0 {
size_t reserved;
size_t Block_size;
void (*copy)(struct __NXBlock__t2_block_impl_0*, struct __NXBlock__t2_block_impl_0*);
void (*dispose)(struct __NXBlock__t2_block_impl_0*);
} __NXBlock__t2_block_desc_0_DATA = { 0, sizeof(struct __NXBlock__t2_block_impl_0), __NXBlock__t2_block_copy_0, __NXBlock__t2_block_dispose_0};
static void _I_NXBlock_t2(NXBlock * self, SEL _cmd) {
__attribute__((__blocks__(byref))) __Block_byref_localValue_0 localValue = {(void*)0,(__Block_byref_localValue_0 *)&localValue, 0, sizeof(__Block_byref_localValue_0), 10};
void(*withBlock)(void) = ((void (*)())&__NXBlock__t2_block_impl_0((void *)__NXBlock__t2_block_func_0, &__NXBlock__t2_block_desc_0_DATA, (__Block_byref_localValue_0 *)&localValue, 570425344));
((void (*)(__block_impl *))((__block_impl *)withBlock)->FuncPtr)((__block_impl *)withBlock);
}
我們通過(guò)一個(gè)表格來(lái)對(duì)比兩者的異同:
對(duì)比 | 無(wú)修飾 | __block修飾 | static修飾 | __weak修飾 |
---|---|---|---|---|
定義 | int localValue = 10; |
__block int localValue = 10; |
static int localValue = 10; |
__weak id self = self; |
完整結(jié)構(gòu)與構(gòu)造函數(shù) |
struct __NXBlock__t1_block_impl_0{} :-1.包括impl咆爽、Desc和localValue三個(gè)變量梁棠。 2.其中l(wèi)ocalValue這個(gè)外部局部變量在內(nèi)部的定義仍然是int localValue。 3.構(gòu)造函數(shù)包括調(diào)用的函數(shù)指針+描述+int型參數(shù)_localValue+flags,分別賦值給impl.FuncPtr斗埂、Desc符糊、localValue和impl.Flags。 |
struct __NXBlock__t2_block_impl_0{} :1.包括impl蜜笤、Desc和localValue三個(gè)變量濒蒋。 2.其中l(wèi)ocalValue這個(gè)外部局部變量在內(nèi)部的定義為__Block_byref_localValue_0 *localValue;是一個(gè)結(jié)構(gòu)體指針; 3.構(gòu)造函數(shù)包括調(diào)用的函數(shù)指針+描述+__Block_byref_localValue_0型參數(shù)_localValue+flags,分別賦值給impl.FuncPtr、Desc把兔、localValue和impl.Flags沪伙。 |
struct __NXBlock__t3_block_impl_0{} ;1.包括impl、Desc和localValue三個(gè)變量县好。 2.其中l(wèi)ocalValue這個(gè)外部變量在內(nèi)部的定義是int *localValue围橡。 3.構(gòu)造函數(shù)包括調(diào)用的函數(shù)指針+描述+int*類型參數(shù)_localValue+flags,分別賦值給impl.FuncPtr、Desc缕贡、localValue和impl.Flags翁授。 |
struct __NXBlock__t4_block_impl_0{} ;1.包括impl、Desc和localValue三個(gè)變量晾咪。 2.其中l(wèi)ocalValue這個(gè)外部變量在內(nèi)部的定義是__weak id localValue收擦。 3.構(gòu)造函數(shù)包括調(diào)用的函數(shù)指針+描述+weak id類型參數(shù)_localValue+flags,分別賦值給impl.FuncPtr、Desc谍倦、localValue和impl.Flags |
外部變量在內(nèi)部的定義 | struct->localValue訪問(wèn)原始數(shù)據(jù); | 封裝在struct __Block_byref_localValue_0{}結(jié)構(gòu)體中 1. 整形int類型變量localValue存儲(chǔ)了外部傳入的localValue值. 2.結(jié)構(gòu)體指針類型__Block_byref_localValue_0 *的變量指向自己(localValue(_localValue->__forwarding)說(shuō)明了這一點(diǎn))塞赂。 3.struct->localValue->__forwarding->localValue即是原始localValue; |
(*struct.localValue)訪問(wèn)原始數(shù)據(jù)。 | struct.localValue訪問(wèn)原始數(shù)據(jù) |
block函數(shù)指針 | __NXBlock__t1_block_func_0(...)即是外部調(diào)用block的執(zhí)行函數(shù)昼蛀。 1.函數(shù)包括一個(gè)完整block的參數(shù)__cself宴猾。 2.通過(guò)__cself->localValue獲得外界的值圆存。 |
__NXBlock__t2_block_func_0(...)即是外部調(diào)用block的執(zhí)行函數(shù)。 1.函數(shù)包括一個(gè)完整block的參數(shù)__cself仇哆。 2.通過(guò)__cself->localValue->__forwarding->localValue獲得外界的值沦辙。 |
__NXBlock__t3_block_func_0(...)即是外部調(diào)用block的執(zhí)行函數(shù)。 1.函數(shù)包括完整block的參數(shù)__cself讹剔。 2.通過(guò)(*__cself.localvalue)獲得外界的值油讯。 |
__NXBlock__t4_block_func_0(...)即是外部調(diào)用block的執(zhí)行函數(shù)。 1.函數(shù)包括完整block的參數(shù)__cself辟拷。 2.通過(guò)__cself.localvalue獲得外界的值 |
描述結(jié)構(gòu) | __NXBlock__t1_block_desc_0: 1:保留字段reserved默認(rèn)值為0; 2.Block_size記錄完成結(jié)構(gòu)體的大小; |
__NXBlock__t2_block_desc_0; 1.保留字段reserved默認(rèn)值為0撞羽。 2.Block_size記錄完成結(jié)構(gòu)體的大小; 3.copy函數(shù)指針; 4.dispose函數(shù)指針; |
__NXBlock__t3_block_desc_0: 1:保留字段reserved默認(rèn)值為0; 2.Block_size記錄完成結(jié)構(gòu)體的大小; |
__NXBlock__t4_block_desc_0: 1:保留字段reserved默認(rèn)值為0; 2.Block_size記錄完成結(jié)構(gòu)體的大小; |
copy函數(shù) | / | __NXBlock__t2_block_copy_0; | / | / |
dispose函數(shù) | / | __NXBlock__t2_block_dispose_0; | / | / |
-
__Block_byref_localValue_0
構(gòu)造過(guò)程中傳給__forwarding
的是__Block_byref_localValue_0
類型,是變量自身衫冻,也就是src.__forwarding
指向的是自己诀紊,這個(gè)是block
在棧區(qū)的情況。 - 當(dāng)棧區(qū)
block
被復(fù)制到堆區(qū)的時(shí)候隅俘,src
結(jié)構(gòu)體會(huì)被復(fù)制一份邻奠,復(fù)制出來(lái)的在堆區(qū)copy. __forwarding = copy;
并且棧區(qū)src. __forwarding = copy;
了。 - 這樣以來(lái)为居,無(wú)論是通過(guò)訪問(wèn)原有的棧區(qū)的
block
還是新拷貝的堆block
碌宴,那么通過(guò)block. byref. __forwarding
獲取到的都是堆內(nèi)存中的那一份。
我們可以通過(guò)一段代碼驗(yàn)證這個(gè)結(jié)果:
__block int localValue = 10;
NSString *(^stackBlock)(void) = ^NSString *(){
return [NSString stringWithFormat:@"&localValue=%p", &localValue];
};
NSLog(@"拷貝前:%@-%@", stackBlock, stackBlock());
NSString *(^mallocBlock)(void) = [stackBlock copy];
NSLog(@"拷貝后:%@-%@", stackBlock, stackBlock());
NSLog(@"拷貝后:%@-%@", mallocBlock, mallocBlock());
打印結(jié)果
拷貝前:<__NSStackBlock__: 0x7ffee17f6b60>-&localValue=0x7ffee17f6ba8
拷貝后:<__NSStackBlock__: 0x7ffee17f6b60>-&localValue=0x600000472878
拷貝后:<__NSMallocBlock__: 0x600000a5d200>-&localValue=0x600000472878
可以看到拷貝后蒙畴,新生成了一個(gè)堆block
對(duì)象并且block
捕獲的localValue
的地址發(fā)生了變化贰镣,由0x7ffee17f6ba8
變成了0x600000472878
,而且原有的棧block
捕獲的localValue
的地址也由0x7ffee17f6ba8
變成了0x600000472878
膳凝,跟堆block
保持一致碑隆。
細(xì)節(jié)邏輯可以在源碼中看到:
// src points to stack
struct Block_byref *copy = (struct Block_byref *)malloc(src->size);
copy->isa = NULL;
// byref value 4 is logical refcount of 2: one for caller, one for stack
copy->flags = src->flags | BLOCK_BYREF_NEEDS_FREE | 4;
copy->forwarding = copy; // patch heap copy to point to itself
src->forwarding = copy; // patch stack to point to heap copy
copy->size = src->size;
簡(jiǎn)單總結(jié):
1.沒(méi)有使用__block
關(guān)鍵字修飾的localValue
是一個(gè)簡(jiǎn)單int
類型,傳入內(nèi)部也是的int
類型的localValue
變量.
2.使用__block
關(guān)鍵字修飾的localValue
會(huì)生成為__Block_byref
的結(jié)構(gòu)體蹬音,傳入內(nèi)部也是__Block_byref
類型的localValue
結(jié)構(gòu)體指針上煤。
3.棧上的block
進(jìn)行copy
:block
本身會(huì)在堆上開(kāi)辟內(nèi)存;__Block_byref
在堆上新開(kāi)辟內(nèi)存著淆;捕獲的外部變量也會(huì)在堆上新開(kāi)辟內(nèi)存劫狠。原有的棧block
的__Block_byref
的__forwarding
會(huì)指向堆block
的__Block_byref
。
三永部、block中的循環(huán)引用
如果對(duì)象A強(qiáng)持有對(duì)象B:
-初始化完成后A的引用計(jì)數(shù)為1独泞,B的引用計(jì)數(shù)為1。B的引用計(jì)數(shù)會(huì)因?yàn)锳的持有而+1變成2苔埋。
-在AB出作用域后系統(tǒng)會(huì)給AB分別發(fā)送一個(gè)release消息懦砂,A的引用計(jì)數(shù)-1變成0;B的引用計(jì)數(shù)-1變成1。
-A引用計(jì)數(shù)變成0調(diào)用dealloc方法進(jìn)行銷毀,A調(diào)用dealloc方法時(shí)也會(huì)給B發(fā)送一個(gè)release消息孕惜,B的引用計(jì)數(shù)-1變成0,則B會(huì)調(diào)用dealloc方法進(jìn)行銷毀晨炕。
如果對(duì)象A強(qiáng)持有對(duì)象B衫画,B也強(qiáng)持有A:
-初始化完成后AB引用計(jì)數(shù)為1,相互賦值后兩者的引用計(jì)數(shù)都變成2瓮栗。
-AB出作用域后系統(tǒng)分別給AB發(fā)送release消息削罩。A的引用計(jì)數(shù)變成1,B的引用計(jì)數(shù)變成1费奸。
-兩者維持引用計(jì)數(shù)為1而得不到釋放弥激,造成內(nèi)存泄漏。
而block中的循環(huán)引用出現(xiàn)的通常是由于self強(qiáng)制有block愿阐,而block又強(qiáng)持有self造成的循環(huán)引用微服。如果能通過(guò)weak斷開(kāi)引用環(huán)那么問(wèn)題就解決了。
2.1.使用weak(/strong)修飾斷開(kāi)環(huán)的方式避免循環(huán)引用:
準(zhǔn)備一段代碼:
@interface NXTester: NSObject
@property (nonatomic, copy) void(^work)(void);
@property (nonatomic, copy) void(^test)(void);
@end
@implementation NXTester
- (void)dealloc{
NSLog(@"NXTester-dealloc");
}
@end
案例1:如下代碼會(huì)造成循環(huán)引用缨历,tester持有work以蕴,work持有tester,形成環(huán)構(gòu)成循環(huán)引用辛孵。解決方案看案例2.
NXTester *tester = [[NXTester alloc] init];
tester.work = ^{
tester;
};
tester.work();
案例2:如下代碼不會(huì)造成循環(huán)引用丛肮,tester持有work,work持有weakself, weakself弱持有tester魄缚。弱引用斷開(kāi)了這個(gè)閉環(huán)宝与,不會(huì)循環(huán)引用。這一點(diǎn)原因可以參考上述表格的__weak修飾的局部變量部分冶匹,在內(nèi)部定義成__weak NXTester *類型习劫。
NXTester *tester = [[NXTester alloc] init];
__weak NXTester *weakself = tester;
tester.work = ^{
weakself;
};
tester.work();
案例3:如下代碼會(huì)造成循環(huán)引用,注釋掉的部分也會(huì)循環(huán)引用徙硅。因?yàn)閟trongself強(qiáng)持有test榜聂,test又強(qiáng)持有strongself,形成環(huán)構(gòu)成循環(huán)引用嗓蘑,解決辦法參考案例4.
NXTester *tester = [[NXTester alloc] init];
__weak NXTester *weakself = tester;
tester.work = ^{
//weakself.test = ^{
// weakself;
//};
//weakself.test();
//或者
__strong NXTester *strongself = weakself;
strongself.test = ^{
strongself;
};
strongself.test();
};
tester.work();
案例4:如下代碼不會(huì)造成循環(huán)引用须肆。通過(guò)weak斷開(kāi)了引用環(huán)。
NXTester *tester = [[NXTester alloc] init];
__weak NXTester *weakself = tester;
tester.work = ^{
__strong NXTester *strongself = weakself;
__weak NXTester *weakweakself = strongself;
strongself.test = ^{
weakweakself;
};
strongself.test();
};
tester.work();
如上我們看到第一種解決循環(huán)引用的方式是weak-stong修飾桩皿。block內(nèi)部需要訪問(wèn)的變量用在block外先用weak修飾豌汇,內(nèi)部使用時(shí)在用strong修飾。
2.2泄隔、使用參數(shù)傳遞來(lái)解決循環(huán)引用的問(wèn)題:
以上案例中拒贱,我們需要在block內(nèi)部捕獲外部的變量。我們也可以通過(guò)參數(shù)傳遞的方法來(lái)解決。
@interface NXTester: NSObject
@property (nonatomic, copy) void(^work)(NXTester *value);
@end
@implementation NXTester
- (void)dealloc{
NSLog(@"NXTester-dealloc");
}
@end
使用:
NXTester *tester = [[NXTester alloc] init];
tester.work = ^(NXTester *value){
value;
};
tester.work(tester);
2.3.使用完畢后將對(duì)象設(shè)置為nil
如果一個(gè)通過(guò)block執(zhí)行任務(wù)后不再需要保留了逻澳,那么可以在block中將對(duì)象設(shè)置為nil闸天,或在不使用之后把block或者對(duì)象設(shè)置為nil都可以解決問(wèn)題。這種方案不推薦斜做。
注意事項(xiàng):
有些場(chǎng)景下不一定會(huì)造成循環(huán)引用苞氮,關(guān)鍵看有沒(méi)有無(wú)法斷開(kāi)的引用環(huán)。