iOS 編寫高質(zhì)量Objective-C代碼(三)

級別: ★★☆☆☆
標(biāo)簽:「iOS」「OC」「Objective-C」
作者: MrLiuQ
審校: QiShare團(tuán)隊(duì)

前言:
這幾篇文章是小編在鉆研《Effective Objective-C 2.0》的知識產(chǎn)出撰茎,其中包含作者和小編的觀點(diǎn)嵌牺,以及小編整理的一些demo。希望能幫助大家以簡潔的文字快速領(lǐng)悟原作者的精華。
在這里逆粹,QiShare團(tuán)隊(duì)向原作者M(jìn)att Galloway表達(dá)誠摯的敬意募疮。

文章目錄如下:
iOS 編寫高質(zhì)量Objective-C代碼(一)
iOS 編寫高質(zhì)量Objective-C代碼(二)
iOS 編寫高質(zhì)量Objective-C代碼(三)
iOS 編寫高質(zhì)量Objective-C代碼(四)
iOS 編寫高質(zhì)量Objective-C代碼(五)
iOS 編寫高質(zhì)量Objective-C代碼(六)
iOS 編寫高質(zhì)量Objective-C代碼(七)
iOS 編寫高質(zhì)量Objective-C代碼(八)


這一篇,將通過介紹OC的接口和API設(shè)計(jì)來提高Objective-C的代碼質(zhì)量

一僻弹、用前綴避免命名空間沖突

OC里沒有命名空間的概念(namespace)阿浓。于是,我們需要給類加前綴蹋绽,避免重名芭毙,避免發(fā)生命名沖突。當(dāng)然卸耘,不僅是類名退敦,一些全局變量和方法也需要加上適當(dāng)?shù)那熬Y加以區(qū)分。

所以蚣抗,我們要:

  • 選擇與公司侈百、工程相關(guān)的前綴作為類名的前綴。

  • 為了避免重復(fù)引用第三方庫帶來的沖突忠聚,必要時也要為他們加上前綴區(qū)分设哗。

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

  • 在類中提供一個全能初始化方法两蟀,并在文檔中寫明注釋网梢。其他的初始化方法全調(diào)用此全能初始化方法。
  • 好處:當(dāng)類的結(jié)構(gòu)發(fā)生改變或初始化邏輯發(fā)生改變時赂毯,只需要改動全能初始化方法即可战虏。

舉個例子來說:可以看一下NSDate類中定義了一個全能初始化方法:

- (instancetype)initWithTimeIntervalSinceReferenceDate:(NSTimeInterval)ti NS_DESIGNATED_INITIALIZER;

其余的初始化方法 定義在NSDate (NSDateCreation) 分類中

- (instancetype)initWithTimeIntervalSinceNow:(NSTimeInterval)secs;
- (instancetype)initWithTimeIntervalSince1970:(NSTimeInterval)secs;
- (instancetype)initWithTimeInterval:(NSTimeInterval)secsToBeAdded sinceDate:(NSDate *)date;

在官方文檔中,關(guān)于NSDate有如下說明
If you want to subclass NSDate to obtain behavior different than that provided by the private or public subclasses, you must:

  • Override initWithTimeIntervalSinceReferenceDate:, one of the designated initializer methods

解釋:選定一個方法作為全能初始化方法党涕,剩下的其余的初始化方法都調(diào)用這這個方法初始化烦感,這樣做的好處是以后如果初始化的邏輯更改了只需更改全能初始化方法,或者即使子類覆寫的時候也只覆寫全能初始化方法~

三膛堤、實(shí)現(xiàn) description 方法

本條寫的是通過覆寫description(或者debugDescription)方法來在NSLog打印(或者LLDB打印時)輸出更多的自定義信息手趣。

下面舉個例子:

- (NSString *)description {

    return [NSString stringWithFormat:@"<%@: %p, %@>",
            [self class],
            self,
            @{
              @"qi": _qi,
              @"share" : _share}
            ];
}

