編寫OC高品質的代碼的有效方法,編寫oc高品質

來源:互聯網
上載者:User

編寫OC高品質的代碼的有效方法,編寫oc高品質
1. 寫這個只是為了自己記憶,有相關pdf檔案,如需要留下郵箱。。2. 在類的標頭檔中盡量少引入其他標頭檔

  • 除非確有必要,否則不要引入標頭檔。一般來說,應在某個類的標頭檔中使用向前聲明來提及別的類(使用@class),並在實現檔案中引入那些類的標頭檔,這樣做可以盡量降低類之間的耦合。
  • 如果要聲明某個類遵循某個協議,應該把這個協議放到分類中,或者把協議單獨放在一個標頭檔中,然後將其引入。
3. 多用字面量文法,少用與之等價的方法

  下面是兩種方式的對比:

// 使用字面量文法的例子NSArray *array1 = @[@"1",,@"2"];NSNumber *number1 = @1;NSDictionary *dictionary1 = @{@"key":@"value"};// 使用與之對應的方法NSArray *array2 = [NSArray arrayWithObjects:@"1",@"2",nil];NSNumber *number2 = [NSNumber numberWithInt:2];NSDictionary *dictionary2 = [NSDictionary dictionaryWithWithObjectsAndKeys:@"value":@"key"];
  •  使用字面量文法來建立字串、數值、數組、字典。與常規方法相比,更加簡明扼要
  • 應該通過取下標操作來訪問數組下標或字典中的鍵所對應的元素
  • 使用字面量文法建立數組或字典時,若值中有nil,則會拋出異常,因此,需確保值裡面不含nil
4. 多用類型常量,少用#define預先處理指令

定義一個常量的方法:

// 第一種:預先處理指令#define ANIMATION_DURATION 0.3// 第二種:定義靜態常量static const NSTimeInterval kAnimationDuration = 0.3

 我們一般推薦使用第二種,這個方式定義的常量包含類型資訊,有助於代碼閱讀。

注意:常量命名法是:若常量局限於“編譯單元”(也就是實現檔案,.m檔案)之內,則在前面加字母k;若常量在類之外可見,則通常以類名為首碼。

如我們需要對外公布某個常量,我們可以寫成下面的代碼:

// Test.h#import <Foundation/Foundation.h>extern NSString *const TestDidChangeNotification;@interface Test : NSObject@end// Test.m#import "Test.h"NSString *const TestDidChangeNotification = @"TestDidChangeNotification";@implementation Test
  •  不要用預先處理指令定義常量。這樣定義出來的常量不含類型資訊,編譯器只是會在編譯前根據此執行尋找和替換。即使有人重新定義了常量值,編譯器也不會有警告,這將導致應用程式中的常量值不一致
  • 在.m檔案中使用 static const 來定義“編譯單元內可見常量”,無需加類名首碼,加k
  • 在標頭檔中使用 extern 來聲明全域常量,並在相關實現檔案中定義其值,這種常量要加類名首碼。
5. 用枚舉來表示狀態、選項、狀態代碼
  • 使用枚舉來表示狀態機器的狀態、傳遞給方法的選項以及狀態代碼等值,給這些值起個易懂的名字
  • 用NS_ENUM 與 NS_OPTIONS 宏來定義枚舉類型,並指明其底層資料類型。
  • 在處理枚舉類型的switch語句中不要事先default分支,這樣的話,加入新枚舉之後,編譯器就會提示開發人員:switch語句並未處理所有枚舉
6. 理解“屬性”這一概念
  • 使用@property文法來定義對象中所封裝的資料
  • 通過“特質”屬性關鍵字來指定儲存資料所需的正確語義
  • 在設定屬性所對應的執行個體變數時,一定要遵從該屬性所聲明的語義。
7. 在對象內部盡量直接存取執行個體變數

比如,Person類有個name屬性,我們在這個類的內部想擷取這個name屬性的資料的時候,一種是通過 self.name,一種是 _name.

這兩種的區別:

  • 直接存取執行個體變數的速度比較快,編譯器所產生的程式碼會直接存取儲存對象執行個體變數的那塊記憶體
  • 直接存取執行個體變數,不會調用其“設定方法”,這就繞過了為相關屬性所定義的“記憶體管理語義”,比如,在ARC下直接存取一個聲明為copy的屬性,那麼並不會拷貝該屬性,只會保留新值,釋放舊值
  • 如果直接存取執行個體變數,那麼不會觸發“KVO”,這樣做是否會產生問題,取決於具體的對象行為。
  • 通過屬性來訪問有助於排查與之相關的錯誤,因為可以給“擷取方法”或“設定方法”中新增“斷點”,監控該屬性的調用者及其訪問時機。

注意點:

  • 在對象內部讀取資料時,應該直接通過執行個體變數來讀,而寫入資料時,則應通過屬性來寫
  • 在初始化方法及dealloc方法中,總是應該直接通過執行個體變數來讀寫資料
  • 有時候會使用惰性初始化技術配置某份資料,這種情況下,需要通過屬性來讀取資料
8. 理解“對象等同性”這一概念
  • 若想檢測對象的等同性,請提供“isEqual:”與hash方法
  • 相同的對象必須具有相同的雜湊碼,但是兩個雜湊碼相同的對象卻未必相同
  • 不要盲目的逐個檢測每條屬性,而是根據具體需求來指定方案
9. “以類族模式”隱藏實現細節

“類族”是一種很有種的模式,可以隱藏“抽象基類”背後的實現細節。OC的系統架構中普遍使用此模式,比如有一個處理僱員的類,每個僱員都有“名字”和“薪水”這兩個屬性,管理者可以命令其執行日常工作,但是各種僱員的工作內容卻不同,經理在帶領僱員做項目時,無需關係每個人如何完成其具體工作,僅需指示其開工就行。我們重構多個子類,把每個人完成具體工作的方法,在子類實現。

首先定義一個抽象基類:

typedef NS_ENUM(NSUInteger, EOCEmployeeType){    EOCEmployeeTypeDeveloper,    EOCEmployeeTypeDesigner,    EOCEmployeeTypeFinance     }@interface EOCEmployee : NSObject@property (copy, nonatomic) NSString *name;@property (assign, nonatomic) NSInteger salary;// 建立一個僱員對象+(EOCEmployee*)employeeWithType:(EOCEmployeeType)type;// 讓僱員工作- (void)doADaysWork;@implementation EOCEmployee+ (EOCEmployee *)employeeWithType:(EOCEmployeeType)type{    switch (type){          case EOCEmployeeTypeDeveloper:                  return [EOCEmployeeTypeDeveloper new];                  break;          case EOCEmployeeTypeDeveloper:                  return [EOCEmployeeTypeDesigner new];                  break;           case EOCEmployeeTypeDeveloper:                  return [EOCEmployeeTypeFinance new];                  break;    }  }- (void)doADayWork{  // 子類去實現}@end

 然後,每個“實體子類”都從基類繼承而來,例如:

@interface EOCEmployeeDeveloper : EOCEmployee@end@implementation EOCEmployeeDeveloper- (void)doADaysWork{   [self wirteCode];}@end

 在本例中,基類實現了一個“類方法”,該方法根據待建立的僱員類別分配好對應的僱員類執行個體,這種“原廠模式”是建立類族的辦法之一。

如果對象所屬的類位於某個類族中,你可能覺得自己建立了某個類的執行個體,然而實際上建立的卻是其子類的執行個體。

OC中的NSNumber、NSArray等都是類族。

  • 類族模式可以把實現細節隱藏在一套簡單的公用介面後面。
  • 系統架構中經常使用類族
  • 從類族的公用抽象基類中整合子類時要當心,若有開發文檔,應先閱讀。
10. 在既有類中使用關聯對象存放自訂資料

有時候需要在對象中存放相關資訊,這時候我們通常都會從對象所屬類中繼承一個子類,然後改用這個子類對象,但是有時候類的執行個體可能是由某種機制所建立的,而開發人員無法令這種機制建立出自己所寫的子類執行個體。OC中有一項強大的特性可以解決,就是“關聯對象”。

