前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >RAC(ReactiveCocoa)介绍(七)——信号销毁

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

作者头像
我只不过是出来写写代码
发布2019-04-02 14:59:15
2.1K0
发布2019-04-02 14:59:15
举报
文章被收录于专栏:我只不过是出来写写iOS

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

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

代码语言:javascript
复制
+ (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。

代码语言:javascript
复制
- (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以及释放操作。

代码语言:javascript
复制
- (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信号销毁过程中内部方法无法对其进行销毁操作,最终会导致内存泄漏问题

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

本文参与 腾讯云自媒体同步曝光计划,分享自作者个人站点/博客。
原始发表:2018.05.28 ,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档