繁体   English   中英

命名方法的简单规则,与ARC命名约定兼容

[英]Simple rules for naming methods, compatible with ARC naming conventions

我很难理解ARC的命名约定。 我一直使用ARC进行编码,我想这就是原因。

1.类方法

  • 我应该为以下方法选择什么名称?
  • 这两个名称在内存管理方面有什么区别?

这个名字:

+ (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 ;
}

2.实例方法

  • 我应该为以下方法选择什么名称?
  • 这两个名称在内存管理方面有什么区别?

这个名字:

- (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 ;
}

2.命名方法的简单规则

命名方法时是否要遵循基本的简单规则?

我所说的“基本,简单”

  • 类似于“当对象属于该类时为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 ....]等的实例类型,因此我仍然绝对希望遵循此约定。

  1. ARC为以前用来跟踪引用计数职责的命名约定提供了语义。 因此,现在编译器使用相同的命名模式来确定方法是否返回保留的对象,等等。请参见@JodyHagens答案中的链接,并继续阅读http://clang.llvm.org/docs/AutomaticReferenceCounting.html#semantics方法家庭

    alloccopymutableCopynew系列中的方法(即,除init以外的所有当前定义的系列中的方法)隐式返回保留的对象,就像它们使用ns_returns_retained属性进行了注释ns_returns_retained 可以通过使用ns_returns_autoreleasedns_returns_not_retained属性之一对方法进行注释来覆盖此方法。

    new家族”是消息选择器,以“ new”开头,后跟一个大写字母。 因此, 之间的内存管理差异newObjectFrom:withOptions:objectFrom:withOptions: ,但你可以使用注释来重写(不要去那里),如果你弄错了编译器应该抱怨。

    (实现给定消息选择器的所有方法最好都提供相同的引用计数语义。)

  2. 除了苹果公司 @ iRebel_85指出的有关约定的文章外, 苹果公司还提供了广泛的命名准则

  3. Google的样式指南增加了一些命名指南

这些准则经过深思熟虑,对于使代码更易于理解和协作非常有用。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM