什么时候需要 NS_RETURNS_RETAINED ?

2024-01-03

以下面的例子为例:

- (NSString *)pcen NS_RETURNS_RETAINED {
    return (__bridge_transfer NSString *)CFURLCreateStringByAddingPercentEscapes(NULL, (__bridge CFStringRef) self, NULL, (CFStringRef) @"!*'();:@&=+$,/?%#[]", kCFStringEncodingUTF8);
}

放置是否正确NS_RETURNS_RETAINED there?


另一个例子:

+ (UIImage *)resizeImage:(UIImage *)img toSize:(CGSize)size NS_RETURNS_RETAINED {
    UIGraphicsBeginImageContextWithOptions(size, NO, 0.0);
    [img drawInRect:...];
    UIImage *resizedImage = UIGraphicsGetImageFromCurrentImageContext();
    UIGraphicsEndImageContext();
    return resizedImage;
}

这看起来更复杂,因为返回的 UIImage 是“Get”方法的结果。但是,它从中获取的图形上下文是在该方法的范围内创建的,因此也具有以下内容是否正确NS_RETURNS_RETAINED here?


第三个例子:

@property (readonly) NSArray *places;
---
@synthesize places=_places;
---
- (NSArray *)places {
    if (_places)
        return _places;
    return [[NSArray alloc] initWithObjects:@"Unknown", nil];
}

不知道在这里要做什么,因为返回的对象可能是新创建的,也可能不是。


最后一个问题;想必NS_RETURNS_RETAINED如果返回的对象是自动释放方法的结果,则不需要。所以说最后一个例子中的返回被修改为

return [NSArray arrayWithObject:@"Unknown"];

那么最佳实践是什么?


[这个答案部分是对贾斯汀给出的答案的长评论/更正。我认为之前的答案对属性的语义以及 ARC 如何处理返回引用给出了错误的描述。]

答案在于 ARC 分析的工作原理和含义NS_RETURNS_RETAINED.

ARC 分析您的源以确定何时保留、释放或自动释放可保留对象引用。

If然后,您的应用程序的所有源代码都可用,理论上,分析可能能够从“第一原理”确定这些信息 - 从最小的表达式开始并向外工作。

However所有来源均不可用 - 例如有些已经在框架等中编译 - 因此在分析方法调用时,ARC 不会查看方法的源代码,而只会查看其签名 - 它的名称及其参数和返回值的类型。

仅考虑可保留对象类型的返回值,ARC 需要知道所有权是否正在转移 - 在这种情况下,ARC 需要release它在某个时刻 - 或不(例如自动释放参考) - 在这种情况下 ARC 需要retain如果需要所有权的话。

ARC 根据以下信息确定此信息name该方法和任何属性。方法开头为init or new或含有copy根据定义,转让所有权;所有其他方法都没有。属性NS_RETURNS_RETAINED通知 ARC,一个方法,无论其名称如何,都会转移其返回引用的所有权。

这就是故事的一半...另一半是 ARC 如何处理return方法体中的语句。

A return实际上是一种赋值类型,当执行可保留对象引用赋值时,ARC 根据其对当前所有权和引用的了解以及目标的要求来确定该引用是否需要保留、自动释放或保持原样。

For a return毫无疑问,目的地的要求是由方法名称和签名上指定的任何属性决定的。如果签名表明所有权正在转移,那么 ARC 将返回retained引用,否则将返回自动释放 one.

重要的是要了解 ARC 在方法调用的两侧都起作用,它确保返回适当的引用and确定如何处理返回的引用。

有了所有的序言,我们可以看看你的第一个例子。看起来你正在写一个方法NSString,所以我们将添加该详细信息,首先我们将省略该属性:

@interface NSString (AddingPercentEscapes)

- (NSString *) pcen;

@end

@implementation NSString (AddingPercentEscapes)

- (NSString *) pcen
{
   return (__bridge_transfer NSString *)CFURLCreateStringByAddingPercentEscapes(NULL, (__bridge CFStringRef) self, NULL, (CFStringRef) @"!*'();:@&=+$,/?%#[]", kCFStringEncodingUTF8);
}

@end

以及它的一个简单用法:

