大概是因为deepCopy
才不是begin与前缀copy
.
所以你可能想改变成类似的东西copyWithDeepCopiedValues
(或类似的东西),然后查看分析器是否对此进行标记。
Update
正如 Alexsander 指出的,您可以使用属性来表示引用计数意图。这应该(IMO)是规则的例外,并且很少使用(如果有的话)。就我个人而言,我不会对 objc 方法使用属性,因为它很脆弱。
到目前为止我使用过的唯一属性是consume
,并且每次我使用这些属性都是在静态类型上下文中(例如 C 函数和 C++ 函数和方法)。
应尽可能避免使用属性的原因:
1)为了程序员的利益,坚持约定。代码比较清晰,不需要看文档。
2)该方法是脆弱的。您仍然可以引入引用计数不平衡,并且属性可以用于由于属性冲突而引入构建错误。
以下案例均启用了 ARC 构建:
Case #1
#import <Foundation/Foundation.h>
@interface MONType : NSObject
- (NSString *)string __attribute__((objc_method_family(copy)));
@end
@implementation MONType
- (NSString *)string
{
NSMutableString * ret = [NSMutableString new];
[ret appendString:@"MONType"];
return ret;
}
@end
int main (int argc, const char * argv[])
{
@autoreleasepool {
id obj = nil;
if (random() % 2U) {
obj = [[NSAttributedString alloc] initWithString:@"NSAttributedString"];
}
else {
obj = [MONType new];
}
NSLog(@"Result: %@, %@", obj, [obj string]);
}
/* this tool's name is ARC, dump the leaks: */
system("leaks ARC");
return 0;
}
该程序产生以下错误:error: multiple methods named 'string' found with mismatched result, parameter type or attributes
.
太好了,编译器正在尽其所能来防止这些问题。这意味着属性冲突可能会导致翻译错误。这是bad因为当组合重要的代码库并且属性发生冲突时,您将需要纠正错误并更新程序。这也意味着在使用属性时,简单地将其他库包含在翻译单元中可能会破坏现有程序。
Case #2
Header.h
extern id NewObject(void);
Header.m
#import <Foundation/Foundation.h>
#import "Header.h"
@interface MONType : NSObject
- (NSString *)string __attribute__((objc_method_family(copy)));
@end
@implementation MONType
- (NSString *)string
{
NSMutableString * ret = [NSMutableString new];
[ret appendString:@"-[MONType string]"];
return ret;
}
@end
id NewObject(void) {
id obj = nil;
if (random() % 2U) {
obj = [[NSAttributedString alloc] initWithString:@"NSAttributedString"];
}
else {
obj = [MONType new];
}
return obj;
}
main.m
#import <Foundation/Foundation.h>
#import "Header.h"
int main (int argc, const char * argv[])
{
@autoreleasepool {
for (size_t idx = 0; idx < 8; ++idx) {
id obj = NewObject();
NSLog(@"Result: %@, %@", obj, [obj string]);
}
}
/* this tool's name is ARC, dump the leaks: */
system("leaks ARC");
return 0;
}
好的。这只是bad。我们引入了泄漏,因为翻译单元中没有必要的信息。这是泄漏报告:
leaks Report Version: 2.0
Process 7778: 1230 nodes malloced for 210 KB
Process 7778: 4 leaks for 192 total leaked bytes.
Leak: 0x1005001f0 size=64 zone: DefaultMallocZone_0x100003000 __NSCFString ObjC CoreFoundation mutable non-inline: "-[MONType string]"
Leak: 0x100500320 size=64 zone: DefaultMallocZone_0x100003000 __NSCFString ObjC CoreFoundation mutable non-inline: "-[MONType string]"
Leak: 0x100500230 size=32 zone: DefaultMallocZone_0x100003000 has-length-byte: "-[MONType string]"
Leak: 0x100500390 size=32 zone: DefaultMallocZone_0x100003000 has-length-byte: "-[MONType string]"
注意:计数可能会有所不同,因为我们使用了random()
这意味着因为MONType
不可见main()
,编译器将 ARC 属性绑定到当前 TU 可见的方法(即string
来自基金会的声明,所有这些都遵循惯例)。结果,编译器出错,我们就可以在程序中引入泄漏。
Case 3
使用类似的方法,我还能够引入负引用计数不平衡(过早发布或消息僵尸)。
注意:未提供代码,因为案例 #2 已经说明了如何实现引用计数不平衡。
结论
您可以通过坚持约定而不是使用属性来避免所有这些问题并提高可读性和可维护性。
让对话回到非 ARC 代码:使用属性会使手动内存管理对于程序员的可读性以及可以帮助您的工具(例如编译器、静态分析)来说变得更加困难。如果程序相当复杂,以至于工具无法检测到此类错误,那么您应该重新考虑您的设计,因为对于您或其他人来说调试这些问题同样复杂。