「OC」__bridge关键字的使用

「OC」__bridge关键字的使用

前言

在学习小白书关于引用计数之中Foundation框架和Core Foundation框架相互转化后的相关内容,学习到了__bridge这个关键字,因此对其进行学习

引入

cocoa应用中,我们离不开Foundation同时又经常用到Core FoundationFoundationCore Foundation的一层包装,其底层数据结构是一样的,我们可以使用__bridgeCF对象和NS对象之间相互转化,但是不移交所有权

以下是书中给出的例子

NSString *str = @"abc";
CFStringRef cfstring = (__bridge CFStringRef)str;
NSLog(@"1 -- %@",cfstring);
NSString *s = (__bridge NSString *)cfstring;
NSLog(@"2 -- %@",s);
    
执行结果
1 -- abc
2 -- abc

因为Core Foundation对象与Objective-C对象没有区别,所以在ARC无效时,只用简单的C语言的转换也能实现互换。另外这种转换不需要使用额外的CPU 资源,因此也被称 为 “ 免费桥 " ( Toll-FreeBridge)。

CFTypeRef CBridgingRetain(id X) {
    return (__bridge_retained CFTypeRef)X;
}

id CBridgingRelease(CFTypeRef X) {
    return (__bridge_transfer id)X;
}

以上就是使用免费桥进行转换的程序,我们借机来深入了解一下__bridge_retained__bridge_transfer

__bridge_retained:

  • 这条语句 (__bridge_retained CFTypeRef)X 告诉编译器把一个 ARC 管理的 Objective-C 对象转换为 Core Foundation 对象,同时转移所有权(即对象的引用计数不会减少,调用者需要负责释放)。
  • 这通常用于将对象传递给需要 Core Foundation 类型的 API,并且明确表示调用者接管了对象的释放责任。

__bridge_retainedCFBridgingRetainOC指针转为CF指针并且移交所有权,需要CF通过CFRelease来负责对象的释放,以书中的例子:

CFMutableArrayRef cfObject = NULL;
{
  id obj = [[NSMutableArray alloc] init];
  // 此时,变量 obj 持有生成对象的强引用,引用计数为1

  // CFBridgingRetain 会把 ARC 管理的对象转换成 Core Foundation 对象,并且转移所有权(即对对象执行 CFRetain 操作),引用计数加1
  cfObject = CFBridgingRetain(obj); // 或写作: cfObject = (__bridge_retained CFMutableArrayRef)obj;

  // 现在,对象有两个所有者:obj 的强引用和 cfObject 的所有权引用,所以引用计数为2
  CFShow(cfObject);
  printf("retain count = %d\n", (int)CFGetRetainCount(cfObject));

 
}

 // 当 obj 超出其作用域后,ARC 自动释放 obj 持有的引用,对象引用计数减1,仍为1
printf("retain count after the scope = %d\n", (int)CFGetRetainCount(cfObject));

// 通过 CFRelease 手动释放 cfObject 的引用,对象引用计数减为0,对象被销毁
CFRelease(cfObject);

使用__bridge关键词直接代替

CFMutableArrayRef cfObject = NULL;

{
  id obj = [[NSMutableArray alloc] init];
  // 此时,变量 obj 持有生成对象的强引用,引用计数为1

  // 使用 __bridge 进行转换,仅仅是类型转换,不改变引用计数,也不转移所有权
  cfObject = (__bridge CFMutableArrayRef)obj;

  // 此时,对象只有一个所有者,即 obj,引用计数依然为1
  CFShow(cfObject);
  printf("retain count = %d\n", (int)CFGetRetainCount(cfObject));
}

// 当 obj 超出作用域后,ARC 会释放 obj 持有的对象,对象引用计数降为0,对象被释放
// 但 cfObject 仍保存着原始指针,此时 cfObject 成为悬垂指针
printf("retain count after the scope = %d\n", (int)CFGetRetainCount(cfObject));

// 如果继续使用 cfObject 或调用 CFRelease,可能会出现错误,因为对象已经被释放
// CFRelease(cfObject); // 切勿在 __bridge 转换后调用 CFRelease

__bridge_transfer:

  • (__bridge_transfer id)X 则是将一个 Core Foundation 对象转换为 ARC 管理的 Objective-C 对象,同时将所有权转交给 ARC。
  • 这样 ARC 会负责对象的释放,无需手动调用 CFRelease。

__bridge_retained相反,是将CF指针转为OC指针并移交所有权

{
  CFMutableArrayRef cfObject = CFArrayCreateMutable(kCFAllocatorDefault, 0, NULL);
  printf("retain count = %d\n", CFGetRetainCount(cfObject)); // 创建后引用计数为1

  // 通过 CFBridgingRelease 将 CF 对象转为 ARC 管理的 Objective-C 对象
  id obj = CFBridgingRelease(cfObject);

  // 此时,CF 对象的所有权已转移给 ARC,ARC 会对 obj 进行一次强引用(retain)管理
  printf("retain count after the cast = %d\n", CFGetRetainCount(cfObject)); // 引用计数仍为1

  NSLog(@"class = %@", obj);
}
// 当变量 obj 超出作用域,其强引用失效,对象会被自动释放(CFRelease 在内部调用)

使用__bridge关键词直接代替

{
   CFMutableArrayRef cfObject = CFArrayCreateMutable(kCFAllocatorDefault, 0, NULL);
  printf("retain count = %d\n", CFGetRetainCount(cfObject)); // 创建后引用计数为1

  // 使用 __bridge 转换,将 CF 对象赋值给 ARC 管理的 Objective-C 对象
  id obj = (__bridge id)cfObject;

  // 由于赋值给强引用的变量,ARC 会额外对对象进行一次 retain,引用计数变为2
  printf("retain count after the cast = %d\n", CFGetRetainCount(cfObject)); // 引用计数为2

  NSLog(@"class = %@", obj);
}

// 当变量 obj 超出作用域,ARC 会自动释放其持有的引用,但 CF 对象仍有一份原始引用
// 这会导致对象未被销毁,发生内存泄漏!

慎用

尽管以上方法,可以实现C语言框架和OC框架的互换,但是我们这些方法尽可能还是少用,因为对于CF框架的内容来说,很容易忘记作用域相关的问题

void *p;
{
    Son *son = [[Son alloc] init];
    // 将OC指针转为CF指针赋值给p
    p = (__bridge void *)son;
    // 出了作用域OC对象就要释放了
}
// 这里访问空对象会出问题
NSLog(@"%@",p);

我们需要使用的是__bridge_retained

    void *p;
    {
        Son *son = [[Son alloc] init];
        // 将OC指针转为CF指针赋值给p,并将所有权交给CF管理
        p = (__bridge_retained void *)son;
        // 出了作用域并不会影响p对象
    }
    // 这里仍然可以正常访问
    NSLog(@"%@",p);
    
执行结果
<Son: 0x281210150>

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值