Col*_*las 4 memory-leaks objective-c naming-conventions class-method automatic-ref-counting
我很难理解ARC的命名约定.我一直用ARC编码,我猜这就是原因.
这个名字:
+ (MyObject *)newObjectFrom:(MyObject *)anObject
withOptions:(NSDictionary*)options
{
MyObject * newObject = [anObject copy] ;
[newObject modifyWith:options] ;
return newObject ;
}
Run Code Online (Sandbox Code Playgroud)
还是这个名字?
+ (MyObject *)objectFrom:(MyObject *)anObject
withOptions:(NSDictionary*)options
{
MyObject * newObject = [anObject copy] ;
[newObject modifyWith:options] ;
return newObject ;
}
Run Code Online (Sandbox Code Playgroud)
这个名字:
- (MyObject *)newObjectwithOptions:(NSDictionary*)options
{
MyObject * newObject = [self copy] ;
[newObject modifyWith:options] ;
return newObject ;
}
Run Code Online (Sandbox Code Playgroud)
还是这个名字?
- (MyObject *)objectwithOptions:(NSDictionary*)options
{
MyObject * newObject = [self copy] ;
[newObject modifyWith:options] ;
return newObject ;
}
Run Code Online (Sandbox Code Playgroud)
在命名方法时是否有一个基本的简单规则?
我的意思是"基本的,简单的"
类似于" 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
Run Code Online (Sandbox Code Playgroud)
进行深入分析,您可以看到有时会出现问题。(对象释放作为副作用。)但这是基本方法。
这样做的好处是可以在本地(在复合语句内)处理一个保留-释放对,并且很容易跟踪对象的所有权。
规则 2:但是当第一次创建对象时,这不起作用:消息的发送者必须始终持有它。否则它会立即被销毁(= 在发件人有机会保留它之前。)所以有一个额外的规则:
如果返回对象的类方法的名称以 alloc、init 或 new 开头,则必须像在其上执行保留一样处理返回的对象。或者一句话:所有权转让:
id object = [Class allocOrInitOrNewMethod];
…
[object release];
Run Code Online (Sandbox Code Playgroud)
由于-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
Run Code Online (Sandbox Code Playgroud)
另一个问题:
案例 2.1:
Person *person = [[Person alloc] initWithName:…]; // Ownership transfer, release person, when you are done
if (…)
{
return; // break, continue, goto
}
…
[person release];
Run Code Online (Sandbox Code Playgroud)
因为永远不会到达最后一行,所以永远不会释放该对象。
您可以使用自动释放方法修复它。只要控制流返回到运行循环,移动到 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
Run Code Online (Sandbox Code Playgroud)
案例 2.2:
Person *person = [[[Person alloc] initWithName:…] autorelease]; // No ownership transfer, prsons belongs to the AR.
if (…)
{
return; // break, continue, goto
}
…
// No release necessary.
Run Code Online (Sandbox Code Playgroud)
因为懒得输入这么长的消息链,所以已经发明了便利分配器来为你做这件事:
+ (Person*)personWithName:(NSString*)name
{
return [[[self alloc] initWithName:name] autorelease];
}
Run Code Online (Sandbox Code Playgroud)
结果:
案例 2.3:
Person *person = [personWithName:…]; // No ownership transfer, persons belongs to the AR.
if (…)
{
return; // break, continue, goto
}
…
// No release necessary.
Run Code Online (Sandbox Code Playgroud)
你可以对 getter 做同样的事情:
- (NSString*)name
{
return [[_name retain] autorelease];
}
Run Code Online (Sandbox Code Playgroud)
所以我们在一天结束时有简单的规则:
规则 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"];
…
Run Code Online (Sandbox Code Playgroud)
新创作者
// MRR:
Person *person = [[Person newPersonWithName:@"Amin"];
…
[person release]; // You create it, you release it
// ARC:
Person *person = [[Person newPersonWithName:@"Amin"];
…
Run Code Online (Sandbox Code Playgroud)
便利分配器
// MRR:
Person *person = [[Person personWithName:@"Amin"]; // Autoreleased
…
// ARC:
Person *person = [[Person personWithName:@"Amin"];
…
Run Code Online (Sandbox Code Playgroud)
如您所见,使用 ARC 创建对象的三种方式没有区别。所以里维埃拉是对的,当时他说这已经不重要了。但在幕后,最后一种方式是不同的,因为它将对象移动到 ARP。
这个方法的实现也是一样的:
实现一个新的分配器
// MRR
+ (Person*)newPersonWithName:(NSString*)name
{
return [[self alloc] initWithName:name];
}
// ARC
+ (Person*)newPersonWithName:(NSString*)name
{
return [[self alloc] initWithName:name];
}
Run Code Online (Sandbox Code Playgroud)
实现便利分配器
// MRR
+ (Person*)personWithName:(NSString*)name
{
return [[[self alloc] initWithName:name] autorelease];
}
// ARC
+ (Person*)personWithName:(NSString*)name
{
return [[self alloc] initWithName:name];
}
Run Code Online (Sandbox Code Playgroud)
同样,使用 ARC 时,这两种方法的实现是相同的。
->你不再需要任何这些规则了。ARC关心四个你。
尤其是您不再需要便利分配器,因为不再有任何不方便之处。(即使它们在运行时进行了优化,仍然存在最小的运行时损失。)我不再实现便利分配器,而是实现新的分配器。
但是ARC必须与MRR兼容。所以它记住了所有的规则。为了让你的代码对其他人可读,你也应该重复这个规则。但是,当然,现在便利分配器和新分配器的实现是按位相同的——其余的由 ARC 完成。
我不知道 100% 传统命名方法的所有细节,因为每种方法都有不同的情况。不过我认为 Apple 文档中的这篇文章会对您有所帮助。https://developer.apple.com/library/ios/documentation/Cocoa/Conceptual/ProgrammingWithObjectiveC/Conventions/Conventions.html