[英]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.