基於runtime來實現,此處就不多說。

  • 可以通過“關聯對象”機制來把兩個對象連起來。
  • 定義關聯對象時可指定記憶體管理語義,用以模仿定義屬性時所採用的“擁有關係”與“非用有關係”
  • 只有在其他做法不可行時才應該選用關聯對象,這種做法通常會引入難於尋找的bug
 11. 理解objc_msgSend的作用

在對象上調用方法是OC中經常使用的功能。專業術語叫做:“傳遞訊息”。訊息有“名稱”或“選擇子”,可以接受參數,而且可能還有傳回值。

C語言使用“靜態繫結”,在編譯期就能決定運行時所應調用的函數。

OC中使用“動態綁定”,對象接收到訊息之後,究竟該調用哪個方法則完全於運行期決定,甚至可以在程式運行時改變。

這裡就不多解釋objc_msgSend的使用,如有需要可以看runtime的使用。

objc_msgSend 函數會根據接收者和選擇子的類型來調用適當的方法,為了完成此操作,該方法需要在接收者所屬的類中找到其“方法列表”,如果能找到與選擇子名稱相符的方法,就跳至其實現代碼,如果找不到,那就沿著繼承體系向上尋找,如果最終沒找到,則執行“訊息轉寄”操作。每個類都會有一塊緩衝,用來緩衝方法,若是稍後還向該類發送與選擇子相同的訊息,那麼執行起來就會很快了。

  • 訊息由接收者,選擇子及參數構成。給某對象“發送訊息”,也就相當於在該對象上“調用方法”
  • 發給某對象的全部訊息都要由“動態訊息派發系統”來處理,該系統會查出對應的方法,並執行其代碼。
12. 理解訊息轉寄機制

當對象接收到無法解讀的訊息後,就會啟動“訊息轉寄”機制,程式員可經由此過程告訴對象應該如何處理未知訊息。

如果在控制台中看到 unrecognized selector sent to instance 0x87 就說明你曾向某個對象發送過一條其無法解讀的訊息,從而啟動了訊息轉寄機制,然後以程式崩潰而告終。

訊息轉寄分為兩個階段:

動態方法解析:

對象在收到無法解讀的訊息後,首先將調用其所屬類的下列類方法:

// 如果該類調用了一個沒有實現的執行個體方法,會調用此方法
+ (BOOL)resolveInstanceMethod:(SEL)selector
// 如果該類調用了一個沒有實現的類方法,會調用此方法
+ (BOOL)resolveClassMethod;

 該方法的參數就是那個未知的選擇子,其傳回值為Boolean類型,表示這個類是否能新增一個執行個體方法用以處理此選擇子。在繼續往下執行轉寄機制之前,我們可以使用runtime動態增加這個方法。

使用這種辦法的前提是:相關方法的實現代碼已經寫好,只等著啟動並執行時候動態插在類裡面就可以了。

備用接收者:

當前接收者還有第二次機會能處理未知的選擇子,在這一步中,運行期系統會問它:能不能把這條訊息轉給其他接收者來處理:

// 方法參數代表未知的選擇子,若當前接收者能夠找到備援對象,則將其返回,如果找不到就返回nil。- (id)forwardingTargetForSelector:(SEL)selector;

 我們可以用“組合”來類比出“多重繼承”的某些特性,在一個對象內部,可能還有其它一系列對象,該對象可經由此方法將能夠處理某選擇子的相關內部對象返回,這樣的話,在外界看來,好像是由該對象親自處理的。

完整的訊息轉寄:

如果轉寄已經來到這一步的話,那麼唯一能做的就是啟用完整的訊息轉寄機制了,系統會建立NSInvocation 對象,把與尚未處理的那條訊息有關的全部細節都封裝於其中,此對象包含選擇子、目標(target)及參數,在觸發NSInvocation對象時,“訊息派發系統”將親自出馬,把訊息指派給目標對象。

此步驟會調用下列方法來轉寄訊息:

// 該方法可以實現的很簡單,只需要改變調用目標,是訊息在新目標上得以調用即可,然而這樣實現出來的方法與“備援接收者”方案所實現的方法等效,所以很少有人採用這麼簡單的實現方法,比較有用的實現方式為:在觸發訊息前,先以某種方式改變訊息內容,比如追加另外一個參數,或是改換選擇子等等。- (void)forwardInvocation:(NSInvocation *)invocation;

 實現此方法時,若發現某叫用作業不應由本類處理,則需要調用超類的同名方法。這樣的話,繼承體系中的每個類都有機會處理此調用請求,直到NSObject,如果最後調用了NSObject類的方法,那麼該方法還會繼續調用“doesNotRecognizeSelector”,以拋出異常,此異常表明選擇子最終未能得到處理。

訊息轉寄全流程:

接收者在每一步中均有機會處理訊息,步驟越往後,處理訊息的代價就越大,最好能在第一步處理完,這樣的話,運行期系統就可以將此方法緩衝起來了,如果這個類的執行個體稍後還收到同名選擇子,那麼根本無需啟動訊息轉寄流程。如果想在第三步裡把訊息轉給備援的接收者,那還不如把轉寄操作提前到第二步。因為第三步只是修改了調用目標,這項改動放在第二部執行會更為簡單,不然的話,還得建立並處理完整的NSInvocation。

  • 若對象無法響應某個選擇子,則進入訊息轉寄流程。
  • 通過運行期的動態方法解析功能,我們可以在需要用到某個方法時再將其加入類中。
  • 對象可以把其無法解讀的某些選擇子轉交給其它對象來處理
  • 經過上述兩步之後,如果還是沒有辦法處理選擇子,那就啟動完整的訊息轉寄機制。

http://www.cocoachina.com/ios/20150604/12013.html 相關的例子

13. 用“方法調配技術”調試“黑盒方法”

主要就是runtime的方法交換,runtime具體可見OC類目中關於runtime的介紹。

我們在這裡簡單的分析下:

類的方法列表會把選擇子的名稱映射到相關的方法實現直上,使得“動態訊息派發系統”能夠據此找到應該調用的方法,這些方法均以函數指標的形式來表示,這種指標叫做IMP,其原型如下:

id (*IMP)(id,SEL,...)

比如,NSString 類可以相應lowercaseString、uppercaseString、capitalizedString等選擇子。這張映射表中的每個選擇子都映射到了不同的IMP之上:

OC運行期系統提供的幾個方法都能夠用來操作這張表,開發人員可以向其中新增選擇子,也可以改變某選擇子所對應的方法實現,還可以交換兩個選擇子所映射到的指標,比如我們交換 lowercaseString 和 uppercaseString 的方法實現,類的方法表就會變成以下這個樣子:

在新的映射表中,我們可以看到交換了lowercaseString 和 uppercaseString 的方法實現,並且多了一個名為newSelector的選擇子,上述修改均無需編寫子類,只要修改了“方法表”的布局,就會反映到程式中所有的NSString執行個體之上。

通過此方案,開發人員可以為那些“完全不知道其具體實現”的黑盒方法增加日誌記錄功能,這有助於程式調試。

  • 在運行期,可以向類中新增或替換選擇子所對應的方法實現。
  • 使用另一份實現來替換原有的方法實現,這道工序叫做“方法調配”,也就是方法交換,開發人員常用此技術向原有實現中添加新功能。
  • 一般來說,只有偵錯工具的時候才需要在運行期修改方法實現,這種做法不宜濫用。
14. 理解“類對象”的用意

物件類型並非在編譯期就綁定好了,而是要在運行期尋找。而且,還有個特殊的類叫做id,它能指代任意的OC物件類型,一般情況下,應該指明訊息接收者的具體類型,這樣的話,如果向其發送了無法解讀的訊息,那麼編譯器就會產生警告資訊,而類型為id的對象則不然,編譯器嘉定它能夠響應所有的訊息。

“在運行期檢視物件類型”,這個操作也叫做“類型資訊查詢”(內省),這個強大而有用的特性內建於Foundation架構的NSObject協議裡,凡是由公用根類(common root class)繼承而來的對象都要遵從此協議。在程式中不要直接比較對象所屬的類,明智的做法是調用“類型資訊查詢方法”。

在此之前,我們看下OC對象的本質是什嗎?

每個OC對象執行個體都是指向某塊記憶體資料的指標,所以在聲明變數時,類型後面要跟一個“*”字元,如下:

// pointerVariable可以理解成存放記憶體位址的變數,而NSString 自身的資料就儲存於那個地址中,因此可以說,該變數”指向“NSString 執行個體。所有OC對象都是如此,NSString *pointerVariable = @"Some string";

 描述OC對象所用的資料結構定義在運行期程式庫的標頭檔裡,id類型本身也定義在這裡:

typedef struct objc_object{    Class isa;}*id;

 每個對象,結構體的首個成員是Class類的變數。該變數定義了對象所屬的類,通常稱為“is a”指標,例如,剛才的例子中所有的對象“是一個”(is a)NSString,所以其“is a”指標就指向NSString。Class對象也定義在運行期程式庫的標頭檔中:

typedef stuct objc_class *Class;struct objc_class{    Class isa;    Class super_class;    const char *name;    long version;    long info;    long instance_size;    struct objc_ivar_list *ivars;    struct objc_method_list *methodList;    struct objc_cache *cache;    struct objc_protocol_list *protocols;}

 此結構體存放類的“中繼資料”,例如類的執行個體實現了幾個方法,具備多少個執行個體變數等資訊。此結構體的首個變數也是isa指標,這說明Class本身亦為OC對象。結構體裡還有個變數為super_class,它定義了本類的超類。類對象所屬的類型(也就是isa指標所指向的類型),是另外一個類,叫做“元類”,用來表述類對象本身所具備的中繼資料。“類方法”就定義於此處,因為這些方法可以理解成類對象的執行個體方法。每個類僅有一個“類對象”,而每個“類對象”僅有一個與之相關的“元類”。

super_class 指標確立了繼承關係,而isa指標描述了執行個體所屬的類。

  • 每個執行個體都有一個指向Class對象的指標,用以表明其類型,而這些Class對象則構成了類的繼承體系。
  • 如果物件類型無法再編譯期確定,那麼就應該使用類型資訊查詢方法來彈指
  • 盡量使用類型資訊查詢方法來確定物件類型,而不要直接比較類對象,因為某些對象可能實現了訊息轉寄功能。
15. 用首碼避免命名空間衝突

應該為所有的名稱都加上適當的首碼,比如,你所在的公司焦作Effective Widgets,那麼就可以在公用部分代碼中使用EWS做首碼,如果有些代碼只用於Effective Browser的瀏覽器項目中,可以使用EWB作首碼。

首碼最好是三個字母的,因為Apple宣稱其保留使用所有“兩字母首碼”。

  • 選擇與你的公司,應用程式或二者皆有關聯之名稱作為類名的首碼,並在所有代碼中使用這一首碼
  • 若自己所開發的程式庫中用到了第三方庫,則應為其中的名稱加上首碼。
16. 提供“全能初始化方法” 

UITableViewCell,初始化該類對象時,需要指明其樣式及標示符,標示符能夠區分不同類型的儲存格,由於這種對象的建立成本較高,所以繪製表格時可依照標示符來複用,以提升程式效率,我們把這種可為對象提供必要資訊以便其能完成工作的初始化方法叫做“全能初始化方法”。

// 比如建立一個NSDate- (id)init;- (id)initWithString:(NSString *)string;- (id)initWithTimeIntervalSinceNow:(NSTimeInterval)seconds;- (id)initWIthTimeIntervalSinceRefrenceDate:(NSTimeInterval)seconds;

 第四個方法是全能初始化方法,也就是說其餘的初始化方法都要調用它,當底層資料存放區機制改變時,只需修改此方法的代碼。

  • 在類中提供一個全能初始化方法,並在文檔裡指明。其它初始化方法均應調用此方法
  • 若全能初始化方法與超類不同,則需要複寫超類中的對應方法。
  • 如果超類的初始化方法不適用子類,那麼應該複寫這個超類方法,並在其中拋出異常。
17. 實現description方法

偵錯工具的時候,經常需要列印並查看對象資訊,我們可以重寫該對象的description方法,如下:

  • 實現description方法返回一個有意義的字串,用以描述該執行個體
  • 若想在調試時列印出更詳盡的對象描述資訊,則應實現debugDescription方法
