[英]Simple rules for naming methods, compatible with ARC naming conventions
我很难理解ARC的命名约定。 我一直使用ARC进行编码,我想这就是原因。
这个名字:
+ (MyObject *)newObjectFrom:(MyObject *)anObject
withOptions:(NSDictionary*)options
{
MyObject * newObject = [anObject copy] ;
[newObject modifyWith:options] ;
return newObject ;
}
还是这个名字?
+ (MyObject *)objectFrom:(MyObject *)anObject
withOptions:(NSDictionary*)options
{
MyObject * newObject = [anObject copy] ;
[newObject modifyWith:options] ;
return newObject ;
}
这个名字:
- (MyObject *)newObjectwithOptions:(NSDictionary*)options
{
MyObject * newObject = [self copy] ;
[newObject modifyWith:options] ;
return newObject ;
}
还是这个名字?
- (MyObject *)objectwithOptions:(NSDictionary*)options
{
MyObject * newObject = [self copy] ;
[newObject modifyWith:options] ;
return newObject ;
}
命名方法时是否要遵循基本的简单规则?
我所说的“基本,简单”
类似于“当对象属于该类时为strong
”,“仅当该类引用该对象并且(因此)为另一类所拥有时”为weak
”的规则;
(和/或)不涉及没有ARC
的内存管理的规则;
(和/或)不使用“自动发布”,“发布”之类的词的规则。
方法名称很重要。 关于ARC解释方法名称的官方文档可以在clang ARC文档的“ 方法系列 ”部分中找到。
当里维拉说方法名称并不重要时,他可能会遵循他的经验:它总是有效的。 这是正确的。 而且您是正确的,因为您一直使用ARC,所以您可能不了解方法名称的作用。 那么有什么大不了的呢?
我在此答案的末尾添加了摘要以显示MRR的问题。 如您所见,您不必像使用MRR那样关心使用ARC命名规则。
为了给您更详细的解释,您必须了解使用MRR会发生什么:
在ARC之前,必须手动进行内存管理。 为此,他必须知道回报值具有什么样的所有权。 简而言之,必须知道他是否必须释放返回的对象:
规则1 :您不拥有方法自动返回的对象。 如果要握住它, 请在完成后保留并释放它。
id object = [[object methodThatReturnsAnObject] retain]; // I want to hold it
…
[object release]; // Done with it
id object = [object methodThatReturnsAnObject]; // I do not want to hold it
…
// I do not release it
进行深入分析,您会发现有时会出现问题。 (对象重新分配是一个副作用。)但这是基本方法。
这样做的好处是可以对保留释放对进行本地处理(在复合语句中),并且很容易跟踪对象的所有权。
规则2 :但是,当第一次创建对象时,此操作将不起作用:消息的发送者必须始终保留它。 否则,它将被立即销毁(=在发件人有机会保留它之前。)因此,还有一条附加规则:
如果返回对象的类方法的名称以alloc,init或new开头,则必须像对待保留对象一样处理返回的对象。 或一句话:所有权转让:
id object = [Class allocOrInitOrNewMethod];
…
[object release];
由于-retain
明确地取得了所有权, alloc–
, init…
, new…
隐式转移了所有权。
所有其他方法的行为类似于规则1。
规则3 :有时您需要一个自动释放池。 为了使ARP的优势可见,请考虑以下代码:(它无用,只是为了演示问题。
情况1.1:
Person *person = [[Person alloc] initWithName:…]; // Ownership transfer, release person, when you are done
NSString *name = [person name]; // the name object is hold by the person object only
[person release]; // I do not need the person object any more
[name doSomething]; // Crash: The person was the only object holding the name
另一个问题:
情况2.1:
Person *person = [[Person alloc] initWithName:…]; // Ownership transfer, release person, when you are done
if (…)
{
return; // break, continue, goto
}
…
[person release];
因为永远不会到达最后一行,所以永远不会释放对象。
您可以使用自动释放方法进行修复。 只要控制流返回到运行循环,移动到ARP的对象就会存在。 因此,它存在于每种方法中,通过方法返回等中。 要明确地做到这一点:
情况1.2:
Person *person = [[[Person alloc] initWithName:…] autorelease]; // No ownership transfer, persons belongs to the ARP
NSString *name = [person name]; // the name object is hold by the person object only
[name doSomething]; // No Crash: The person object holding the name object is still alive
情况2.2:
Person *person = [[[Person alloc] initWithName:…] autorelease]; // No ownership transfer, prsons belongs to the AR.
if (…)
{
return; // break, continue, goto
}
…
// No release necessary.
因为必须懒得键入这么长的消息链,所以发明了便利分配器来为您做到这一点:
+ (Person*)personWithName:(NSString*)name
{
return [[[self alloc] initWithName:name] autorelease];
}
结果:
情况2.3:
Person *person = [personWithName:…]; // No ownership transfer, persons belongs to the AR.
if (…)
{
return; // break, continue, goto
}
…
// No release necessary.
您也可以使用吸气剂做同样的事情:
- (NSString*)name
{
return [[_name retain] autorelease];
}
因此,总而言之,我们有简单的规则:
规则1:如果要将返回的对象保留在内存中,请保留该对象,然后在不再需要它时将其释放。
规则2:如果类方法的名称以alloc,new或init开头,则将其视为隐式retain
(并在完成对象操作后将其释放)。
规则3:为方便起见,请使用-autorelease
延迟返回对象的重新分配。
概要:
创建Alloc-init
// MRR:
Person *person = [[Person alloc] initWithName:@"Amin"];
…
[person release]; // You create it, you release it
// ARC:
Person *person = [[Person alloc] initWithName:@"Amin"];
…
新创作者
// MRR:
Person *person = [[Person newPersonWithName:@"Amin"];
…
[person release]; // You create it, you release it
// ARC:
Person *person = [[Person newPersonWithName:@"Amin"];
…
便利分配器
// MRR:
Person *person = [[Person personWithName:@"Amin"]; // Autoreleased
…
// ARC:
Person *person = [[Person personWithName:@"Amin"];
…
如您所见,使用ARC创建对象的三种方式没有区别。 所以里维埃拉是对的,当他说这不再重要时。 但是在幕后,最后一种方法是不同的,因为它将对象移动到ARP。
此方法的实现相同:
实施新的分配器
// MRR
+ (Person*)newPersonWithName:(NSString*)name
{
return [[self alloc] initWithName:name];
}
// ARC
+ (Person*)newPersonWithName:(NSString*)name
{
return [[self alloc] initWithName:name];
}
实现便利分配器
// MRR
+ (Person*)personWithName:(NSString*)name
{
return [[[self alloc] initWithName:name] autorelease];
}
// ARC
+ (Person*)personWithName:(NSString*)name
{
return [[self alloc] initWithName:name];
}
同样,使用ARC,这两种方法的实现方式都是相同的。
-> 您不再需要任何此规则。 ARC在乎您四个。
特别是您不再需要使用便利分配器,因为不再有任何不便之处。 (即使它们在运行时进行了优化,运行时的损失也仍然最小。)我不再实现便捷分配器,而是实现新的分配器。
但是ARC必须与MRR兼容。 因此它记住所有规则。 为了使您的代码对其他人可读,您也应该重复此规则。 但是,当然,现在便利分配器和新分配器的实现按位相同–其余工作由ARC完成。
将代码从MRC转换为ARC时,以及与ARC Code中的MRC代码进行互操作时,方法命名约定非常重要。
Apple指南说,“仅ARC代码”的命名约定“不太重要”,但这并不是100%正确的。
例如(这只是一个例子,我想还有很多其他例子),请看一下这个项目
我将release-autorelease调用都记录下来,您可以看到其中的区别:
当方法以特殊命名(例如“ new”)开头时,ARC返回一个保留计数为+1的对象,该对象由释放调用平衡。
使用差异名称时,ARC返回一个AUTORELEASED对象。 (查看NSLog调用)
在循环中分配大量对象的代码中,这可能是一个很大的差异。 在这种情况下,程序员应创建一个自动释放池。
因此,使用仅ARC的代码时,命名约定的重要性并不那么重要。
按照惯例,我不了解有关命名方法的所有详细信息,而是100%,因为每种情况都有不同的情况。 但是我认为Apple文档中的这篇文章会为您提供帮助。 https://developer.apple.com/library/ios/documentation/Cocoa/Conceptual/ProgrammingWithObjectiveC/Conventions/Conventions.html
首先,内存管理与为方法或类选择的名称无关。 换句话说,如果它可以编译并且您没有使用保留关键字或关键方法名称,那么您应该很好。 (请参阅编辑注释)
至于1,之前的new
在Objective-C中很少见,因此最好使用objectFrom:
更好的是要更精确地创建您要创建的对象的类型。 例如:
[NSArray arrayWithArray:]
[NSString stringWithFormat:]
或者在您创建副本的情况下,并假设您正在创建“客户端”对象:
[Client clientWithClient:options:]
凡with
是不是真的需要。
对于2我将选择:
copyWithOptions:
随着您或多或少地定制[NSObject copy]
。 我也将仅实现此方法并删除现在多余的1,因为更少的方法更清晰,易于记录且易于维护!
如有疑问,请搜索SDK文档,以了解Apple如何命名类似的方法。
编辑:
在第一段中,我并不是要鼓励不良的命名习惯,只是说它们不是引起您的内存管理问题的原因。 但是,您应该尝试遵循其他答案中指出的命名约定,或者正如我所说的,“像Apple一样”。
我在苹果世界中的经验是,像您的示例这样的工厂方法通常会按惯例包括“创建”。 现在,这对于ARC来说可能不再那么重要了,但是在过去,方法或函数签名中的“创建”是一种非常确定的方法,可以确保您了解自己对结果对象的所有权(由于release / free()它),而不是您假设会自动释放[NSArray array],[UIColor colorWith ....]等的实例类型,因此我仍然绝对希望遵循此约定。
ARC为以前用来跟踪引用计数职责的命名约定提供了语义。 因此,现在编译器使用相同的命名模式来确定方法是否返回保留的对象,等等。请参见@JodyHagens答案中的链接,并继续阅读http://clang.llvm.org/docs/AutomaticReferenceCounting.html#semantics方法家庭
alloc
,copy
,mutableCopy
和new
系列中的方法(即,除init
以外的所有当前定义的系列中的方法)隐式返回保留的对象,就像它们使用ns_returns_retained
属性进行了注释ns_returns_retained
。 可以通过使用ns_returns_autoreleased
或ns_returns_not_retained
属性之一对方法进行注释来覆盖此方法。
“ new
家族”是消息选择器,以“ new”开头,后跟一个大写字母。 因此, 有之间的内存管理差异newObjectFrom:withOptions:
和objectFrom:withOptions:
,但你可以使用注释来重写(不要去那里),如果你弄错了编译器应该抱怨。
(实现给定消息选择器的所有方法最好都提供相同的引用计数语义。)
除了苹果公司 @ iRebel_85指出的有关约定的文章外, 苹果公司还提供了广泛的命名准则 。
这些准则经过深思熟虑,对于使代码更易于理解和协作非常有用。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.