編寫OC高質量的代碼的有效方法

来源:https://www.cnblogs.com/chenjiangxiaoyu/archive/2018/01/13/8275816.html
-Advertisement-
Play Games

1. 寫這個只是為了自己記憶,有相關pdf文件,如需要留下郵箱。。 2. 在類的頭文件中儘量少引入其他頭文件 除非確有必要,否則不要引入頭文件。一般來說,應在某個類的頭文件中使用向前聲明來提及別的類(使用@class),併在實現文件中引入那些類的頭文件,這樣做可以儘量降低類之間的耦合。 如果要聲明某 ...


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 就說明你曾向某個對象發送過一條其無法解讀的消息,從而啟動了消息轉發機制,然後以程式崩潰而告終。

消息轉發分為兩個階段:

  1. 徵詢接收者,所屬的類,看其是否能動態添加方法,以處理當前這個“未知的選擇子(unknown selector)”,這叫做“動態方法解析”
  2. 第二階段,涉及“完整的消息轉發機制”,如果運行期系統已經把第一階段執行完了,那麼接收者自己就無法再以動態新增方法的手段來響應包含該選擇子的消息了。此時,運行期系統會請求接收者以其他手段來處理與消息相關的方法調用。這又分為兩小步:
    1. 首先,看接收者看看有沒有其他對象能否處理這條消息
    2. 如果有,則運行期系統會把消息轉給那個對象,於是轉發郭恆結束,如果沒有“備用的接收者”,則啟動完整的消息轉發機制,運行期系統會把與消息有關的全部細節都封裝到NSInvocation對象中,再給接收者最後一次機會,令其設法解決當前還未處理的這條消息。

動態方法解析:

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

