[iOS翻譯]《iOS 7 Programming Pushing the Limits》系列:你可能不知道的Objective-C技巧

來源:互聯網
上載者:User

簡介:

如果你閱讀這本書,你可能已經牢牢掌握iOS開發的基礎,但這裡有一些小特點和實踐是許多開發人員並不熟悉的,甚至有數年經驗的開發人員也是。在這一章裡,你會學到一些很重要的開發技巧,但這仍遠遠不夠,你還需要積累更多的實踐來讓你的代碼更強力。

/*

本文翻譯自《iOS 7 Programming Pushing the Limits》一書的第三章“You May Not Know”,想體會原文精髓的朋友請支援原書正版。

——————(部落格園、新浪微博)葛布林大帝

*/

 

目錄:

一. 最好的命名實踐

二. Property和執行個體變數(Ivar)的最佳實務

三. 分類(Categories)

四. 關聯引用(Associative References)

五. Weak Collections 

六. NSCache 

七. NSURLComponents 

八. CFStringTransform 

九. instancetype

十. Base64 和 Percent編碼

十一. -[NSArray firstObject]

十二. 總結

十三. 更多閱讀

 

一、最好的命名實踐

在iOS開發裡,命名規範極其重要。在下面的部分,我們將學習如何正確命名各種條目,以及為什麼這樣命名。

 

1. 自動變數

Cocoa是動態類型的語言,你很容易對所使用的類型感到困惑。集合(數組、字典等等)沒有關聯它們的類型,所以這樣的意外很容易發生:

1 NSArray *dates = @[@”1/1/2000”];2 NSDate *firstDate = [dates firstObject];

編譯器沒有警告,但當你使用firstDate時,它很可能會報錯(an unknown selector exception)。錯誤是調用一個string dates數組。這個數組應該調用dateStrings,或者應該包含NSDate對象。這樣小心的命名將會避免很多令人頭痛的錯誤。

 

2. 方法

1)方法名應該清楚表明接收和返回的類型

例如,這個方法名是令人困惑的:

1 - (void)add; // 令人困惑

看起來add應該帶一些參數,但它沒有。難道它是增加一些預設對象?

這樣命名就清楚多了:

1 - (void)addEmptyRecord;2 - (void)addRecord:(Record *)record;

現在addRecord:接收一個Record參數,看起來清楚多了。

 

2)對象的類型應符合名稱,如果類型和名稱不匹配,則容易弄混

這個例子展示了一個常見錯誤:

1 - (void)setURL:(NSString *)URL; // 錯誤的

這裡錯誤是因為調用setURL時,應該接收一個NSURL,而不是一個NSString。如果你需要string,你需要增加一些指示讓它更明朗:

1 - (void)setURLString:(NSString *)string;2 - (void)setURL:(NSURL *)URL;

這個規則不應過度使用。如果類型很明顯,別添加類型資訊到變數上。一個叫做name的屬性就比叫做nameString的屬性更好。

  

3)方法名也有與記憶體管理和KVC相關的特定原則

雖然ARC使得其中的一些規則不再重要,但在ARC與非ARC進行互動時(包括Apple架構的非ARC代碼),不正確的命名規則仍會導致非常具有挑戰性的錯誤。

方法名應該永遠是小寫字母開頭,駝峰結構。

如果一個方法名以alloc、new、copy或者nutableCopy開頭,調用者擁有返回的對象。如果你的property的名字像newRecord這樣,這個規則可能會導致問題,請換一個名字。

get方法的開頭應該返回一個參照值,例如:

1 - (void)getPerson:(Person **)person;

不要使用get首碼作為property accessor的一部分,property name的getter應該為-name。

 

 

二、Property和執行個體變數(Ivar)的最佳實務

Property應該代表一個對象的狀態,Getter應該沒有外部影響(它們可以具有內部影響,例如caching,但那些應該是調用者不可見的)。

避免直接存取執行個體變數,使用accessor來代替。