- (void)applicationDidFinishLaunching:(NSNotification *)aNotification
{
   NSString *test = @"This & than > other";

   NSLog(@"pcen: %@", [test pcen]);
}

编译时pcen method return语句 ARC 查看签名、姓名 (pcen) 并不表示所有权转移,也没有属性,所以 ARC 添加了一个autorelease表达式返回的引用的(__bridge_transfer NSString *) ... kCFStringEncodingUTF8)因为该表达式返回一个引用pcen.

重要的: what表达方式并不重要,重要的是是否pcen拥有它保留的引用 - 特别是__bridge_transfer不确定该方法返回的引用的所有权。

编译时调用pcen in the applicationDidFinishLaunching方法 ARC 再次查看签名,确定当前方法需要所有权并且返回的引用不被拥有,并插入一个retain.

您可以通过在 Xcode 中调用“产品 > 生成输出 > 程序集文件”来验证这一点,在生成的程序集中,您将在代码中看到pcen大致如下:

callq   _CFURLCreateStringByAddingPercentEscapes
movq    %rax, %rdi
callq   _objc_autoreleaseReturnValue
addq    $16, %rsp
popq    %rbp
ret

它显示了 ARC 插入的自动释放,并在程序集中applicationDidFinishLaunching大致如下:

callq   _objc_msgSend
movq    %rax, %rdi
callq   _objc_retainAutoreleasedReturnValue

这是调用pcen随后是 ARC 插入保留。

所以你的例子在没有注释的情况下工作得很好,ARC 做了正确的事情。不过,它也可以与注释配合使用,让我们将接口更改为:

@interface NSString (AddingPercentEscapes)

- (NSString *) pcen NS_RETURNS_RETAINED;

@end

运行(并分析)这个版本,它也可以工作。然而,生成的代码已更改,ARC 确定它应该根据属性的存在转移所有权,因此该程序集return语句变为:

callq   _CFURLCreateStringByAddingPercentEscapes
addq    $16, %rsp
popq    %rbp
ret

ARC 确实not插入自动释放。在调用站点,程序集变为:

callq   _objc_msgSend
movq    -40(%rbp), %rdi         ## 8-byte Reload
movq    %rax, %rsi
movq    %rax, -48(%rbp)         ## 8-byte Spill
movb    $0, %al
callq   _NSLog

这里 ARC 所做的not插入保留。

所以两个版本都是“正确的”,但哪个更好呢?

看起来带有该属性的版本更好,因为 ARC 不需要插入自动释放/保留;但运行时优化了这个序列(因此调用_objc_retainAutoreleasedReturnValue而不是类似的东西_objc_retain)因此成本并不像看起来那么大。

However正确答案是neither...

推荐的解决方案是依赖 Cocoa/ARC 约定并更改方法的名称,例如:

@interface NSString (AddingPercentEscapes)

- (NSString *) newPercentEscapedString;

@end

以及相关的变化。

执行此操作,您将获得与以下相同的代码pcen NS_RETURNS_RETAINEDARC 决定应根据name new....

这个答案已经(太)长了,希望以上内容能帮助您找到其他两个示例的答案!

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

什么时候需要 NS_RETURNS_RETAINED ? 的相关文章

