觀“編寫高質(zhì)量iOS與OC 代碼的52個(gè)有效方法”有感(三)· 接口與API設(shè)計(jì)

iPhone.jpg

我們?cè)趯懗绦驎r(shí),都希望其中一部分代碼用于后續(xù)項(xiàng)目,又或者把某些代碼發(fā)布出來,供他人使用。因此,我們構(gòu)建項(xiàng)目的要注意:

15、用前綴避免命名空間沖突

  • 因?yàn)镺bjective-C沒有其他語言那種內(nèi)置的命名空間機(jī)制(namespace)。
    所以我們?cè)谄鹈麜r(shí)要設(shè)法避免潛在的命名沖突,否則就容易重名,發(fā)生命名沖突,程序鏈接過程就會(huì)出錯(cuò)。比如下面:
命名沖突.png
  • 避免問題:
    變相實(shí)現(xiàn)命名空間:為所有名稱都加上適當(dāng)前綴(一般是與公司或者應(yīng)用程序有關(guān)聯(lián)的名字)。命名時(shí)要注意:因?yàn)锳pple宣傳其保留使用所有“兩字母前綴”的權(quán)利,那我們自己選的前綴應(yīng)該是三個(gè)字母的。

16、提供“全能初始化方法”

  • 所有對(duì)象都要初始化。但是創(chuàng)建類實(shí)例的方式有的時(shí)候不止一種。
    例如:UITableViewCell,初始化時(shí),需要指明其樣式以及標(biāo)識(shí)符,標(biāo)識(shí)符區(qū)分不同類型的單元格。
    這種對(duì)象的創(chuàng)建成本比較高,繪制表格的時(shí)候可依照標(biāo)識(shí)符復(fù)用,提升程序效率。我們把這種可為對(duì)象提供必要信息以便能完成工作的初始化方法叫做“全能初始化方法”
  • 試一試
    首先創(chuàng)建一個(gè)類“ZSCManager”然后給其添加2個(gè)只讀的屬性,這樣外界就無法更改了,再提供初始化方法設(shè)置這兩個(gè)屬性:
 #import <Foundation/Foundation.h>

@interface ZSCManager : NSObject

@property (nonatomic, readonly, copy) NSString *name;
@property (nonatomic, readonly, copy) NSString *phone;

- (id)initWithManageName:(NSString *)name
                   phone:(NSString *)phone;

@end

//.m的實(shí)現(xiàn)
#import "ZSCManager.h"

@implementation ZSCManager


- (id)init {
    return [self initWithManageName:@"未知名字" phone:@"110"];
}


- (id)initWithManageName:(NSString *)name
                   phone:(NSString *)phone {
    if (self = [super init]) {
        _name = name;
        _phone = phone;
    }
    return self;
}

@end
  • 注意:
    設(shè)置默認(rèn)值的那個(gè)init方法調(diào)用了“全能初始化方法”。
    若是存儲(chǔ)方式變了(比如名字和手機(jī)號(hào)放在某個(gè)結(jié)構(gòu)中),則init與全能初始化方法設(shè)置數(shù)據(jù)的代碼就要修改。
    簡單的情況沒有太大問題,如果類的初始化方法有很多種,而且初始化的數(shù)據(jù)較為復(fù)雜,那么這樣做就麻煩的多。很容易忘記修改某個(gè)初始化方法,造成初始化方法之間相互不一致。

  • 假如現(xiàn)在創(chuàng)建個(gè)名叫 ZSCLeader 的類,讓其成為 ZSCManager 的子類。那么新類的初始化方法要怎么寫呢?

#import "ZSCManager.h"

@interface ZSCLeader : ZSCManager

- (id)initWithLeaderName:(NSString *)name;

@end

//.m的實(shí)現(xiàn)
#import "ZSCLeader.h"

@implementation ZSCLeader

- (id)initWithManageName:(NSString *)name
                   phone:(NSString *)phone {
    return [self initWithLeaderName:name];
}

- (id)initWithLeaderName:(NSString *)name {
    return [super initWithManageName:name phone:nil];
}

@end
  • ZSCLeader類的全能初始化方法調(diào)用了超類的全能初始化方法,
    再看 ZSCManager 類的實(shí)現(xiàn),也會(huì)發(fā)現(xiàn) ZSCManager 也調(diào)用了超類的全能初始化方法,因此說明:全能初始化的方法調(diào)用鏈一點(diǎn)要維系。
    調(diào)用者創(chuàng)建 ZSCLeader 會(huì)通過 initWithLeaderName 或者 init的方法。所以要覆寫超類的全能初始化方法,否則init方法創(chuàng)建就會(huì)有問題。

  • 有時(shí)候我們不想覆寫超類的全能初始化方法,可以理解為:我們認(rèn)為這是調(diào)用者自己犯錯(cuò)了。這樣子,我們就需要覆寫超類的全能初始化方法并拋出異常:

- (id)initWithManageName:(NSString *)name
                   phone:(NSString *)phone {
    @throw [NSException exceptionWithName:NSInternalInconsistencyException reason:@"不讓你調(diào)用此方法,方法名字:initWithManageName:(NSString *)name phone:(NSString *)phone 或者 init" userInfo:nil];
}
  • 然后調(diào)用init 或者超類的方法創(chuàng)建時(shí),程序就會(huì)崩潰報(bào)錯(cuò)
    *** Terminating app due to uncaught exception 'NSInternalInconsistencyException', reason: '不讓你調(diào)用此方法,方法名字:initWithManageName:(NSString *)name phone:(NSString *)phone 或者 init'

  • 我們不想程序崩潰也想知道崩潰的原因,可以這樣辦:

    @try {
        // 可能會(huì)出現(xiàn)崩潰的代碼
        ZSCLeader *leader = [[ZSCLeader alloc] init];
    }
    @catch (NSException *exception) {
        // 捕獲到的異常exception
        NSLog(@"%@",exception);
        //控制臺(tái)的輸出
        //不讓你調(diào)用此方法,方法名字:initWithManageName:(NSString *)name phone:(NSString *)phone 或者 init
    }
    @finally {
        // 結(jié)果處理
    }
  • 總結(jié):
    • 如果在類中提供一個(gè)全能初始化方法,在文檔中指明。其他初始化方法均應(yīng)調(diào)用此方法。
    • 若全能初始化方法與超類不同,則需覆寫超類中的對(duì)應(yīng)方法。
    • 如果超類的初始化方法不適用子類,那么應(yīng)該覆寫這個(gè)超類方法,并在其中拋出異常。

17、實(shí)現(xiàn) description 方法

  • 調(diào)試程序時(shí),經(jīng)常需要打印并查看對(duì)象信息。一般都是這樣做
    ZSCManager *manager = [[ZSCManager alloc] init];
    NSLog(@"%@",manager);
    可以看到輸出為:
    <ZSCManager: 0x610000026b40>
    這樣的信息不太有用。只有類的名字和指針地址。

  • 我們?cè)陬愔懈矊?description 方法。

    - (NSString *)description {
        return [NSString stringWithFormat:@"<%@:%p ,\"name : %@  phone : %@\">",[self class],self,_name,_phone];
    

}

這次的輸出變得不一樣了,對(duì)我們也更有用:
      <ZSCManager:0x6080000354e0 ,"name : 未知名字  phone : 110">

- 注意:
  - 實(shí)現(xiàn) description 方法返回一個(gè)有意義的字符串,用以描述該實(shí)例。
  - 若想在調(diào)試時(shí)打印出更詳盡的對(duì)象描述信息,則應(yīng)該實(shí)現(xiàn) debugDescription 方法。

#18、盡量使用不可變對(duì)象
- 設(shè)計(jì)類的時(shí)候,要充分運(yùn)用屬性來封裝數(shù)據(jù)。
屬性默認(rèn)情況是“既可讀又可寫的”(readWrite)
一般來說,我們有些建模的數(shù)據(jù)未必需要改變。這樣我們最好把其屬性設(shè)置為readonly。
      @property (nonatomic, readonly, copy) NSString *name;
如果想“暴力”修改,那就通過KVC:
      [manager setValue:@"哈哈" forKey:@"name"];

