簡體   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