四、盡量使用不可變對象

  1. 聲明對外屬性時肥荔,盡量使用不可變對象绿渣,同時,對外屬性聲明里盡量加上readonly修飾(默認(rèn)是readwrite修飾)燕耿。這樣外部只能讀取數(shù)據(jù)而不能修改數(shù)據(jù)中符,保證了這個類的實(shí)例所持有的數(shù)據(jù)更加安全。尤其是不要把可變的collection作為屬性公開誉帅,而是應(yīng)該提供相應(yīng)的方法修改可變的collection淀散。
  2. 若外部想修改修改對象的值有兩種途徑:
    • 提供接口方法修改
    • 使用KVC(Key-Value Coding)技術(shù)
      這種技術(shù)允許對象的數(shù)據(jù)或?qū)傩钥梢栽谶\(yùn)行時通過其鍵名進(jìn)行查找右莱,其中,屬性的名稱即為其值的鍵名档插。在靜態(tài)語言中慢蜓,這樣的做法是不可能的。KVC大大的增加了設(shè)計(jì)的自由度:通過KVC阀捅,無需知道對象的類型即可訪問其屬性或數(shù)據(jù)胀瞪。

例如:
不推薦寫法:

//Animals.h
@property (nonatomic, strong) NSSet *animals;

應(yīng)改為:

//Animals.h
@interface Animals : NSObject

@property (nonatomic, strong, readonly) NSSet *animals;

- (void)addAnimal:(NSString *)animal;
- (void)removeAnimal:(NSString *)animal;

@end


//Animals.m
@implementation Animals {
    NSMutableSet *_mutableAnimals;
}

- (NSSet *)animals {
    return [_mutableAnimals copy];
}

- (void)addAnimal:(NSString *)animal {
    [_mutableAnimals addObject:animal];
}

- (void)removeAnimal:(NSString *)animal {
    [_mutableAnimals removeObject:animal];
}

但是针余,小編認(rèn)為這樣寫固然有好處:保證了數(shù)據(jù)的安全性饲鄙,但代碼量也會提升不少。所以推薦大家可以有選擇的使用圆雁,對一些重要的類才有使用必要忍级。

另外,如果某屬性僅可以在對象內(nèi)部修改伪朽,則可以在.h文件中聲明為readonly轴咱。然后 在.m的類擴(kuò)展中將屬性擴(kuò)展為readwrite屬性。

當(dāng)然烈涮,師父說了:也可以在.h文件中把屬性聲明為readonly朴肺,在.m文件中通過實(shí)例變量修改值,當(dāng)block內(nèi)部修改值時坚洽,可以用self->實(shí)例變量的方法訪問修改戈稿。(小編測試過,確實(shí)有效讶舰。歡迎路過的大神繼續(xù)討論)

五鞍盗、使用清晰而協(xié)調(diào)的命名方式

師父語錄:“寫OC代碼像是在講故事,而讀OC代碼更像是在聽故事跳昼“慵祝”

這句話要?dú)w功于OC清晰而協(xié)調(diào)的命名方式。

  • 首先鹅颊,是駝峰式命名方法:這個和大部分編程語言都一樣敷存。
  • 其次,也是最關(guān)鍵的方法命名堪伍。從左至右讀起來就像日常用語中的句子锚烦。

例如:我們想給初始化一個矩形,并給他的寬和高賦值杠娱。

// C++:
Rectangle *aRectangle = new Rectangle(5.0, 10.0);

// Objective-C:
Rectangle *aRectangle = [[Rectangle alloc] initWithWidth:5.0 andHeight:10.0];

很顯然挽牢,OC的方法可以很直接的看出所要傳遞的參數(shù)的具體含義,而C++的傳參就并沒有這么直觀摊求。

六禽拔、為私有方法名加前綴

這一條:給大家參考一下我們QiShare團(tuán)隊(duì)制定的 iOS 代碼規(guī)范
QiShare更喜歡通過#pragma mark -來區(qū)分 公私有等方法
例如:

#pragma mark - Private Functions

// code...


#pragma mark - Action functions