#19、使用清晰而協(xié)調(diào)的命名方式
 - 方法和變量名使用“駝峰式大小寫命名法”
小寫字母開頭,其后每個(gè)單詞首字母大寫。
類名也用駝峰命名法,不過其字母要大寫,而且前面通常有兩三個(gè)前綴字母。

- 方法名要言簡意賅,從左至右讀起來要像個(gè)日常用語中的句子才好。
- 方法名里不要使用縮略后的類型名稱。
- 給方法起名時(shí)的第一要?jiǎng)?wù)就是確保其風(fēng)格與你自己的代碼或所要集成的框架相符。

#20、為私有方法名加前綴
- 一個(gè)類所要做的事情通常都要比外面看到的更多。
編寫類的實(shí)現(xiàn)代碼時(shí),經(jīng)常要寫一些只在內(nèi)部使用的方法。
建議應(yīng)該為這種方法的名稱加上某些前綴。
1、有助于調(diào)試。
2、容易把公共方法和私有方法區(qū)別開。
- 不要單用一個(gè)下劃線做私有方法的前綴,因?yàn)檫@種做法是預(yù)留給蘋果公司用的,建議用p_XXXX這樣。p代表 “private”(私有的)。

#21、理解Objective-C錯(cuò)誤模型
- 很多編程語言都有“異常”(exception)機(jī)制,Objective-C 也不例外。
       @throw [NSException exceptionWithName:NSInternalInconsistencyException reason:@"XXX" userInfo:nil];
OC語言現(xiàn)在采用的方法:只在極其罕見的情況下拋出異常,異常拋出之后,無須考慮恢復(fù)問題,應(yīng)用會(huì)立刻退出。不需要編寫“異常安全”代碼。
-  Objective-C在出現(xiàn)“非致命錯(cuò)誤”時(shí)。這樣處理的:
令方法返回 nil/0,或者是使用NSError。
 - 假如調(diào)用者發(fā)現(xiàn)方法的返回值是nil,就可以確定其中發(fā)生了錯(cuò)誤。就可以采取相對(duì)應(yīng)的措施。
 - NSError 用法靈活。我們可以把導(dǎo)致錯(cuò)誤的原因匯報(bào)給調(diào)用者。
NSError domain(錯(cuò)誤范圍,類型為字符串)
產(chǎn)生錯(cuò)誤的根源,比如從URL中解析或取得數(shù)據(jù)時(shí)出錯(cuò)了,就會(huì)使用NSURLErrorDomain來表示錯(cuò)誤范圍。
Error code(錯(cuò)誤碼,類型為整數(shù))
獨(dú)有的錯(cuò)誤代碼,錯(cuò)誤情況通常采用 enum 來定義。比如 HTTP 請(qǐng)求出錯(cuò)時(shí),可能會(huì)把HTTP狀態(tài)碼設(shè)為錯(cuò)誤碼。
User info (用戶信息,類型為字典)
關(guān)于錯(cuò)誤的額外信息。
- 參考 AFNetworking 中對(duì)NSError的使用,直接把錯(cuò)誤信息放在 NSError 對(duì)象里,經(jīng)由“輸出參數(shù)”返回給調(diào)用者。

#22、理解 NSCopying 協(xié)議
- 使用對(duì)象時(shí)經(jīng)常需要拷貝它。
在Objective-C中,如果想令自己的類支持拷貝操作,就要實(shí)現(xiàn) NSCopying 協(xié)議。協(xié)議里只有一個(gè)方法:
      - (id)copyWithZone:(nullable NSZone *)zone
NSZone是什么呢?
以前開發(fā)程序時(shí),會(huì)把內(nèi)存分為不同的“區(qū)”(zone),對(duì)象會(huì)創(chuàng)建在某個(gè)區(qū)里面。
現(xiàn)在不用了,每個(gè)程序只有一個(gè)“默認(rèn)區(qū)”,不用擔(dān)心其中的 zone 參數(shù)。
  • (id)copyWithZone:(nullable NSZone *)zone {
    ZSCManager *copy = [[[self class] allocWithZone:zone] initWithManageName:_name phone:_phone];
    return copy;
    }
