目标C语言 命名方法的简单规则,与ARC命名约定兼容



我很难理解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的内存管理的规则;

  • (和/或)不使用"autorelease", "release"等词的规则。

当Rivera说方法名并不重要时,他可能遵循了他的经验:它总是有效的。这是正确的。你是对的,你可能不理解方法名的作用,因为你一直在使用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开头,则必须像对其执行retain操作一样处理返回的对象。或者一句话:所有权转让:

id object = [Class allocOrInitOrNewMethod];
…
[object release];

-retain显式占有所有权,alloc–init…new…隐式占有所有权。

所有其他方法的行为与规则1相同。

规则3:有时候你需要一个自动发布池。要使ARPs的优势可见,请考虑以下代码:(这是无用的,只是为了演示问题

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.

你也可以对getter做同样的事情:

- (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完成。

方法名很重要。ARC如何解释方法名的官方文档可以在clang ARC文档中的方法族章节中找到。

在将代码从MRC转换为ARC以及与ARC代码中的MRC代码进行互操作时,方法命名约定非常重要。
苹果指南上说,"只有ARC代码"的命名约定"不那么重要",但这并不是100%正确的。

例如(这只是一个例子,我认为还有很多其他的例子),看看这个项目

我混合了release-autorelease调用来记录它,你可以看到区别:

当一个方法以一个特殊的命名(例如"new")开始时,ARC返回一个保留计数为+1的对象,这由释放调用来平衡。当使用不同的名称时,ARC返回一个autorelease对象。(查看NSLog调用)

对于在循环中分配大量对象的代码来说,这可能是一个很大的区别。在这种情况下,程序员应该创建一个自动发布池。

所以,在使用纯arc代码时,命名约定不那么重要并不是真的。

我不知道100%常规命名方法的所有细节,因为每种方法适用于不同的情况。不过我认为苹果文档中的这篇文章会对你有所帮助。https://developer.apple.com/library/ios/documentation/Cocoa/Conceptual/ProgrammingWithObjectiveC/Conventions/Conventions.html

我在apple世界的经验是,像您的示例那样的工厂方法通常按照惯例包含'create'。这在现在的ARC中可能不那么重要了,但在过去,方法或函数签名中的"create"是一种非常确定的方法,可以确保你理解你正在获得结果对象的所有权(在其上拥有release/free()),而不是一个实例类型,你会假设自动释放[NSArray array], [UIColor colorWith....等等,所以我仍然非常喜欢遵循这个惯例。

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

    alloccopymutableCopynew家族中的方法——也就是说,除了init之外的所有当前定义的家族中的方法——隐式返回一个保留的对象,就好像它们被ns_returns_retained属性注释了一样。这可以通过用ns_returns_autoreleasedns_returns_not_retained属性注释方法来覆盖。

    "new系列"是消息选择器,以"new"开头,后跟一个大写字母。所以在newObjectFrom:withOptions:objectFrom:withOptions:之间有内存管理的差异,但是您可以使用注释来覆盖它(不要这样做),如果您弄错了,编译器会报错。

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

  2. 除了@iRebel_85指出的苹果关于惯例的文章外,苹果还有一套广泛的命名指南。

  3. Google的样式指南增加了更多的命名准则。

这些指导方针是经过深思熟虑的,对于使代码更容易理解和协作非常有用。

首先,内存管理与您为方法或类选择的名称无关。换句话说,如果它可以编译,并且您没有使用保留关键字或关键方法名,那么应该是好的。(请参考编辑说明)

对于1,前面的new在Objective-C中很少见,所以最好使用objectFrom:

最好是更精确地说明你创建的对象的类型。例如:

[NSArray arrayWithArray:]
[NSString stringWithFormat:]

或者在您创建副本的情况下,假设您正在创建"Client"对象:

[Client clientWithClient:options:]

不需要with的地方

对于2,我会选择:

copyWithOptions:

当您或多或少地定制[NSObject copy]时。我还会只实现这个方法,并删除现在冗余的1,因为更少的方法更清晰,更容易记录,更容易维护!

如果有疑问,请搜索SDK文档,看看Apple如何命名类似的方法。

编辑:

在第一段中,我并不是要鼓励不良的命名实践,只是说它们不是您关注内存管理的原因。然而,你应该尝试遵循其他答案中指出的命名约定,或者正如我所说的,"像Apple那样做"。

最新更新