18. 盡量使用不可變對象

設計類的時候,應充分運用屬性來封裝資料,盡量把對外公布出來的屬性設為唯讀,而且只在確有必要時才將屬性對外公布。

  • 盡量建立不可變的對象
  • 若某屬性僅可於對象內部修改,則在.m檔案中,則將其由readonly變成readwrite屬性。
  • 不要把可變的collection作為屬性公開,而應提供相關方法,以此修改對象中的collection
19. 使用清晰而協調的命名方式

給方法命名時注意事項:

  • 如果方法的傳回值是新建立的,那麼方法名的某個詞應該是傳回值的類型,除非還有修飾語,如:localizedString。屬性的存取方法不遵循這種命名方式。
  • 應該把表示參數類型的名詞放在參數前面。
  • 如果方法要在當前對象上執行操作,那麼應該包含動詞。
  • 不要使用str這種簡稱,使用全程。
  • Boolean屬性應加is首碼。如果某方法返回非屬性的Boolean值,那麼應該根據其功能,選用has或is當首碼。
  • 將get這個首碼留給那些藉由”輸出參數“來儲存傳回值的方法。

總結:

  • 起名時應遵從標準的OC命名規範,這樣建立出來的介面更容易為開發人員所理解。
  • 方法名要言簡意賅
  • 方法名不要使用縮減後的類型名稱
  • 給方法起名時的第一要務就是確保其風格與你自己的代碼或所要繼承的架構相符。
20. 為私人方法名加首碼

一個類所做的事情通常都要比從外面看到的更多,編寫類的實現代碼時,經常要寫一些在內部使用的方法。應該為這種方法的名稱加上某些首碼,這有助於調試,因為據此很容易就能把公用方法和私人方法區別開。

具體使用何種首碼,可根據個人喜好來定,其中最好包含底線和字母p,比如p_method。不要使用 _method,因為Apple公司喜歡單用一個底線做私人方法的首碼,可能會引起衝突。

  • 給私人方法的名稱加上首碼,這樣可以很容易地將其同公用方法區分開
  • 不要單用一個底線做私人方法的首碼,因為這種做法是留給蘋果公司用的。
21. 理解OC錯誤模型
  • 只有發生了可使整個應用程式崩潰的嚴重錯誤時,才使用異常。
  • 在錯誤不嚴重的情況下,使用NSError
22. 理解NSCopying協議
  • 若想讓自己所寫的對象具有拷貝功能,則需要實現NSCopying協議
  • 如果自訂的對象分為可變和不可變,那麼就要同時實現NSCopying和NSMutableCopying協議
  • 複製對象時需決定採用淺拷貝還是深拷貝,一般情況下執行淺拷貝
23. 通過委託與資料來源協議進行對象間通訊

委託模式:定義一套介面,某對象若想接受另一個對象的委託,則需要實現這個介面,以便成為其"委派物件",而這”另一個對象“則可以給其委派物件回傳一些資訊,也可以在發生相關事件時通知委派物件。

  • 委託模式為對象提供了一套介面,使其可由此將相關事件告知其它對象
  • 將委派物件應該支援的介面定義成協議,在協議中把可能需要處理的事件定義成方法
  • 當某對象需要從另外一個對象中擷取資料時,可以使用委託模式,比如 tableView的dataSource
  • 如果有必要,可實現含有位段的結構體,將委派物件是否能響應相關協議方法這一資訊緩衝下來,比如,聲明一個屬性,記錄是否實現了某個方法。
24. 將類的實現代碼分散到便於管理的數個分類之中
  • 使用分類機制把類的實現代碼劃分成易於管理的小塊
  • 將應該視為”私人“的方法歸入名叫Private的分類中,隱藏實現細節。
25. 總是為第三方類的分類名稱加首碼

比如你想給系統類別添加個方法,如果你沒有添加首碼的話,可能會覆蓋其方法。

  • 向第三方類中添加分類時,總應給其名稱加上你專用的首碼。
  • 給其中的方法名加上你專用的首碼。
26. 不要再分類中聲明屬性
  • 把封裝資料所用的全部屬性都定義在主介面裡
  • 在分類中,可以定義存取方法,但盡量不要定義屬性。
