iOS下的__block与__weak

来源:互联网 发布:javascript转义字符 编辑:程序博客网 时间:2024/05/18 21:08

先看下结论:
这里写图片描述

所有对象在编译之后,都会转换成一个结构体,包括block,和被__block修饰的任意变量,形成一个拷贝(你可以用clang 将oc转c即可验证)。被__block修饰的变量,转换后的结构体里包含一个_forwarding指针,指向自己的拷贝(从栈到堆的拷贝)。因此:

    __block id  weakS=[MyViewController new];    //假设这里的指针weakS变量本身的地址(不是指向地址)A    int i= [weakS retainCount];    self.block=^{        int j = [weakS retainCount];            NSLog(@"doing blk %@",weakS);    }; //假设这里weakS的本身地址B,     int k= [weakS retainCount];

上述代码里的地址A和地址B是不相等的,你可以自己去打印下。

而__weak,在转换之后却是一条gc指令(自动垃圾回收)。

很多人在arc下用__block来避免循环引用,其实是无效的,依然会导致内存泄露,可以自己新建一个类来在dealloc方法里进行信息打印,看是否 执行。
如果你一定要在arc下用__block,尽量避免循环,如果无法避免下,你依然要用,也有一个办法,就是在block最后一行,将对象设置为nil

 __block id  weakS=[MyViewController new];    self.block=^{    /*某些个其他代码*/              NSLog(@"doing blk %@",weakS);      weakS = nil ;    };

如上,但是以上办法有一个缺陷,便是,如果你的block未被执行,就肯定会有泄露,因为weakS =nil,最终并未被执行。

    修饰语WordA  Tsobj * tobj =self;    NSObject * obj2 =[[NSObject alloc] init];    _obj.aBlk =^{             NSObject *obj =[[NSObject alloc] init];        /*场景1*/        tobj->_name = obj.description;        /*场景2*/        _name = obj.description;        /*场景3*/        tobj.name = obj.description;    };

针对不同修饰语,不同场景,会是怎么样的呢?如表:

修饰符 场景1 场景2 场景3 无修饰符 内存泄露 内存泄露 内存泄露 __weak 非法操作 内存泄露 内存无泄露 __unsafe_unretained 内存无泄露 内存泄露 内存无泄露

为什么场景2在所有场景下都会内存泄露呢?我们看下此时block转化成IMP后的代码,如下:

static void __Tsobj__heh_block_func_0(struct __Tsobj__heh_block_impl_0 *__cself) {    /*请重点看下面这行A*/  Tsobj *self = __cself->self; // bound by copy  Tsobj *tobj = __cself->tobj; // bound by copy  NSObject *obj2 = __cself->obj2; // bound by copy        NSObject *obj =((NSObject *(*)(id, SEL))(void *)objc_msgSend)((id)((NSObject *(*)(id, SEL))(void *)objc_msgSend)((id)objc_getClass("NSObject"), sel_registerName("alloc")), sel_registerName("init"));        /*和重点看下面这行B*/       (*(NSString **)((char *)self + OBJC_IVAR_$_Tsobj$_name)) = ((NSString *(*)(id, SEL))(void *)objc_msgSend)((id)obj, sel_registerName("description"));//        (*(NSString **)((char *)tobj + OBJC_IVAR_$_Tsobj$_name)) = ((NSString *(*)(id, SEL))(void *)objc_msgSend)((id)obj2, sel_registerName("description"));    }

看一下上面重点注释的那两行,block转化IMP后带了一个指针,这个指针有一个 ->self,也就是self对象被retain了,然后在有注释的代码B行里,通过self指针,来访问成员变量。而不是直接通过成员变量的指针。
换句话说,如果block里直接访问成员变量,此时ratain的是self,而不是直接retain成员变量。

我们在看看被__weak修饰的block变量会如何:

static void __Tsobj__heh_block_func_0(struct __Tsobj__heh_block_impl_0 *__cself) { /*看这里*/  Tsobj *__weak tobj = __cself->tobj; // bound by copy        NSObject *obj =((NSObject *(*)(id, SEL))(void *)objc_msgSend)((id)((NSObject *(*)(id, SEL))(void *)objc_msgSend)((id)objc_getClass("NSObject"), sel_registerName("alloc")), sel_registerName("init"));        ((void (*)(id, SEL, NSString *))(void *)objc_msgSend)((id)tobj, sel_registerName("setName:"), ((NSString *(*)(id, SEL))(void *)objc_msgSend)((id)obj, sel_registerName("description")));    }static void _I_Tsobj_heh(Tsobj * self, SEL _cmd) {    (*(Tsobj **)((char *)self + OBJC_IVAR_$_Tsobj$_obj)) =((Tsobj *(*)(id, SEL))(void *)objc_msgSend)((id)((Tsobj *(*)(id, SEL))(void *)objc_msgSend)((id)objc_getClass("Tsobj"), sel_registerName("alloc")), sel_registerName("init"));    /*看这里*/    __attribute__((objc_gc(weak))) Tsobj * tobj =self;    ((void (*)(id, SEL, void (*)()))(void *)objc_msgSend)((id)(*(Tsobj **)((char *)self + OBJC_IVAR_$_Tsobj$_obj)), sel_registerName("setABlk:"), ((void (*)())&__Tsobj__heh_block_impl_0((void *)__Tsobj__heh_block_func_0, &__Tsobj__heh_block_desc_0_DATA, tobj, 570425344)));}

我们可以看到,系统添加了attribute((objc_gc(weak))) 修饰对象。

0 0
原创粉丝点击