iOS開(kāi)發(fā):block的分析及循環(huán)引用的解決方案

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)行copyblock本身會(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)。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末瓤逼,一起剝皮案震驚了整個(gè)濱河市笼吟,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌霸旗,老刑警劉巖贷帮,帶你破解...
    沈念sama閱讀 219,039評(píng)論 6 508
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場(chǎng)離奇詭異诱告,居然都是意外死亡撵枢,警方通過(guò)查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,426評(píng)論 3 395
  • 文/潘曉璐 我一進(jìn)店門蔬啡,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái)诲侮,“玉大人,你說(shuō)我怎么就攤上這事箱蟆」敌鳎” “怎么了?”我有些...
    開(kāi)封第一講書(shū)人閱讀 165,417評(píng)論 0 356
  • 文/不壞的土叔 我叫張陵空猜,是天一觀的道長(zhǎng)绽慈。 經(jīng)常有香客問(wèn)我,道長(zhǎng)辈毯,這世上最難降的妖魔是什么坝疼? 我笑而不...
    開(kāi)封第一講書(shū)人閱讀 58,868評(píng)論 1 295
  • 正文 為了忘掉前任,我火速辦了婚禮谆沃,結(jié)果婚禮上钝凶,老公的妹妹穿的比我還像新娘。我一直安慰自己唁影,他們只是感情好耕陷,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,892評(píng)論 6 392
  • 文/花漫 我一把揭開(kāi)白布。 她就那樣靜靜地躺著据沈,像睡著了一般哟沫。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上锌介,一...
    開(kāi)封第一講書(shū)人閱讀 51,692評(píng)論 1 305
  • 那天嗜诀,我揣著相機(jī)與錄音猾警,去河邊找鬼。 笑死隆敢,一個(gè)胖子當(dāng)著我的面吹牛发皿,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播拂蝎,決...
    沈念sama閱讀 40,416評(píng)論 3 419
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼雳窟,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼!你這毒婦竟也來(lái)了匣屡?” 一聲冷哼從身側(cè)響起,我...
    開(kāi)封第一講書(shū)人閱讀 39,326評(píng)論 0 276
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤拇涤,失蹤者是張志新(化名)和其女友劉穎捣作,沒(méi)想到半個(gè)月后,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體鹅士,經(jīng)...
    沈念sama閱讀 45,782評(píng)論 1 316
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡券躁,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,957評(píng)論 3 337
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了掉盅。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片也拜。...
    茶點(diǎn)故事閱讀 40,102評(píng)論 1 350
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖趾痘,靈堂內(nèi)的尸體忽然破棺而出慢哈,到底是詐尸還是另有隱情,我是刑警寧澤永票,帶...
    沈念sama閱讀 35,790評(píng)論 5 346
  • 正文 年R本政府宣布卵贱,位于F島的核電站,受9級(jí)特大地震影響侣集,放射性物質(zhì)發(fā)生泄漏键俱。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,442評(píng)論 3 331
  • 文/蒙蒙 一世分、第九天 我趴在偏房一處隱蔽的房頂上張望编振。 院中可真熱鬧,春花似錦臭埋、人聲如沸踪央。這莊子的主人今日做“春日...
    開(kāi)封第一講書(shū)人閱讀 31,996評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)杯瞻。三九已至,卻和暖如春炫掐,著一層夾襖步出監(jiān)牢的瞬間魁莉,已是汗流浹背。 一陣腳步聲響...
    開(kāi)封第一講書(shū)人閱讀 33,113評(píng)論 1 272
  • 我被黑心中介騙來(lái)泰國(guó)打工, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留旗唁,地道東北人畦浓。 一個(gè)月前我還...
    沈念sama閱讀 48,332評(píng)論 3 373
  • 正文 我出身青樓,卻偏偏與公主長(zhǎng)得像检疫,于是被迫代替她去往敵國(guó)和親讶请。 傳聞我的和親對(duì)象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 45,044評(píng)論 2 355

推薦閱讀更多精彩內(nèi)容