// 如果該類調用了一個沒有實現的實例方法,會調用此方法
+ (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代碼塊將相關業務邏輯聲明
  • 比如網路請求一般使用代碼塊來回調數據

 


您的分享是我們最大的動力!

-Advertisement-
Play Games
更多相關文章
  • DirectSound是DirectX組件之一,提供了對音頻設備的捕獲和播放能力,同時它也是唯一幾個支持Xp系統的音頻技術之一。 DirectSound主要有以下特點: 優點: 播放音頻 低延遲 。 硬體資源控制 。 同時 播放 多個 聲音。 控制硬體緩衝區的使用 優先順序 (DirectSound使 ...
  • ELF全稱Executable and Linkable Format,可執行連接格式,ELF格式的文件最早用於存儲Linux程式,後演變到ARM系統上存儲ARM程式 ...
  • relocatable文件,即可重定向文件,這個文件是由編譯器彙編源文件(.c/.s)而成的。直接生成的重定向文件叫object file,經過封裝的重定向文件稱為library file。relocatable文件是一個中間的過渡文件,其本身也不能被ARM直接執行,需經過第二步轉換,即鏈接,所以這... ...
  • 一、系統環境 操作系統:Windows10專業版 64位Redis版本:redis-64.3.0.503 二、問題描述 1.命令行啟動: 可以啟動成功; 2.將Redis安裝為Windows系統服務: 3.進入系統服務頁面: Win + r打開運行命令框,services.msc打開系統服務頁面 4 ...
  • <?php $link = mysqli_connect("localhost","root","root","dbname"); //連接資料庫 $sql = "select field from dbname limit 1"; $ressql = mysqli_query($link,$sql ...
  • 連接資料庫: 輸入資料庫密碼即可登陸。 查看mysql版本信息: 查看當前時間: 實現Windows與Ubuntu虛擬機之間的文件互傳問題。 其實很簡單就是需要安裝一個VMware tools即可。 打開文件管理就有一個VM的tar包,沒有的話就需要多掛一個cd驅動器。 右鍵,設置, 我有兩個cd驅 ...
  • Note:這篇文章是基於Android Studio 3.01版本的,NDK是R16。step1:創建一個包含C++的項目其他預設就可以了。C++ Standard指定編譯庫的環境,其中Toolchain Default使用的是預設的CMake環境;C++ 11也就是C++環境。兩種環境都可以編庫,... ...
  • 1 Cookie、Session 和 Token 都是用來做持久化處理的,目的就是讓客戶端和服務端相互認識。Http 請求預設是不持久的沒有狀態的,誰也不認識誰。 2 Cookie: 是存放在客戶端的信息,伺服器通過響應頭 Set-Cookie 欄位給客戶端,如果 Cookie 已過期一般是會被清楚 ...
一周排行
    -Advertisement-
    Play Games
  • 移動開發(一):使用.NET MAUI開發第一個安卓APP 對於工作多年的C#程式員來說,近來想嘗試開發一款安卓APP,考慮了很久最終選擇使用.NET MAUI這個微軟官方的框架來嘗試體驗開發安卓APP,畢竟是使用Visual Studio開發工具,使用起來也比較的順手,結合微軟官方的教程進行了安卓 ...
  • 前言 QuestPDF 是一個開源 .NET 庫,用於生成 PDF 文檔。使用了C# Fluent API方式可簡化開發、減少錯誤並提高工作效率。利用它可以輕鬆生成 PDF 報告、發票、導出文件等。 項目介紹 QuestPDF 是一個革命性的開源 .NET 庫,它徹底改變了我們生成 PDF 文檔的方 ...
  • 項目地址 項目後端地址: https://github.com/ZyPLJ/ZYTteeHole 項目前端頁面地址: ZyPLJ/TreeHoleVue (github.com) https://github.com/ZyPLJ/TreeHoleVue 目前項目測試訪問地址: http://tree ...
  • 話不多說,直接開乾 一.下載 1.官方鏈接下載: https://www.microsoft.com/zh-cn/sql-server/sql-server-downloads 2.在下載目錄中找到下麵這個小的安裝包 SQL2022-SSEI-Dev.exe,運行開始下載SQL server; 二. ...
  • 前言 隨著物聯網(IoT)技術的迅猛發展,MQTT(消息隊列遙測傳輸)協議憑藉其輕量級和高效性,已成為眾多物聯網應用的首選通信標準。 MQTTnet 作為一個高性能的 .NET 開源庫,為 .NET 平臺上的 MQTT 客戶端與伺服器開發提供了強大的支持。 本文將全面介紹 MQTTnet 的核心功能 ...
  • Serilog支持多種接收器用於日誌存儲,增強器用於添加屬性,LogContext管理動態屬性,支持多種輸出格式包括純文本、JSON及ExpressionTemplate。還提供了自定義格式化選項,適用於不同需求。 ...
  • 目錄簡介獲取 HTML 文檔解析 HTML 文檔測試參考文章 簡介 動態內容網站使用 JavaScript 腳本動態檢索和渲染數據,爬取信息時需要模擬瀏覽器行為,否則獲取到的源碼基本是空的。 本文使用的爬取步驟如下: 使用 Selenium 獲取渲染後的 HTML 文檔 使用 HtmlAgility ...
  • 1.前言 什麼是熱更新 游戲或者軟體更新時,無需重新下載客戶端進行安裝,而是在應用程式啟動的情況下,在內部進行資源或者代碼更新 Unity目前常用熱更新解決方案 HybridCLR,Xlua,ILRuntime等 Unity目前常用資源管理解決方案 AssetBundles,Addressable, ...
  • 本文章主要是在C# ASP.NET Core Web API框架實現向手機發送驗證碼簡訊功能。這裡我選擇是一個互億無線簡訊驗證碼平臺,其實像阿裡雲,騰訊雲上面也可以。 首先我們先去 互億無線 https://www.ihuyi.com/api/sms.html 去註冊一個賬號 註冊完成賬號後,它會送 ...
  • 通過以下方式可以高效,並保證數據同步的可靠性 1.API設計 使用RESTful設計,確保API端點明確,並使用適當的HTTP方法(如POST用於創建,PUT用於更新)。 設計清晰的請求和響應模型,以確保客戶端能夠理解預期格式。 2.數據驗證 在伺服器端進行嚴格的數據驗證,確保接收到的數據符合預期格 ...