美文网首页函数响应式编程rac我爱编程
RAC(ReactiveCocoa)介绍(七)——信号销毁

RAC(ReactiveCocoa)介绍(七)——信号销毁

作者: 我只不过是出来写写代码 | 来源:发表于2018-05-28 18:54 被阅读112次

    这一篇讲主要针对RACSignal信号销毁进行探究

    在RACSignal信号发送命令执行之后,本着谁创建谁销毁的原则,最后一步必须要进行销毁操作。而销毁操作的执行则由RACDisposable类来完成。
    RACDisposable类在RAC中作为一个父类,由三种子类继承自它。RACCompoundDisposableRACSerialDisposable以及RACKVOTrampoline
    首先来看下RACDisposable类在执行销毁disposableWithBlock方法时的操作。

    + (instancetype)disposableWithBlock:(void (^)(void))block {
        return [[self alloc] initWithBlock:block];
    }
    
    - (instancetype)initWithBlock:(void (^)(void))block {
        NSCParameterAssert(block != nil);
    
        self = [super init];
    
        _disposeBlock = (void *)CFBridgingRetain([block copy]); 
        OSMemoryBarrier();
    
        return self;
    }
    

    可以看到,将销毁信号中的代码块进行了保存操作,赋值给了_disposeBlock
    而_dispostBlock的声明方式为:void * volatile _disposeBlock
    volatile的作用是,每次取得数值的方式都是直接从内存中读取

    (void *)CFBridgingRetain( )代码是Objective-C与C语言进行桥接的方法,使用__bridge_retained方法自行管理内存。
    桥接方法有三种:__bridge、__bridge_retained、__bridge_transfer
    __bridge是将Objective-C转换成C语言,OC对象交给CF对象同时其所有权不变化
    __bridge_retained将Objective-C转换成C语言,OC对象将所有权交给CF对象,但会解除自动管理内存机制ARC的所有权,意味着要自行进行内存管理。当管理对象需要释放时,必须要执行CFBridgingRelease方法来手动释放。

    __bridge_retained内部方法
    CFBridgingRelease方法的内部实现,是为第三种方法__bridge_transfer的执行,将CF对象的所有权交给OC对象,给予管理对象自动管理内存机制ARC的所有权。
    __bridge_transfer内部方法
    此处为什么将该block转换成C函数?将Objective-C对象转换成C函数的,而C函数可以直接拿到相应的函数指针,拿到函数指针之后就可以指向任意类型,即重定向指针。此处重定向指针之后,会在dispose方法进行指针处理。

    OSMemoryBarrier();被称为内存屏障,为了保证相应的对象按顺序依次执行。
    类似的,在dispose方法中使用到了OSAtomicCompareAndSwapPtrBarrier( )方法

    OSAtomicCompareAndSwapPtrBarrier( )方法内部实现
    对比第一个oldValue与 & value是否相等,若相等则返回BOOL值YES,并把第二个newValue赋值给 & value。
    - (void)dispose {
        void (^disposeBlock)(void) = NULL;
    
        while (YES) {
            void *blockPtr = _disposeBlock;
            if (OSAtomicCompareAndSwapPtrBarrier(blockPtr, NULL, &_disposeBlock)) {
                if (blockPtr != (__bridge void *)self) {
                    disposeBlock = CFBridgingRelease(blockPtr);
                }
    
                break;
            }
        }
    
        if (disposeBlock != nil) disposeBlock();
    }
    

    OSAtomicCompareAndSwapPtrBarrier( )方法将_disposeBlock赋值给的blockPtr_disposeBlock进行对比,如果相等就将_disposeBlock赋值为NULL,同时将blockPtr释放销毁,此处写法作用是将_disposeBlock置为NULL的操作,同时进入下一步判断blockPtr是否与self相同,若不同则将blockPtr的OC对象赋值给disposeBlock
    那么,判断局部变量disposeBlock不为nil,意味着还存在销毁者,还不需要执行销毁操作,则继续执行disposeBlock( ),即销毁信号block中的代码块。

    在dispose方法中,当OSAtomicCompareAndSwapPtrBarrier( )方法判断_disposeBlockblockPtr不相同时,_disposeBlock无法赋值为NULL,就无法执行下一步操作。那么就在dealloc方法中执行置为NULL以及释放操作。

    - (void)dealloc {
        if (_disposeBlock == NULL || _disposeBlock == (__bridge void *)self) return;
    
        CFRelease(_disposeBlock);
        _disposeBlock = NULL;
    }
    

    释放CF对象_disposeBlock,同时将其置为NULL。
    销毁信号的整个操作,并不需要外部进行管理,全部由内部执行操作完成,让开发更专注于业务逻辑。
    销毁过程中,是通过手动+自动释放来共同进行内存释放管理。

    在发送信号的三种执行方法实现中,sendNext方法没有实现[self.disposable dispose],而sendErrorsendCompleted方法却实现了。

    发送信号方法实现区别
    在dispose方法中,会有while(YES)的死循环,用于不断寻找销毁对象,直到找到为止,并将其销毁置空掉。
    而sendNext方法并不意味着创建信号的代码块已执行结束完成,当创建信号的代码块中所有代码都已执行完成,但未实现[self.disposable dispose]方法,那么就会去执行dealloc方法。

    扩展一下:
    在控制器创建销毁信号时,若创建了一个RACDisposable类的成员变量,将其放入销毁信号return中。因为持有该销毁信号对象的是当前类,在RAC信号销毁过程中内部方法无法对其进行销毁操作,最终会导致内存泄漏问题


    销毁信号使用成员变量而非临时变量,导致的内存泄漏

    相关文章

      网友评论

        本文标题:RAC(ReactiveCocoa)介绍(七)——信号销毁

        本文链接:https://www.haomeiwen.com/subject/wfegjftx.html