【问题标题】:Is a __bridge_transfer valid on a NULL object__bridge_transfer 在 NULL 对象上是否有效
【发布时间】:2014-07-12 00:05:30
【问题描述】:

假设一个方法通过指针返回CFErrorRef。这个返回的错误可能是NULL。那么仍然执行__bridge_transfer 是否安全,或者我应该检查NULL

例如

CFErrorRef cfError;
ABAddressBookRef addressBookRef = ABAddressBookCreateWithOptions(NULL, &cfError);

NSError *error = (__bridge_transfer NSError *)cfError;

我在文档中没有看到任何提及这一点,CFRelease 文档特别指出 This value must not be NULL. https://developer.apple.com/library/mac/documentation/CoreFoundation/Reference/CFTypeRef/Reference/reference.html#//apple_ref/c/func/CFRelease

【问题讨论】:

  • NULL 不是一个对象,它是一个指针值。 NSNull 是一个 iOS 对象,可以用来表示 NULL/nil 值(就像 NSNumber 可以表示一个 int 一样)。
  • 是的,但对象引用 cfErrorNULL。所以我的问题成立。

标签: ios objective-c core-foundation toll-free-bridging


【解决方案1】:

您不需要检查 NULL。

ARC 是一种严格的编译时机制。当您使用__bridge_transfer 时,您只是将变量的内存管理职责转移给编译器。 cfError 在运行时是否恰好为 NULL 与编译器完全无关。

在您的情况下,ARC 将为 error 插入一个版本,但如果 error 恰好为 nil,则它是一个简单的无操作。

【讨论】:

  • 你不应该触摸 cfError,除非ABAddressBookCreateWithOptions返回nil/NULL。 Cocoa 不保证以这种方式返回的错误是nil 在这种情况下有效。自己阅读文档:错误时,包含错误信息。 成功时包含什么? 没有记录。
  • 好主意,这当然是更安全、更合适的做事方式。但是,方法签名确实保证传入的指针指向CFError。因此,从语义上讲,它应该向调用者返回CFErrorNULL。虽然这肯定是一场冒险的游戏;)
【解决方案2】:

如果函数的返回值为 NULL,则错误将为非 NULL。 这种 CF 函数的模式是将错误检查包装在 if 语句中。 if (addressBookRef == NULL) { /* your error handling here */}

你不应该尝试桥接任何东西,除非它是非 NULL。对象所有权或更准确地保留计数和减少它的责任,对于 NULL 或 nil 没有意义。这将是一种反模式。 充其量是一个空操作。 使用 Objective-C 向 nil 发送消息很好,包括保留和释放。 将 NULL 值传递给 CFRelease() 或 CGRetain() 是不行的

【讨论】:

  • “不检查直接返回值就不要碰错误”确实是这个特定代码的正确答案,即使它没有直接回答更广泛的问题。
  • 虽然这是此特定代码的正确解决方案,但它仍然无法回答问题。
  • 我不在乎选票。我这样做只是为了帮助别人,锻炼我的大脑。
【解决方案3】:

问题的直接答案是肯定的,你可以在NULL上使用__bridge_transfer。但这不是正确的问题。

阅读ABAddressBookCreateWithOptions 上的文档。尤其是the documentationerror

出错时,包含错误信息。请参阅“通讯录错误”。

这很重要。

  1. error 在成功的情况下的价值没有记录。
  2. errornil/NULL/0曾经)没有记录。

这不是学术性的。一些 API 历来将错误设置为无效值。想象一下调用将CFError 设置为-1。这是“有效的”,因为非NULL 回复意味着您不应该解释错误,但是将-1 转换为NSError 可能会崩溃。

这意味着您不得触摸cfError,除非ABAddressBookCreateWithOptions 返回NULL 指示错误。

CFErrorRef cfError;
NSError *error;
ABAddressBookRef addressBookRef = ABAddressBookCreateWithOptions(NULL, &cfError); 
if (addressBookRef == NULL) {
    error = (__bridge_transfer NSError *)cfError;
}

你没有问这个,但这里的另一个问题是,如果编译器识别出某些东西是 0 等价的,那么甚至不需要桥接。例如,这段代码将静默编译(假设_thing1_thing2 是实例变量):

- (id)bar {
    if (_thing1) return NO;
    if (_thing2) return 0;
    return NULL;
}

这是草率的代码,我应该故意这样做,但是知道它可以干净地构建......这是一件好事。我遇到了一个由这样的东西引起的错误:

- (NSNumber *)someCalculationWithError:(NSError *)error {
   return 0; // meant to return @(0)
}

【讨论】:

  • ABAddressBookRequestAccessWithCompletion的情况呢?完成处理程序具有参数(bool granted, CFErrorRef error),并且文档从不明确说明CFErrorRef 何时到达。
  • 我也很好奇您是否可以提供一些“历史上将错误设置为无效值”的 API 示例。从我对其他 C 语言的探索中,我毫不怀疑它,但我不记得曾经在 Objective-C 中遇到过这个问题。 (这也不是一个好的借口,真的只是好奇。)
  • 不幸的是,我不确定完成处理程序的规则是什么。我想他们被记录在某个地方。我基于 API 的猜测是,如果授予不是 YES,则错误是有效的,但这只是一个猜测。不幸的是,我也不记得是哪个 API 将错误设置为无效的。当时我是 Cocoa 的新手。我只记得回答说不能保证错误不会被设置为无效的工程师,但它应该并且报告它的人应该为它提交一个雷达。那是苹果运行邮件列表的日子。
【解决方案4】:

与 NSObjects 不同,向 NULL CF 对象发送消息是不行的。我不知道具体的桥接转换,但我猜不,使用 __bridge_transfer 将 CF 对象转换为 NSObject 是不行的。

为什么不试试看呢?将其转换为实例方法的本地范围内的变量。这样,一旦方法超出范围,系统就应该尝试释放对象。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2015-07-21
    • 2013-11-09
    • 1970-01-01
    • 2022-11-20
    • 2018-11-15
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多