以下面的例子为例:
- (NSString *)pcen NS_RETURNS_RETAINED {
return (__bridge_transfer NSString *)CFURLCreateStringByAddingPercentEscapes(NULL, (__bridge CFStringRef) self, NULL, (CFStringRef) @"!*'();:@&=+$,/?%#[]", kCFStringEncodingUTF8);
}
把它放在一边是否正确? NS_RETURNS_RETAINED
那里?
另一个例子:
+ (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
这里?
第三个例子:
@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"];
什么是最佳实践呢?
第一个例子
将NS_RETURNS_RETAINED放在那里是否正确?
它是 不正确 - 这里没有必要的属性。添加属性将违反命名约定,这非常重要。
更详细地说,因为引用是,所以不需要任何属性 转入 在示例中使用 (__bridge_transfer NSString*)
。有人可能会认为CFCreated-Reference可能需要更多东西,但是 (__bridge_transfer NSString*)
就是将该引用转移到ARC所需的一切;为它管理你。
如果你要使用typecasted (__bridge NSString*)CF_*_Create_*_
,那么CFCreate函数返回的引用将不会转移到ARC,并且会引入泄漏。
(作为替代方案,如果您选择明确释放返回的字符串,则可以避免泄漏(例如,使用 CFRelease
)。)
第二个例子
但是,它所获得的图形上下文是在方法范围内创建的,所以在这里使用NS_RETURNS_RETAINED也是正确的吗?
使用属性不正确或不必要。 ARC使用命名约定和属性来确定要添加的引用计数操作 - 它了解您的程序。
与第一个例子不同,一个明确的 __bridge_transfer
不应该做。
添加属性会破坏命名约定。
第三个例子
- (NSArray *)places
...
不知道该怎么做,因为返回的对象可以是新创建的。
同样,不应使用任何属性。一个明确的 __bridge_transfer
不应该做。 ARC了解ObjC约定,包括返回现有和新创建的对象。它将为两个路径插入正确的引用计数操作。
还有最后一个问题;如果返回的对象是autorelease'ed方法的结果,则可能不需要NS_RETURNS_RETAINED。所以说最后一个例子的回报被修改为
return [NSArray arrayWithObject:@"Unknown"];
同样,不需要任何属性。不应进行明确的转移。
所有系统库中只存在少数几个属性用途。
我真的,真的,真的,真的建议不要使用这些属性,特别是:
- 涉及动态调度的地方(所有objc方法都符合条件)
- 参数(消耗)和结果(保留的返回)是ObjC类型
理由是参考转移应该是实施的本地转移,并且很少有真正的需要偏离它;向后兼容性可能是我能想到的“最佳”原因。如果您可以控制您的代码,只需更新它以尽可能做正确的事情,而不是引入这些属性。这可以通过遵守命名约定,并在适当的时候将引用转移到ARC来实现。
有关错误的一些示例,您可能会遇到使用属性并偏离命名约定,请参阅: 字典的深层副本在Xcode 4.2中给出了分析错误 。
坚持使用命名约定的另一个好理由是,您并不总是知道如何使用您的程序。如果有人想在MRC翻译中使用你的程序,那么他们将不得不编写如下所示的异常程序:
某个地方
- (NSString *)name NS_RETURNS_RETAINED;
别处
NSString * name = obj.name;
NSLog(@"%@", name);
[name release]; // << ME: not a mistake. triple checked.
第一个例子
将NS_RETURNS_RETAINED放在那里是否正确?
它是 不正确 - 这里没有必要的属性。添加属性将违反命名约定,这非常重要。
更详细地说,因为引用是,所以不需要任何属性 转入 在示例中使用 (__bridge_transfer NSString*)
。有人可能会认为CFCreated-Reference可能需要更多东西,但是 (__bridge_transfer NSString*)
就是将该引用转移到ARC所需的一切;为它管理你。
如果你要使用typecasted (__bridge NSString*)CF_*_Create_*_
,那么CFCreate函数返回的引用将不会转移到ARC,并且会引入泄漏。
(作为替代方案,如果您选择明确释放返回的字符串,则可以避免泄漏(例如,使用 CFRelease
)。)
第二个例子
但是,它所获得的图形上下文是在方法范围内创建的,所以在这里使用NS_RETURNS_RETAINED也是正确的吗?
使用属性不正确或不必要。 ARC使用命名约定和属性来确定要添加的引用计数操作 - 它了解您的程序。
与第一个例子不同,一个明确的 __bridge_transfer
不应该做。
添加属性会破坏命名约定。
第三个例子
- (NSArray *)places
...
不知道该怎么做,因为返回的对象可以是新创建的。
同样,不应使用任何属性。一个明确的 __bridge_transfer
不应该做。 ARC了解ObjC约定,包括返回现有和新创建的对象。它将为两个路径插入正确的引用计数操作。
还有最后一个问题;如果返回的对象是autorelease'ed方法的结果,则可能不需要NS_RETURNS_RETAINED。所以说最后一个例子的回报被修改为
return [NSArray arrayWithObject:@"Unknown"];
同样,不需要任何属性。不应进行明确的转移。
所有系统库中只存在少数几个属性用途。
我真的,真的,真的,真的建议不要使用这些属性,特别是:
- 涉及动态调度的地方(所有objc方法都符合条件)
- 参数(消耗)和结果(保留的返回)是ObjC类型
理由是参考转移应该是实施的本地转移,并且很少有真正的需要偏离它;向后兼容性可能是我能想到的“最佳”原因。如果您可以控制您的代码,只需更新它以尽可能做正确的事情,而不是引入这些属性。这可以通过遵守命名约定,并在适当的时候将引用转移到ARC来实现。
有关错误的一些示例,您可能会遇到使用属性并偏离命名约定,请参阅: 字典的深层副本在Xcode 4.2中给出了分析错误 。
坚持使用命名约定的另一个好理由是,您并不总是知道如何使用您的程序。如果有人想在MRC翻译中使用你的程序,那么他们将不得不编写如下所示的异常程序:
某个地方
- (NSString *)name NS_RETURNS_RETAINED;
别处
NSString * name = obj.name;
NSLog(@"%@", name);
[name release]; // << ME: not a mistake. triple checked.
[这个答案部分是对贾斯汀给出的答案的长期评论/修正。之前的回答让我相信对属性和ARC如何处理返回引用的语义的错误描述。
答案在于ARC分析的工作原理和意义 NS_RETURNS_RETAINED
。
ARC分析您的源以确定何时保留,释放或自动释放可保留的对象引用。
如果 您的应用程序的所有源代码都可用,理论上,分析可能能够从“第一原则”确定此信息 - 从最小的表达式开始并向外工作。
然而 所有来源都不可用 - 例如有些已经在框架等中编译了 - 因此在分析方法时,ARC不会查看方法的来源,而只会查看其签名 - 其名称及其参数类型和返回值。
考虑到可保留对象类型的返回值,ARC需要知道是否正在转移所有权 - 在这种情况下ARC将需要 发布 它在某个时刻 - 或者不是(例如 自动释放 参考) - 在这种情况下ARC将需要 保留 如果需要所有权。
ARC根据此信息确定此信息 名称 方法和任何属性。方法从 init
要么 new
或含有 copy
根据定义,转让所有权;所有其他方法都没有。属性 NS_RETURNS_RETAINED
告知ARC一个方法,无论其名称如何,都会转移其返回引用的所有权。
那是故事的一半......另一半是ARC处理的问题 return
方法体中的语句。
一个 return
实际上是一种赋值,当执行可保留对象引用赋值时,ARC根据其对当前所有权和引用以及目标要求的了解,确定引用是否需要保留,自动释放或保留。
为一个 return
声明目的地的要求,毫不奇怪,由方法的名称和签名上指定的任何属性决定。如果签名表明所有权正在转移,那么ARC将返回一个 保留 参考,否则它将返回 自动释放 一。
重要的是要理解ARC正在进行方法调用的两个方面,它确保返回适当的引用 和 确定如何处理返回的引用。
通过所有前言,我们可以看看你的第一个例子。看起来你正在写一个方法 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
方法 return
声明ARC查看签名,名称(pcen
)并不表示所有权的转移,也没有属性,因此ARC增加了一个 autorelease
表达式返回的引用 (__bridge_transfer NSString *) ... kCFStringEncodingUTF8)
因为该表达式返回所拥有的引用 pcen
。
重要: 什么 表达式并不重要,只是表达式 pcen
拥有它保留的参考 - 特别是 __bridge_transfer
不确定方法返回的引用的所有权。
编译调用时 pcen
在里面 applicationDidFinishLaunching
方法ARC再次查看签名,确定当前方法需要所有权,并且返回的引用不是所有者并插入一个 retain
。
您可以通过在Xcode中调用“Product> Generate Output> Assembly File”来验证这一点,在生成的程序集中,您将在代码中看到 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确实如此 不 插入自动释放。在呼叫站点,组件变为:
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这样做 不 插入一个保留。
所以这两个版本都是“正确的”,但哪个更好?
可能看起来带有属性的版本更好,因为ARC不需要插入自动释放/保留;但是运行时优化了这个序列(因此调用了 _objc_retainAutoreleasedReturnValue
而不是像 _objc_retain
所以成本并不像看上去那么大。
然而 正确的答案是 也不...
建议的解决方案是依赖Cocoa / ARC约定并更改方法的名称,例如:
@interface NSString (AddingPercentEscapes)
- (NSString *) newPercentEscapedString;
@end
以及相关的变化。
这样做,你会得到相同的代码 pcen NS_RETURNS_RETAINED
因为ARC决定它应该基于的转移所有权 名称 new...
。
这个答案已经很久了,希望上面的内容可以帮助你找出其他两个例子的答案!