struct __Block_byref__para1_0 { void *__isa;__Block_byref__para1_0 *__forwarding; int __flags; int __size; char *_para1;};
block結構體相應的也有一個成員引用,這樣會增加對局部變量的 _para1引用,在Block銷毀的時候引用就釋放掉了
struct __main1_block_impl_0 { struct __block_impl impl; struct __main1_block_desc_0* Desc; __Block_byref__para1_0 *_para1; // by ref}
struct __KDBlockTest__test3_block_impl_0 { struct __block_impl impl; struct __KDBlockTest__test3_block_desc_0* Desc; KDBlockTest *self;
}
這樣會增加對self的引用,在Block銷毀的時候引用就釋放掉了
在使用的時候需要注意循環引用,即某個對象有一個copy或者strong的 block成員屬性,這時block內部直接引用了 成員變量(全局變量) 或者self,這樣就產生了self持有 block成員,block成員又持有了self,就會導致循環引用。
我們看以下代碼(ARC):
typedef void(^ActionTest)(void);@interface KDBlockTest(){ __block NSString *_person2; ActionTest _action;}
@implementation KDBlockTest#PRagma mark - system-(instancetype)init{ self=[super init]; if(self) { [self test3]; } return self;}-(void)dealloc{ NSLog(@"KDBlockTest dealloc");}#pragma mark - private////循環引用-(void )test3{ _person2=@"person2"; _action= ^(void) { //block內賦值 NSLog(@"excuteing _person2:%@,%p",_person2,_person2); }; _action();}
這樣我們執行以下代碼:
KDBlockTest *_test=[[KDBlockTest alloc]init];
通過調試發現沒有走到dealloc,這里不管成員變量 _person2 是否聲明 __block都沒有什么影響。
成員變量 這一篇已經詳細介紹了,對于block 使用 成員變量、self來說,block內部是直接強引用self的。也就是block持有了self,在這里bock又作為self的一個成員被持有,所以就形成了相互引用,導致無法釋放。
對于上面這種情況,我們引入了__weak解決,__weak不會增加對象的引用計數,而且當指向的內存銷毀后,__weak指針會自動置為nil。
我們對上面的代碼稍作修改
-(void )test3{ __weak typeof(self) _weakSelf=self; _person2=@"person2"; NSLog(@"init:%@,%p,%p",_person2,_person2,&self); _action= ^(void) { //block內賦值 NSLog(@"excuteing _person2:%@,%p,%p",_weakSelf.person2,_weakSelf.person2,&_weakSelf); }; _action();}
輸出日志:
2014-07-29 13:38:30.872 Test[2642:60b] init:person2,0x5b980,0x27dae9442014-07-29 13:38:30.875 Test[2642:60b] excuteing _person2:person2,0x5b980,0x1562ed442014-07-29 13:38:30.876 Test[2642:60b] KDBlockTest dealloc
從日志可以看出block內部使用 person2 、_weakSelf 和外面的 person2 、self 地址是一樣的,看來也是引用關系,既達到block內部修改變量的效果,又沒有對變量產生強引用。我們來看下轉換后的代碼:
block結構體的定義:
struct __KDBlockTest__test3_block_impl_0 { struct __block_impl impl; struct __KDBlockTest__test3_block_desc_0* Desc; __weak typeof (self) _weakSelf; __KDBlockTest__test3_block_impl_0(void *fp, struct __KDBlockTest__test3_block_desc_0 *desc, __weak typeof (self) __weakSelf, int flags=0) : _weakSelf(__weakSelf) { impl.isa = &_NSConcreteStackBlock; impl.Flags = flags; impl.FuncPtr = fp; Desc = desc; }};
重點就在這,使用_weak聲明的self,block結構體對應 也生成了一個_weak的self成員。我們在看下 我們的test3 方法:
static void _I_KDBlockTest_test3(KDBlockTest * self, SEL _cmd) { __attribute__((objc_gc(weak))) typeof(self) _weakSelf=self; (*(NSString **)((char *)self + OBJC_IVAR_$_KDBlockTest$_person2))=(NSString *)&__NSConstantStringImpl__var_folders_5l_2l25j3tn0wl_3zy1hpsq1rhc0000gp_T_KDBlockTest_d10f18_mi_1; NSLog((NSString *)&__NSConstantStringImpl__var_folders_5l_2l25j3tn0wl_3zy1hpsq1rhc0000gp_T_KDBlockTest_d10f18_mi_2,(*(NSString **)((char *)self + OBJC_IVAR_$_KDBlockTest$_person2)),(*(NSString **)((char *)self + OBJC_IVAR_$_KDBlockTest$_person2)),&self); (*(ActionTest *)((char *)self + OBJC_IVAR_$_KDBlockTest$_action))= (void (*)())&__KDBlockTest__test3_block_impl_0((void *)__KDBlockTest__test3_block_func_0, &__KDBlockTest__test3_block_desc_0_DATA, _weakSelf, 570425344); ((void (*)(__block_impl *))((__block_impl *)(*(ActionTest *)((char *)self + OBJC_IVAR_$_KDBlockTest$_action)))->FuncPtr)((__block_impl *)(*(ActionTest *)((char *)self + OBJC_IVAR_$_KDBlockTest$_action)));}
block初始化的時候把 _weakSelf的地址傳入,block內部對_weakSelf進行弱引用。在執行block的時候
static void __KDBlockTest__test3_block_func_0(struct __KDBlockTest__test3_block_impl_0 *__cself) { __weak typeof (self) _weakSelf = __cself->_weakSelf; // bound by copy NSLog((NSString *)&__NSConstantStringImpl__var_folders_5l_2l25j3tn0wl_3zy1hpsq1rhc0000gp_T_KDBlockTest_d10f18_mi_3,((NSString *(*)(id, SEL))(void *)objc_msgSend)((id)_weakSelf, sel_registerName("person2")),((NSString *(*)(id, SEL))(void *)objc_msgSend)((id)_weakSelf, sel_registerName("person2")),&_weakSelf); }
通過取得block結構體的 弱引用對象self 成員來訪問相對應的方法 person2 (給對象發消息)。
上面例子,我們稍作修改:
-(void )test3{ _person2=@"person2"; __weak typeof(_person2) _weakPerson2=_person2; NSLog(@"init:%@,%p,%p",_person2,_person2,&_person2); NSLog(@"init weak:%@,%p,%p",_weakPerson2,_weakPerson2,&_weakPerson2); _action= ^(void) { //block內賦值
//_weakPerson2=@"person4";//error ,不能修改
NSLog(@"excuteing _person2:%@,%p,%p",_weakPerson2,_weakPerson2,&_weakPerson2); }; _person2=@"person22"; NSLog(@"before:%@,%p,%p",_person2,_person2,&_person2); NSLog(@"before weak:%@,%p,%p",_weakPerson2,_weakPerson2,&_weakPerson2); _action(); NSLog(@"after:%@,%p,%p",_person2,_person2,&_person2);}
輸出日志:
2014-07-29 15:29:33.472 Test[2719:60b] init:person2,0x5397c,0x16566db82014-07-29 15:29:33.475 Test[2719:60b] init weak:person2,0x5397c,0x27db693c2014-07-29 15:29:33.476 Test[2719:60b] before:person22,0x539bc,0x16566db82014-07-29 15:29:33.477 Test[2719:60b] before weak:person2,0x5397c,0x27db693c2014-07-29 15:29:33.479 Test[2719:60b] excuteing _person2:person2,0x5397c,0x165b5be42014-07-29 15:29:33.480 Test[2719:60b] after:person22,0x539bc,0x16566db82014-07-29 15:29:33.481 Test[2719:60b] KDBlockTest dealloc
從日志可以看出:
我們來看下轉換后的代碼:其實和不加__block的局部變量差不多,無非多了一個弱引用,不會對引用計數有影響。
struct __KDBlockTest__test3_block_impl_0 { struct __block_impl impl; struct __KDBlockTest__test3_block_desc_0* Desc; __weak typeof (self->_person2) _weakPerson2; __KDBlockTest__test3_block_impl_0(void *fp, struct __KDBlockTest__test3_block_desc_0 *desc, __weak typeof (self->_person2) __weakPerson2, int flags=0) : _weakPerson2(__weakPerson2) { impl.isa = &_NSConcreteStackBlock; impl.Flags = flags; impl.FuncPtr = fp; Desc = desc; }};
static void _I_KDBlockTest_test3(KDBlockTest * self, SEL _cmd) { (*(NSString **)((char *)self + OBJC_IVAR_$_KDBlockTest$_person2))=(NSString *)&__NSConstantStringImpl__var_folders_5l_2l25j3tn0wl_3zy1hpsq1rhc0000gp_T_KDBlockTest_f32cef_mi_1;
//聲明_weak 變量 __attribute__((objc_gc(weak))) typeof(_person2) _weakPerson2=(*(NSString **)((char *)self + OBJC_IVAR_$_KDBlockTest$_person2)); NSLog((NSString *)&__NSConstantStringImpl__var_folders_5l_2l25j3tn0wl_3zy1hpsq1rhc0000gp_T_KDBlockTest_f32cef_mi_2,(*(NSString **)((char *)self + OBJC_IVAR_$_KDBlockTest$_person2)),(*(NSString **)((char *)self + OBJC_IVAR_$_KDBlockTest$_person2)),&(*(NSString **)((char *)self + OBJC_IVAR_$_KDBlockTest$_person2))); NSLog((NSString *)&__NSConstantStringImpl__var_folders_5l_2l25j3tn0wl_3zy1hpsq1rhc0000gp_T_KDBlockTest_f32cef_mi_3,_weakPerson2,_weakPerson2,&_weakPerson2);
//初始化block (*(ActionTest *)((char *)self + OBJC_IVAR_$_KDBlockTest$_action))= (void (*)())&__KDBlockTest__test3_block_impl_0((void *)__KDBlockTest__test3_block_func_0, &__KDBlockTest__test3_block_desc_0_DATA, _weakPerson2, 570425344); (*(NSString **)((char *)self + OBJC_IVAR_$_KDBlockTest$_person2))=(NSString *)&__NSConstantStringImpl__var_folders_5l_2l25j3tn0wl_3zy1hpsq1rhc0000gp_T_KDBlockTest_f32cef_mi_5; NSLog((NSString *)&__NSConstantStringImpl__var_folders_5l_2l25j3tn0wl_3zy1hpsq1rhc0000gp_T_KDBlockTest_f32cef_mi_6,(*(NSString **)((char *)self + OBJC_IVAR_$_KDBlockTest$_person2)),(*(NSString **)((char *)self + OBJC_IVAR_$_KDBlockTest$_person2)),&(*(NSString **)((char *)self + OBJC_IVAR_$_KDBlockTest$_person2))); NSLog((NSString *)&__NSConstantStringImpl__var_folders_5l_2l25j3tn0wl_3zy1hpsq1rhc0000gp_T_KDBlockTest_f32cef_mi_7,_weakPerson2,_weakPerson2,&_weakPerson2); ((void (*)(__block_impl *))((__block_impl *)(*(ActionTest *)((char *)self + OBJC_IVAR_$_KDBlockTest$_action)))->FuncPtr)((__block_impl *)(*(ActionTest *)((char *)self + OBJC_IVAR_$_KDBlockTest$_action)));}
在聲明 _weak變量的時候,生成了一個 弱引用的指針 指向 self的person2變量。在block初始化的時候,把弱引用指針指向的內容地址 傳遞給了block成員
__weak typeof (self->_person2) _weakPerson2;
block結構體內部通過 成員 _weakPerson2 直接弱引用了外部變量 person2的內容地址。這時候如果把person2指針指向另外一塊內存地址,那么肯定是同步不到block內部的,這個和 局部變量 大同小異。
總結:
對于引用類型的修改,如果block初始化后,修改指針指向,即指向另外一塊內存,這樣也是無法同步到block內部
對于引用類型的修改,如果block初始化后,對指針指向的內存進行修改,即NSMutableArray add 、remove操作,這樣是可以用同步到block內部。
新聞熱點
疑難解答