zoukankan      html  css  js  c++  java
  • 格而知之12:我所理解的内存管理(3)

    30、有时候你可能需要用到一些Core Foundation对象(比如CFArrayRef或者CFMutableDictionaryRef),对于这些对象,编译器是不会自动管理它们的生命周期的,你需要使用CFRetain或CFRelease之类的方法来管理它们的持有情况(ownership)。

    如果要进行Core Foundation对象和Objective-C对象的相互转换,就可以使用Toll-Free Bridging。

    而由于ARC已不能直接使用retain、release等方法,那么在转换的时候就需要将CF指针的持有情况告知OC指针,同理OC指针在转换成CF指针时也要告知其持有情况。

    31、Toll-Free Bridging可以使用修饰符来进行转换,有3种转换方法:

    (1)、__bridge 

    用于两个指针间的直接转换,不考虑持有情况;

    (2)、__bridge_retained

    用于OC指针转换成CF指针,转换之后CF指针也会持有对象。即是,转换后被赋值的指针也会持有对象

    使用CFBridgingRetain函数也有等同效果;

    (3)、__bridge_transfer

    用于CF指针(官方文档说的是“非OC指针”)转换成OC指针,转换之后CF指针不再持有对象。即是,转换后赋值指针不再持有对象

    使用CFBridgingRelease函数也有等同效果。

    以下用4个例子来演示这3个修饰符:

    32、__bridge_retained:

    创建一个OC指针,通过__bridge_retained将它转换为CF指针,同时打印出retainCount:

    分析一下这段代码执行过程中的持有情况:

    可以证明,在使用__bridge_retained修饰符转换后CF指针也会持有对象。

    33、__bridge:

    如果仅仅使用__bridge做直接转换的话,会有什么问题呢?将32代码中的转换修改为使用__bridge,如下:

    可以发现,在这种情况下会导致悬挂指针。所以仅仅使用__bridge做直接转换的话有时候是很危险的。

    34、__bridge_transfer:

    创建一个CF指针,通过__bridge_transfer将它转换为OC指针,同时打印出retainCount:

    分析一下这段代码执行过程中的持有情况:

    可以证明,在使用__bridge_transfer修饰符转换后CF指针不再持有对象。

    35、__bridge:

    同样的,试一试仅仅使用__bridge来做直接转换,看看会发生什么问题。将34代码中的转换修改为使用__bridge,并嵌套在一层花括号内限制变量的作用域,如下:

    可以发现,在这种情况下会导致内存泄漏。所以在这种情况下仅仅使用__bridge做直接转换也是很危险的。

    36、Toll-Free Bridging除了可以做OC指针和CF指针之间的转换,还可以做其他转换,比如上文29(4)提到的id变量和void*变量的相互转换。

    虽然在ARC模式下,不允许id变量和void*变量进行直接转换,但是可以使用Toll-Free Bridging来完成这个转换。

    37、在研究这种转换之前,先要了解一下void*类型的变量对它指向的对象的持有情况是否会有影响:

    (1)、在MRC模式下,由于void*类型并不是NSObject的子类,所以这种类型的变量无法调用retain、retainCount等方法,也即无法影响引用计数。

    所以,在MRC模式下void*类型的变量不会对它指向对象的持有情况造成任何影响;

    (2)、在ARC模式下,修饰符只能用来修饰OC指针和块指针类型,而void*类型的变量作为一种无类型的变量,修饰符对这种它是不起作用的。

    即是说:当定义变量id obj的时候,其实定义的是id __strong obj,而当定义void *obj的时候,定义就仅仅只是void *obj,它的作用类似于使用了__unsafe_unretained修饰符。

    所以,在ARC模式下void*类型的变量也不会对它指向的对象的持有情况造成任何影响。

    38、前文29(4)的代码在ARC模式下可以使用__bridge来处理如下:

    id obj = [[NSObject alloc] init];
    void *p = (__bridge void *)obj;
    id o = (__bridge id)p;

    但是通过上文已知道,仅仅使用__bridge做转换是很危险的,而且void*类型的变量不会持有它指向的对象,这也是很危险的。比如这段代码,总共有3个指针指向了这个NSObject对象,但是它的retainCount却只有2,这样就很容易造成悬挂指针了。

    39、如果前两句代码使用__bridge_retained来处理这种转换,代码如下:

    id obj = [[NSObject alloc] init];
    void *p = (__bridge_retained void *)obj;

    由上文已经知道:使用__bridge_retained转换后,被赋值变量也会持有这个对象。所以这段代码其实是相当于在MRC模式下的这样子转换:

    id obj = [[NSObject alloc] init];
    void *p = obj;
    [(id)p retain]; //强转为id类型后才能调用retain

    这样void*类型的p变量就拥有了“持有”对象的效果。

    40、如果最后一句代码使用__bridge_transfer来处理这种转换,代码如下:

    id o = (__bridge_transfer id)p;

    由上文已经知道:使用__bridge_transfer转换后,赋值变量不会再持有这个对象。所以这段代码其实是相当于在MRC模式下的这样子转换:

    id o = (id)p;
    [o retain];
    [(id)p release];

    这样将p变量赋值给o变量后,p变量便会有“释放”的效果了。

  • 相关阅读:
    窗口实训1
    课后练习----实现窗口的切换
    第五次实训作业继承
    课程总结
    999
    777777777777777777777
    7
    6
    5
    事件处理程序
  • 原文地址:https://www.cnblogs.com/shayneyeorg/p/5783938.html
Copyright © 2011-2022 走看看