大概是因为deepCopy
不以前缀开头copy
。
因此,您可能想要更改为类似copyWithDeepCopiedValues
(或类似)的内容,然后查看分析器是否标记了该内容。
更新
正如 Alexsander 所指出的,您可以使用属性来表示引用计数意图。这应该(IMO)是规则的例外,并且很少使用,如果有的话。就个人而言,我不会为 objc 方法使用属性,因为它很脆弱。
到目前为止,我使用的唯一属性是consume
,并且每次我使用这些属性时都是在静态类型的上下文中(例如 C 函数和 C++ 函数和方法)。
您应该尽可能避免使用属性的原因:
1)为了程序员的利益,坚持约定。代码更清晰,无需参考文档。
2)方法很脆弱。您仍然可以引入引用计数不平衡,并且由于属性冲突,可以使用属性来引入构建错误。
以下案例都是在启用 ARC 的情况下构建的:
情况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
。
太好了,编译器正在尽其所能来防止这些问题。这意味着属性冲突可能会引入基于翻译的错误。这很糟糕,因为当重要的代码库组合在一起并且属性冲突时,您将需要更正错误并更新程序。这也意味着在翻译单元中简单地包含其他库可能会在使用属性时破坏现有程序。
案例#2
头文件.h
extern id NewObject(void);
头文件.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;
}
主文件
#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;
}
行。这很糟糕。我们引入了泄漏,因为翻译单元中没有必要的信息。这是泄漏报告:
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
来自 Foundation 中的声明,所有这些都遵循约定)。结果,编译器出错了,我们能够在程序中引入泄漏。
案例3
使用类似的方法,我还能够引入负引用计数不平衡(过早发布或消息僵尸)。
注意:未提供代码,因为案例 #2 已经说明了如何实现引用计数不平衡。
结论
您可以通过遵守约定而不是使用属性来避免所有这些问题并提高可读性和可维护性。
让话题回到非 ARC 代码:使用属性使手动内存管理对于程序员的可读性和可以帮助您的工具(例如编译器、静态分析)更加困难。如果程序相当复杂,以至于工具无法检测到此类错误,那么您应该重新考虑您的设计,因为您或其他人调试这些问题同样复杂。