// code...


#pragma mark - Request functions

// code...


#pragma mark - xxxDataSource

// code...


#pragma mark - xxxDelegate

// code...

當(dāng)然,大家也可以根據(jù)團(tuán)隊(duì)自己定制規(guī)范睹栖。

七硫惕、理解 Objective-C 錯誤模型

很多語言都有異常處理機(jī)制,Objective-C也不例外野来。@throw
但是恼除,
注意:OC里拋異常很可能會導(dǎo)致內(nèi)存泄漏
注意:OC里拋異常很可能會導(dǎo)致內(nèi)存泄漏
注意:OC里拋異常很可能會導(dǎo)致內(nèi)存泄漏
解釋:OC里的ARC機(jī)制(Automatic Reference Counting)在默認(rèn)情況下是“無異常安全”。簡單來說曼氛,一旦拋出異常豁辉,對象很可能就無法正常自動釋放了。
所以舀患,

  1. 異常只用于處理嚴(yán)重的錯誤(fatal error徽级,致命錯誤)
  2. 對于一些不那么嚴(yán)重的錯誤(nonfatal error,非致命錯誤)聊浅,有兩種解決方案:
    • 讓對象返回nil或者0(例如:初始化的參數(shù)不合法餐抢,方法返回nil或0)
    • 使用NSError

八、理解 NSCopying協(xié)議

在iOS開發(fā)中低匙,使用對象時經(jīng)常需要拷貝它旷痕,這時我們會通過copy/mutableCopy方法完成。
如果我們想讓自己的類支持拷貝顽冶,那就必須要實(shí)現(xiàn)NSCopying協(xié)議欺抗,該協(xié)議只有一個方法:

- (id)copyWithZone:(nullable NSZone *)zone;

當(dāng)然,如果要求返回對象是可變的那就要實(shí)現(xiàn)NSMutableCopying協(xié)議渗稍,對應(yīng)方法:

- (id)mutableCopyWithZone:(nullable NSZone *)zone;

同時佩迟,在拷貝對象時,要注意是執(zhí)行淺拷貝還是深拷貝

那么引出了一個概念:什么是深拷貝竿屹?什么是淺拷貝报强?

  • 深拷貝:內(nèi)容拷貝(既拷貝新的指針又拷貝出新的Object
  • 淺拷貝:指針拷貝(僅拷貝新的指針指向原來的Object

這里有張很經(jīng)典的圖解:

左邊淺拷貝,右邊深拷貝

深拷貝在拷貝對象時拱燃,會將指針?biāo)傅牡讓訑?shù)據(jù)也拷貝一份秉溉。而淺拷貝只是創(chuàng)建了一個新的指針指向要拷貝的內(nèi)容。一般情況下碗誉,盡量使用淺拷貝召嘶。
此外,還有一個注意點(diǎn):
[NSMutableArray copy] 拷貝出 => NSArray (不可變)
[NSArray mutableCopy] 拷貝出 => NSMutableArray(可變)
這種操作可以在可變版本和不可變版本間切換哮缺。

說太多弄跌,不如給一個Demo~

  • 下面請看小編準(zhǔn)備的NSCopying協(xié)議相關(guān)的小Demo:

QiShareMember.h:

@interface QiShareMember : NSObject <NSCopying>

@property (nonatomic, copy, readonly) NSString *name; //!< 姓名
@property (nonatomic, copy, readonly) NSString *sex; //!< 性別
@property (nonatomic, assign, readonly) NSUInteger age; //!< 年齡

//! 初始化方法
- (instancetype)initWithName:(NSString *)name andSex:(NSString *)sex andAge:(NSUInteger)age;

- (void)addFriend:(QiShareMember *)friend;
- (void)removeFriend:(QiShareMember *)friend;

@end

QiShareMember.m:

@implementation QiShareMember {
    
    NSMutableSet *_friends;
}

- (instancetype)initWithName:(NSString *)name andSex:(NSString *)sex andAge:(NSUInteger)age {
    
    if (self = [super init]) {
        
        _name = [name copy];
        _sex = [sex copy];
        _age = age;
        _friends = [NSMutableSet new];
    }
    
    return self;
}

