iOS

block里面怎么避免引用对象

__bridge

Posted by Jincc on January 23, 2018

看到这个标题有人会问这不就是__weak__strong的问题吗?抛开这两个关键字不说,我的疑惑来之下面这几行代码:

// Purposely not retaining 'object', since we want to tear down the binding
	// when it deallocates normally.
	//1.
	__block void * volatile objectPtr = (__bridge void *)object;

	RACDisposable *subscriptionDisposable = [self subscribeNext:^(id x) {
		// Possibly spec, possibly compiler bug, but this __bridge cast does not
		// result in a retain here, effectively an invisible __unsafe_unretained
		// qualifier. Using objc_precise_lifetime gives the __strong reference
		// desired. The explicit use of __strong is strictly defensive.
		
		//2.
		__strong NSObject *object __attribute__((objc_precise_lifetime)) = (__bridge __strong id)objectPtr;
		[object setValue:x ?: nilValue forKeyPath:keyPath];
	} error:^(NSError *error) {

这里为什么作者要丢一个指针进去,而不是对象本身?带着这个疑惑我们接着往下看

下面我们首先创建了一个局部变量,注意这里是局部变量,我们想要做的事情是不让这个gblock持有它.

    Aclass * a = [Aclass new];
    self.gBlock = ^(){
        [a class];
    };
    //    self.gBlock = nil;

这段代码会在block里面强引用a这个对象,只要我们不去手动释放这个block的话,a这个对象会永远持有. 不能添加__weak,因为添加之后初始化完a就会被释放.

那么怎么不让block强引用这个对象呢?

rac的处理就是先将对象转换成指针,然后在block里面去使用这个指针.这样gblock就不会强引用a这个对象,出了这块作用于a就会被释放.

注: 这里并不提倡大家这么去用,因为这非常容易造成野指针,这篇文章的目的只是好奇为什么这么去写.

先来看下这几个关键字:

__bridge

我们在开发程序的时候,经常涉及到CF框架,可能就需要OC对象和CF对象之间的相互转换了.

如果想单纯地赋值,可以使用 __bridge,它不改变对象的持有状况。

    NSString *aString = [NSString stringWithFormat:@"Jincc"];
    //我们现在把oc对象转换成指针
    //如果想单纯地赋值,可以使用 __bridge,它不改变对象的持有状况。
    void *ptr = (__bridge void *)aString;
    //在转换成oc对象
    id newObj = (__bridge NSString*)ptr;
    NSLog(@"%p---%p",newObj,aString);//0xa000063636e694a5---0xa000063636e694a5

__bridge_retained /CFBridgingRetain

常用在将OC对象转换成CF对象时,将OC对象的所有权交给CF对象来管理

    NSString *aString = [NSString stringWithFormat:@"Jincc"];
    // aString的生命周期管理责任交给CF
    CFStringRef cString = (__bridge_retained CFStringRef)(aString);
    // 原对象的内存并不会因此而销毁
    aString = nil;
    NSLog(@"%@----%@",cString,aString); //Jincc----(null)
    // 正确的释放方法 :
    CFRelease(cString);

__bridge_transfer

常用在CF对象转换成OC对象时,将CF对象的所有权交给OC对象,此时ARC就能自动管理该内存

 CFStringRef cString = CFStringCreateWithCString(NULL, "Jincc", kCFStringEncodingASCII);
    NSString *aString = (__bridge_transfer NSString *)cString;
    //CFRelease(cString); //not need
    NSLog(@"%@",aString);

回顾上面的问题

    Aclass * a = [Aclass new];
    __block void * volatile objectPtr = (__bridge void *)a;
    NSLog(@"ptr:%@,a:%@",objectPtr,a); //ptr:<Aclass: 0x60800000f040>,a:<Aclass: 0x60800000f040>
    self.gBlock = ^(){
        // Possibly spec, possibly compiler bug, but this __bridge cast does not
        // result in a retain here, effectively an invisible __unsafe_unretained
        // qualifier. Using objc_precise_lifetime gives the __strong reference
        // desired. The explicit use of __strong is strictly defensive.
        __strong Aclass *object __attribute__((objc_precise_lifetime)) = (__bridge __strong id)objectPtr;
        NSLog(@"---%@",[object class]);

    };
    self.gBlock();

这段代码实际上__bridge起了作用,我们首先将a这个对象转换成了objectPtr指针,然后这个指针被block引用了.结果就是a并没有被持有?what?

why?


 __block Aclass * a = [Aclass new];
    gBlock = ^(){
//        // Possibly spec, possibly compiler bug, but this __bridge cast does not
//        // result in a retain here, effectively an invisible __unsafe_unretained
//        // qualifier. Using objc_precise_lifetime gives the __strong reference
//        // desired. The explicit use of __strong is strictly defensive.
//        __strong Aclass *object __attribute__((objc_precise_lifetime)) = (__bridge __strong id)objectPtr;
//        NSLog(@"---%@",[object class]);
        [a class];
    };
    gBlock();
    

实际上block里面会持有__Block_byref_a_0这个结构,这个结构里面有两个方法来专门管理引用对象的生命周期的,当对象需要被拷贝的时候回调用__Block_byref_id_object_copy_131,当对象销毁的时候回执行__Block_byref_id_object_dispose_131.这两个方法就会操作对象的引用计数来保持对象的持有.

// 静态函数 __Block_byref_id_object_copy_131
static void __Block_byref_id_object_copy_131(void *dst, void *src){
    _Block_object_assign((char*)dst + 40, *(void * *) ((char*)src + 40), 131);
}

// 静态函数 __Block_byref_id_object_dispose_131
static void __Block_byref_id_object_dispose_131(void *src){
    _Block_object_dispose(*(void * *) ((char*)src + 40), 131);
}

// Runtime support functions used by compiler when generating copy/dispose helpers

// Values for _Block_object_assign() and _Block_object_dispose() parameters
enum {
    // see function implementation for a more complete description of these fields and combinations
    BLOCK_FIELD_IS_OBJECT   =  3,  // id, NSObject, __attribute__((NSObject)), block, ...
    BLOCK_FIELD_IS_BLOCK    =  7,  // a block variable
    BLOCK_FIELD_IS_BYREF    =  8,  // the on stack structure holding the __block variable
    BLOCK_FIELD_IS_WEAK     = 16,  // declared __weak, only used in byref copy helpers
    BLOCK_BYREF_CALLER      = 128, // called from __block (byref) copy/dispose support routines.
};

_Block_object_assign就是根据最后一个标志参数选择执行_Block_retain,_Block_assign。此方法是被Block定义(就是NSBlock的一种),在构造私有调用栈时调用的。 另一个__Block_byref_object_dispose_就是__block在析构里调用的。

当我们将a的指针扔进block:

__Block_byref_objectPtr_1没有这两个方法来控制objectPtr的周期.所以我们上面的结论得出了解释.

资源:

main.cpp

main_1.cpp