在早期的ARC裡,引起bug最常見的原因就是直接存取執行個體變數。開發人員沒有正確的retain和release執行個體變數,它們的應用就會崩潰或者記憶體泄露。由於ARC自動管理retain和release,一些開發人員認為這個規則已經不再重要,但仍還有其他使用accessors的原因:

  • KVO
    • 也許使用accessor的最關鍵原因是,property可以被觀察到。如果你不使用accessor,你需要在每次修改property裡的執行個體變數時調用willChangeValueForKey: 和 didChangeValueForKey: ,而accessor會在需要時自動調用這些方法。
  • Side effects
    • 在setter裡,你或者你的子類可能包含side effects。通知可能被傳送、事件可能被註冊到NSUndoManager裡,你不應該繞過這些side effects,除非它是必要的。
  • Lazy instantiation
    • 如果一個property被lazily instantiated,必須使用accessor來確保它的正確初始化。
  • Locking
    • 如果引進locking到一個property裡來管理多線程代碼,直接存取執行個體變數將違背你的lock,並可能導致程式崩潰。
  • Consistency
    • 在看到前面的內容後,有人可能會說應該只使用accessor,但這使得代碼很難維護。懷疑和解釋每一個直接存取的執行個體變數,而不是記住哪些需要accessor哪些不需要,這樣使得代碼更容易審核、審閱和維護。Accessor,特別是synthesized accessors,已經在OC裡被高度最佳化,它們值得使用。

 

這就是說,你不應該在這幾個地方使用accessor:

  • Accessor內部
    • 顯然,你不能在accessor內部使用自身。通常,你也不想在getter和setter內部使用它們自己(這可能建立無限迴圈),一個accessor應該訪問其自身的執行個體變數。
  • Dealloc
    • ARC極大地減少了dealloc,但它有時仍會出現。最好調用dealloc裡的外部對象,該對象可能處於不一致的狀態,並很可能造成混淆。
  • Initialization
    • 類似dealloc,對象可能在初始化過程中處於不一致狀態,你不應該在此時銷毀通知或者其他的side effects。

 

三、 分類(Categories)

分類允許你在運行中的類裡添加方法。任何類(甚至是由Apple提供的Cocoa類)都可以通過分類來拓展,這些新方法對類的所有執行個體都是可用的,分類聲明如下:

1 @interface NSMutableString (PTLCapitalize)2 - (void)ptl_capitalize;3 @end

PTLCapitalize是分類的名稱,注意這裡沒有聲明任何執行個體變數。

分類不能聲明執行個體變數,也不能synthesize properties。

分類可以聲明properties,因為它只是聲明方法的另一種方式。

分類不能synthesize properties,因為這會建立一個執行個體變數。

 

1. +load

分類在運行時附加到類,這可能定義分類為動態載入,所以分類可以很晚添加(雖然你不能在iOS裡編寫自己的動態庫,但系統架構是動態載入的,並且包括分類)。OC提供了一個名為 +load 的東西,在分類首次附加時運行。隨著 +initialize,你可以使用它來實現指定分類的設定,例如初始化靜態變數。你不能安全的在分類裡使用+initialize,因為類可能已經實現它。如果有多個分類實現+initialize,那麼運行一個沒有意義。

我希望你已經準備好要問一個顯而易見的問題:“如果分類不能使用+initialize,因為他們可能與其他分類衝突,那麼多個分類實現+load呢?”這正是OC runtime神奇的地方之一, +load方法是runtime的特例,是每一個分類能實現它,並且所有的實現都運行。當然,你不應該嘗試手動調用+load。

 

四、關聯引用(Associative References)

關聯引用允許你附加key-value資料到任何對象。這個能力有多種用途,但最常用的是允許你的分類添加資料的property。

考慮一個Person類的情況,你想使用分類來添加一個叫做emailAddress的新property。也許你在其他程式裡使用Person類,並且有時使用email address而有時不用,因此使用分類是可以避免開銷的很好解決方案。或者,你沒有自己的Person類,並且維護者不會為你添加property,你該如何解決這個問題?首先來看一下基礎的Person類:

1 @interface Person : NSObject2 @property (nonatomic, readwrite, copy) NSString *name;3 @end4 5 @implementation Person6 @end