- (void)addFriend:(QiShareMember *)friend {
    
    [_friends addObject:friend];
}

- (void)removeFriend:(QiShareMember *)friend {
    
    [_friends removeObject:friend];
}

- (id)copyWithZone:(NSZone *)zone {
    
    QiShareMember *copy = [[[self class] allocWithZone:zone] initWithName:_name andSex:_sex andAge:_age];
    copy->_friends = [_friends mutableCopy]; //!< 注意friends只是一個實(shí)例變量不是一個屬性,所以不能用點(diǎn)語法
    
    return copy;
}

@end

最后尝苇,特別致謝《Effective Objective-C 2.0》第三章


推薦文章:
代碼快不快铛只?跑個分就知道
iOS UIButton之改變有效點(diǎn)擊區(qū)域(改變熱區(qū))

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末埠胖,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子淳玩,更是在濱河造成了極大的恐慌直撤,老刑警劉巖,帶你破解...
    沈念sama閱讀 216,843評論 6 502
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件蜕着,死亡現(xiàn)場離奇詭異谋竖,居然都是意外死亡,警方通過查閱死者的電腦和手機(jī)承匣,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,538評論 3 392
  • 文/潘曉璐 我一進(jìn)店門蓖乘,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人悄雅,你說我怎么就攤上這事驱敲。” “怎么了宽闲?”我有些...
    開封第一講書人閱讀 163,187評論 0 353
  • 文/不壞的土叔 我叫張陵,是天一觀的道長握牧。 經(jīng)常有香客問我容诬,道長,這世上最難降的妖魔是什么沿腰? 我笑而不...
    開封第一講書人閱讀 58,264評論 1 292
  • 正文 為了忘掉前任览徒,我火速辦了婚禮,結(jié)果婚禮上颂龙,老公的妹妹穿的比我還像新娘习蓬。我一直安慰自己,他們只是感情好措嵌,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,289評論 6 390
  • 文/花漫 我一把揭開白布躲叼。 她就那樣靜靜地躺著,像睡著了一般企巢。 火紅的嫁衣襯著肌膚如雪枫慷。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 51,231評論 1 299
  • 那天浪规,我揣著相機(jī)與錄音或听,去河邊找鬼。 笑死笋婿,一個胖子當(dāng)著我的面吹牛誉裆,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播缸濒,決...
    沈念sama閱讀 40,116評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼足丢,長吁一口氣:“原來是場噩夢啊……” “哼元镀!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起霎桅,我...
    開封第一講書人閱讀 38,945評論 0 275
  • 序言:老撾萬榮一對情侶失蹤栖疑,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后滔驶,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體遇革,經(jīng)...
    沈念sama閱讀 45,367評論 1 313
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,581評論 2 333
  • 正文 我和宋清朗相戀三年揭糕,在試婚紗的時候發(fā)現(xiàn)自己被綠了萝快。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 39,754評論 1 348
  • 序言:一個原本活蹦亂跳的男人離奇死亡著角,死狀恐怖揪漩,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情吏口,我是刑警寧澤奄容,帶...
    沈念sama閱讀 35,458評論 5 344
  • 正文 年R本政府宣布,位于F島的核電站产徊,受9級特大地震影響昂勒,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜舟铜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,068評論 3 327
  • 文/蒙蒙 一戈盈、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧谆刨,春花似錦塘娶、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,692評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至生兆,卻和暖如春难捌,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背鸦难。 一陣腳步聲響...
    開封第一講書人閱讀 32,842評論 1 269
  • 我被黑心中介騙來泰國打工根吁, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人合蔽。 一個月前我還...
    沈念sama閱讀 47,797評論 2 369
  • 正文 我出身青樓击敌,卻偏偏與公主長得像,于是被迫代替她去往敵國和親拴事。 傳聞我的和親對象是個殘疾皇子沃斤,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,654評論 2 354

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