27. 使用 "class-continuation分類"隱藏實現細節

"class-continuation分類"和普通的分類不同,它必須定義在其所接續的那個累的實現檔案裡。其重要之處在於,這是唯一能夠聲明執行個體變數的分類,而且此分類沒有特定的實現檔案,其中的方法都應該定義在類的主實現檔案裡。而且,和其它分類不同,它沒有名字,比如:

@interface Person ()// Methods here@end
  •  通過“class-continuation分類”向類中新增執行個體變數
  • 如果某屬性在主介面中聲明為唯讀,而類的內部又要用設定方法修改此屬性,那麼就在“class-continuation分類”中將其擴充為“可讀寫”
  • 把私人方法的原型聲明在“class-continuation分類”裡面
  • 若想讓類所遵循的協議不為人所知,則可於“class-continuation分類”中聲明。
28. 通過協議提供匿名對象

如下面的代碼:

@property (nonatomic, weak) id <WCEDelegate> delegate;

由於該屬性的類型id<EOCDelegate>,所以實際上任何類的對象都能充當這一屬性,對於具備此屬性的類來說,delegate就是”匿名的“。

  • 協議可在某種程度上提供匿名型別。具體的物件類型可以淡化成遵從某協議的id類型,協議裡規定了對象所應實現的方法
  • 使用匿名對象來隱藏類型名稱
  • 如過具體類型不重要,重要的是對象能夠響應(定義在協議裡的)特定方法,那麼可使用匿名對象來表示。
29. 理解引用計數
  • 引用計數機制通過可以遞增遞減的計數器來管理記憶體。對象建立好之後,其保留計數至少為1.若保留計數為正,則對象繼續存活,當保留計數將為0時,對象就銷毀了
  • 在對象聲明期中,其餘對象通過引用來保留或釋放此對象,保留和釋放操作分別會遞增及遞減保留計數
30. ARC注意事項
  • 在ARC之後,程式員就無需擔心記憶體管理問題了
  • 不要手動管理
  • CoreFoundation對象不歸ARC管理,開發人員必須適時調用CFRetain/CFRelease.
31. 在dealloc方法中只釋放引用並解除監聽

對象在經曆其生命週期後,最終會為系統所回收,這時就要執行dealloc方法,在每個對象的生命週期內,此方法僅執行一次,也就是當保留計數為0的時候,然而具體何時執行,則無法保證。

在dealloc方法中,一般都是移除觀測行為,登出通知。

  • 在dealloc方法裡,應該做的事情就是釋放指向其它對象的引用,並取消原來訂閱的”kvo“或通知中樞的等通知,不要做其它事情
  • 如果對象持有檔案描述符等系統資源,那麼應該專門編寫一個方法來釋放此種資源。
  • 執行非同步任務的方法不應再dealloc裡,只能在正常狀態執行的哪些方法也不應在dealloc裡調用,因為此時對象已處於正在回收的狀態了。
32. 以弱引用避免循環參考

如果兩個對象,相互引用,那麼這兩個對象都無法被釋放,產生記憶體泄露。

unsafe_unretained 和 weak的區別:

當指向某個執行個體的引用移除後,unsafe_unretained屬性仍指向那個已經回收的執行個體,而weak屬性則指向nil。weak比unsafe_unretained應用可以令代碼更安全。

  • 當某些引用設為weak,可避免出現循環參考
  • weak引用可以自動清空,也可以不自動清空。
33. 自動釋放池
  • 自動釋放池排布在棧中,對象收到autorelease訊息後,系統將其放入最頂端的池裡
  • 合理運用自動釋放池,可降低應用程式的記憶體峰值
  • 使用@autoreleasepool
34. 為常用的block類型建立typedef

比如:

typedef void(^WCECompletionHander)(NSData *data);
  •  用typedef重新定義塊類型,可讓塊變數用起來更加簡單
  • 定義新類型時,應遵循命名規則
35. 使用block降低代碼分散程度
  • 在建立對象時,可以使用內聯的handler代碼塊將相關商務邏輯聲明
  • 比如網路請求一般使用代碼塊來回調資料

 

相關文章

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在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.