随机推荐

  • if comment.name == user 无法正常工作

    我希望用户能够删除他们写的评论 不幸的是 我无法让 if 语句发挥作用 if comment name user a href delete this comment a endif 所以我可以看到 user 和 comment name
  • Cocoa 脚本:接受并返回 NSData

    为了支持我的可编写脚本的 Mac 应用程序中的二进制数据交换 我希望能够使用 AS ObjC 桥接器以 NSData 形式接收和传递数据 如果可能的话 例如 我喜欢在 AppleScript 中实现以下代码 use framework Fo
  • 直接链接到 SWF 有哪些缺点?

    通常 Flash 和 Flex 应用程序使用以下组合嵌入到 HTML 中 object and embed标签 或更常见的是使用 JavaScript 但是 如果您直接链接到 SWF 文件 它将在浏览器窗口中打开 并且无需查看地址栏 您就无
  • 使用 Serde 反序列化时如何忽略额外的元组项? (“尾随字符”错误)

    Serde 在反序列化为常规结构时会忽略未知的命名字段 当反序列化为元组结构 例如来自异构 JSON 数组 时 如何同样忽略额外的项目 例如 此代码忽略额外的 c 字段就好了 derive Serialize Deserialize Deb
  • 当我尝试发出目标代码时,为什么 LLVM 出现段错误?

    我试图遵循有关编译器实现的 LLVM 教程 但当我尝试发出目标代码时 我的代码出现段错误 这是一个尝试编译函数的最小示例func 为了让事情变得简单 func是一个不执行任何操作的函数 include
  • 页面加载时显示进度轮

    当用户等待时正在进行大量数据库和第 3 方 cURL 查询时 我需要一个进度轮来显示 进度轮应该立即自行显示 还是应该在页面模板 而不是内容 加载后显示它 我是否应该显示进度轮 直到页面的 HTML javascript 加载完成 或者 P
  • 如何转换(旋转)已经存在的 CALayer/动画?

    我已将 CALayer 添加到我的应用程序的 UIView 中 CATransition animation CATransition animation animation setDelegate self animation setDu
  • 用于连续语音识别的 OS X Yosemite (10.10) API

    OSX 现在已经为多个版本内置了连续语音识别功能 现在情况非常好 OSX 10 10 是否有用于此连续语音识别引擎的 API 谷歌搜索告诉我大约 2009 年只有 NSSpeechRecogniser OSX 上的 MS SAPI sdk
  • CSS 文本溢出省略号在 Grid / Flex 中不起作用

    我无法得到text overflow省略号在 CSS 网格中工作 文本被截断 但省略号点不显示 这是我的CSS grid display grid margin auto width 90 grid template columns 2fr
  • Django:按日期过滤日期时间而忽略时间

    我正在尝试过滤Matches 安排在某一天 我不能只这样做 match queryset filter start date 因为它也会按时间过滤 但我可以这样做 match queryset filter start year a dat
  • 在诗歌项目上安装 GDAL

    我在 Poetry 管理的虚拟环境中安装 GDAL 时遇到问题 在 Ubuntu 18 04 发行版上工作 我已经发现的是 这个问题 https github com python poetry poetry issues 845在诗歌 g
  • 如何使用 pymc 制作离散状态马尔可夫模型?

    我试图找出如何正确制作离散状态马尔可夫链模型pymc http pymc devs github io pymc index html 举个例子 查看nbviewer http nbviewer ipython org github shp
  • window.location.href 不更改 URL

    我想更改提交表单后出现的 URL 尽管下面的代码似乎没有执行我希望执行的操作 如何更改地址栏中的 URL CODE
  • 理解列表理解

    我对编程有点陌生 我创建了一个在其初始值设定项中使用列表理解的类 如下 class Collection of word counts this class has one instance variable called counts w
  • 套接字连接()与绑定()

    Both connect and bind 系统调用将套接字文件描述符 关联 到一个地址 通常是 IP 端口组合 他们的原型是这样的 int connect int sockfd const struct sockaddr addr soc
  • 清除 Android 中的 Glide 图像缓存

    我正在使用 glide 库来显示图像 我正在尝试使用以下代码清除滑动缓存 Glide get MainActivity this clearDiskCache Glide get MainActivity this clearMemory
  • 在 Ubuntu Linux 中运行的 CodeIgniter IDE [关闭]

    就目前情况而言 这个问题不太适合我们的问答形式 我们希望答案得到事实 参考资料或专业知识的支持 但这个问题可能会引发辩论 争论 民意调查或扩展讨论 如果您觉得这个问题可以改进并可能重新开放 访问帮助中心 help reopen questi
  • addEventListener 与 onclick

    有什么区别addEventListener and onclick var h document getElementById a h onclick dothing1 h addEventListener click dothing2 上
  • 如何在 ubuntu 18.04.2 上访问 nuget 包管理器控制台

    所以我在我刚买的这台小笔记本电脑上安装了ubuntu 18 04 2 thinkpad x220 我正在尝试将其打造成一台可以在其上编写 ASP NET Core 应用程序的机器 然而 我一生都无法找到如何在 Ubuntu 终端的包管理器控
  • 什么时候需要 NS_RETURNS_RETAINED ?

    以下面的例子为例 NSString pcen NS RETURNS RETAINED return bridge transfer NSString CFURLCreateStringByAddingPercentEscapes NULL