現在你可以添加新的property了,在分類裡使用關聯引用:

 1 #import <objc/runtime.h> 2 @interface Person (EmailAddress) 3 @property (nonatomic, readwrite, copy) NSString *emailAddress; 4 @end 5  6 @implementation Person (EmailAddress) 7 static char emailAddressKey; 8  9 - (NSString *)emailAddress {10  return objc_getAssociatedObject(self, &emailAddressKey);11 }12 13 - (void)setEmailAddress:(NSString *)emailAddress {14  objc_setAssociatedObject(self, &emailAddressKey, emailAddress, OBJC_ASSOCIATION_COPY);16 } 17 @end

注意關聯引用是基於key的記憶體位址,而不是它的值。emailAddressKey裡儲存什麼並不重要,它只需要有一個唯一、不變的地址,這就是為什麼它通常使用未分配的static char作為key。

關聯引用有很好的記憶體管理,用以參照objc_setAssociatedObject的參數傳遞正確處理copy、assign或者retain。當相關的對象被deallocated,它們會released。這實際上意味著在另一個對象被銷毀時,你可以使用相關的對象進行追蹤,例如:

 1 const char kWatcherKey; 2  3 @interface Watcher : NSObject 4 @end 5  6 #import <objc/runtime.h> 7  8 @implementation Watcher 9 - (void)dealloc {10      NSLog(@"HEY! The thing I was watching is going away!");11 }12 @end13 ...14 NSObject *something = [NSObject new];
15 objc_setAssociatedObject(something, &kWatcherKey, [Watcher new], OBJC_ASSOCIATION_RETAIN);

這種技術對於調試非常有用,同時也可用於非調試任務,例如執行清理。

使用關聯引用是附加相關對象到alert panel或者control的好方法,例如你可以附加一個“represented object”到alert panel,代碼如下:

 1 ViewController.m (AssocRef) 2 id interestingObject = ...; 3 UIAlertView *alert = [[UIAlertView alloc] 4                                  initWithTitle:@"Alert" message:nil 5                                  delegate:self 6                                  cancelButtonTitle:@"OK" 7                                  otherButtonTitles:nil]; 8 objc_setAssociatedObject(alert, &kRepresentedObject, 9                          interestingObject,10 [alert show];

許多程式在調用裡使用執行個體變數處理這個任務,但關聯引用更簡潔。對於那些熟悉Mac的開發人員,這些代碼類似於representedObject

,但卻更靈活。

聯想引用的一個限制是,它們沒有與encodeWithCoder:整合,因此它們很難通過一個分類來序列化。

 

 

五、Weak Collections

大多數Cocoa的集合例如NSArray、NSSet和NSDictionary都具有強大功能,但它們不適合某些情況。NSArray與NSSet會保留你儲存進去的對象,NSDictionary會儲存value和key,這些行為通常是你想要的,但對於某些工作它們並不適合。幸運的是,自從iOS6開始,一些其他的集合開始出現:NSPointerArray、NSHashTable與NSMapTable。它們統稱為Apple文檔的指標集合類(pointer collection classes),並且有時使用NSPointerFunctions類來進行配置。

NSPointerArray類似NSArray, NSHashTable類似NSSet,而NSMapTable類似NSDictionary。每個新的集合類都可以配置為保持弱引用,指向Null 物件或者其他異常情況。NSPointerArray的一個額外好處是它還可以儲存NULL值。

指標集合類可以使用NSPointerFunctions來廣泛的配置,但大多數情況下,它只是簡單的傳送一個NSPointerFunctionsOptions flag到–initWithOptions:。最常見的情況,例如+weakObjectsPointerArray,有自己的建構函式。

 

六、 NSCache

NSCache有幾個被低估的功能,比如事實上它是安全執行緒的,你可能在任何無鎖的線程裡改變一個NSCache。NSCache也被設計來融合對象遵從<NSDiscardableContent>,其中最常見的類型是NSPurgeableData,通過調用beginContentAccess 與 endContentAccess,你可以控制何時安全放棄這個對象。這不僅在你的應用運行時提供自動緩衝管理,它甚至有助於你的應用被暫停。通常情況下,當記憶體緊張時,記憶體警告沒有釋放出足夠的記憶體,iOS會開始殺死暫停在背景應用。在這種情形下,你的應用沒有得到delegate資訊,就這樣被殺死。不過如果你使用NSPurgeableData,iOS會釋放這塊記憶體給你,即使你的應用被暫停。

想得到

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.