直接把待拷貝的對(duì)象交給“全能初始化方法”,令其執(zhí)行所有初始化工作。

- 若是自定義的對(duì)象分為可變版本和不可變版本,那么就要同時(shí)實(shí)現(xiàn) NSCopying 和 NSMutableCopying 協(xié)議。
- 復(fù)制對(duì)象時(shí)需要決定采用淺拷貝還是深拷貝,一般情況下執(zhí)行淺拷貝。
深拷貝:在拷貝自身時(shí),將其底層數(shù)據(jù)也一并復(fù)制過來。
淺拷貝:只拷貝容器對(duì)象本身,而不復(fù)制其中數(shù)據(jù)。
注意:容器內(nèi)的對(duì)象未必都能拷貝,調(diào)用者也不一定想在拷貝容器的時(shí)候一并拷貝其中的每個(gè)對(duì)象。
因?yàn)椋簺]有專門定義深拷貝的協(xié)議。
所以:具體執(zhí)行方式由每個(gè)類來確定,只需要你自己決定自己寫的類是否要提供深拷貝的方法。如果你所寫的對(duì)象需要深拷貝,那么可以考慮新增一個(gè)專門執(zhí)行深拷貝的方法。

### 接下來也將會(huì)繼續(xù)整理。如果覺得有用請(qǐng)點(diǎn)個(gè)喜歡!
### 您的支持將是我繼續(xù)寫作的動(dòng)力!謝謝。

[觀“編寫高質(zhì)量iOS與OC X代碼的52個(gè)有效方法”有感(一)· 熟悉Objective-C](http://www.lxweimin.com/p/0876e60d3160)
[觀“編寫高質(zhì)量iOS與OC X代碼的52個(gè)有效方法”有感(二)· 對(duì)象、消息、運(yùn)行時(shí)](http://www.lxweimin.com/p/1aac4fb98888)
[觀“編寫高質(zhì)量iOS與OC X代碼的52個(gè)有效方法”有感(三)· 接口與API設(shè)計(jì)](http://www.lxweimin.com/p/5d9e61db2a01)
[觀“編寫高質(zhì)量iOS與OC X代碼的52個(gè)有效方法”有感(四)· 協(xié)議與分類](http://www.lxweimin.com/p/0944e1e276ff)
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌,老刑警劉巖,帶你破解...
    沈念sama閱讀 229,763評(píng)論 6 539
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異,居然都是意外死亡,警方通過查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 99,238評(píng)論 3 428
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人,你說我怎么就攤上這事。” “怎么了?”我有些...
    開封第一講書人閱讀 177,823評(píng)論 0 383
  • 文/不壞的土叔 我叫張陵,是天一觀的道長。 經(jīng)常有香客問我,道長,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 63,604評(píng)論 1 317
  • 正文 為了忘掉前任,我火速辦了婚禮,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘。我一直安慰自己,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 72,339評(píng)論 6 410
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著,像睡著了一般。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 55,713評(píng)論 1 328
  • 那天,我揣著相機(jī)與錄音,去河邊找鬼。 笑死,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播,決...
    沈念sama閱讀 43,712評(píng)論 3 445
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 42,893評(píng)論 0 289
  • 序言:老撾萬榮一對(duì)情侶失蹤,失蹤者是張志新(化名)和其女友劉穎,沒想到半個(gè)月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 49,448評(píng)論 1 335
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 41,201評(píng)論 3 357
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 43,397評(píng)論 1 372
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情,我是刑警寧澤,帶...
    沈念sama閱讀 38,944評(píng)論 5 363
  • 正文 年R本政府宣布,位于F島的核電站,受9級(jí)特大地震影響,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 44,631評(píng)論 3 348
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧,春花似錦、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 35,033評(píng)論 0 28
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 36,321評(píng)論 1 293
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 52,128評(píng)論 3 398
  • 正文 我出身青樓,卻偏偏與公主長得像,于是被迫代替她去往敵國和親。 傳聞我的和親對(duì)象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 48,347評(píng)論 2 377

推薦閱讀更多精彩內(nèi)容