《編寫高質量OC代碼》已順利完成一二三四五六七八篇!
附上連接:
iOS 編寫高質量Objective-C代碼(一)—— 簡介
iOS 編寫高質量Objective-C代碼(二)—— 面向對象
iOS 編寫高質量Objective-C代碼(三)—— 接口和API設計
iOS 編寫高質量Objective-C代碼(四)—— 協議與分類
iOS 編寫高質量Objective-C代碼(五)—— 內存管理機制
iOS 編寫高質量Objective-C代碼(六)—— block專欄
iOS 編寫高質量Objective-C代碼(七)—— GCD專欄
iOS 編寫高質量Objective-C代碼(八)—— 系統框架html
這一篇,將經過介紹OC的接口和API設計
來提升Objective-C的代碼質量編程
OC裏沒有命名空間的概念(
namespace
)。因而,咱們須要給類加前綴,避免重名,避免發生命名衝突。固然,不只是類名,一些全局變量和方法也須要加上適當的前綴加以區分。安全
因此,咱們要:bash
選擇與公司、工程相關的前綴做爲類名的前綴。框架
爲了不重複引用第三方庫帶來的衝突,必要時也要爲他們加上前綴區分。編程語言
全能初始化方法
,並在文檔中寫明註釋。其餘的初始化方法全調用此全能初始化方法。舉個例子來講:能夠看一下NSDate類中定義了一個全能初始化方法:ide
- (instancetype)initWithTimeIntervalSinceReferenceDate:(NSTimeInterval)ti NS_DESIGNATED_INITIALIZER;
複製代碼
其他的初始化方法 定義在NSDate (NSDateCreation) 分類中測試
- (instancetype)initWithTimeIntervalSinceNow:(NSTimeInterval)secs;
- (instancetype)initWithTimeIntervalSince1970:(NSTimeInterval)secs;
- (instancetype)initWithTimeInterval:(NSTimeInterval)secsToBeAdded sinceDate:(NSDate *)date;
複製代碼
在官方文檔中,關於NSDate有以下說明 If you want to subclass NSDate to obtain behavior different than that provided by the private or public subclasses, you must:
atom
initWithTimeIntervalSinceReferenceDate:
, one of the designated initializer methods解釋:選定一個方法做爲全能初始化方法,剩下的其他的初始化方法都調用這這個方法初始化,這樣作的好處是之後若是初始化的邏輯更改了只需更改全能初始化方法,或者即便子類覆寫的時候也只覆寫全能初始化方法~spa
本條寫的是經過覆寫
description
(或者debugDescription
)方法來在NSLog打印
(或者LLDB打印
時)輸出更多的自定義信息。
下面舉個例子:
- (NSString *)description {
return [NSString stringWithFormat:@"<%@: %p, %@>",
[self class],
self,
@{
@"qi": _qi,
@"share" : _share}
];
}
複製代碼
readonly
修飾~(默認是readwrite修飾)~。這樣外部只能讀取數據而不能修改數據,保證了這個類的實例所持有的數據更加安全。~尤爲是不要把可變的collection做爲屬性公開,而是應該提供相應的方法修改可變的collection。~接口方法
修改KVC(Key-Value Coding)
技術 ~這種技術容許對象的數據或屬性能夠在運行時經過其鍵名進行查找,其中,屬性的名稱即爲其值的鍵名。在靜態語言中,這樣的作法是不可能的。KVC大大的增長了設計的自由度:經過KVC,無需知道對象的類型便可訪問其屬性或數據。~例如: 不推薦寫法:
//Animals.h
@property (nonatomic, strong) NSSet *animals;
複製代碼
應改成:
//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];
}
複製代碼
可是,小編認爲這樣寫當然有好處:保證了數據的安全性,但代碼量也會提高很多。因此推薦你們能夠有選擇的使用,對一些重要的類纔有使用必要。
另外,若是某屬性僅能夠在對象內部修改,則能夠在.h
文件中聲明爲readonly
。而後 在.m的類擴展
中將屬性擴展爲readwrite
屬性。
固然,師父說了:也能夠在.h
文件中把屬性聲明爲readonly
,在.m
文件中經過實例變量
修改值,當block內部修改值時,能夠用self->實例變量
的方法訪問修改。~(小編測試過,確實有效。歡迎路過的大神繼續討論)~
師父語錄:「寫OC代碼像是在講故事,而讀OC代碼更像是在聽故事。」
這句話要歸功於OC清晰而協調的命名方式。
例如:咱們想給初始化一個矩形,並給他的寬和高賦值。
// C++:
Rectangle *aRectangle = new Rectangle(5.0, 10.0);
// Objective-C:
Rectangle *aRectangle = [[Rectangle alloc] initWithWidth:5.0 andHeight:10.0];
複製代碼
很顯然,OC的方法能夠很直接的看出所要傳遞的參數的具體含義,而C++的傳參就並無這麼直觀。
這一條:給你們參考一下咱們QiShare團隊制定的 iOS 代碼規範 QiShare更喜歡經過#pragma mark -
來區分 公私有等方法
。 例如:
#pragma mark - Private Functions
// code...
#pragma mark - Action functions
// code...
#pragma mark - Request functions
// code...
#pragma mark - xxxDataSource
// code...
#pragma mark - xxxDelegate
// code...
複製代碼
固然,你們也能夠根據團隊本身定製規範。
不少語言都有異常處理機制,Objective-C也不例外。@throw
可是, 注意:OC裏拋異常可能會致使內存泄漏 注意:OC裏拋異常可能會致使內存泄漏 注意:OC裏拋異常可能會致使內存泄漏 解釋:OC裏的ARC機制(Automatic Reference Counting)在默認狀況下是「無異常安全」。簡單來講,一旦拋出異常,對象就沒法正常自動釋放了。 因此,
nil
或者0
(例如:初始化的參數不合法,方法返回nil或0)NSError
在iOS開發中,使用對象時常常須要拷貝它,這時咱們會經過copy/mutableCopy
方法完成。 若是咱們想讓本身的類支持拷貝,那就必需要實現NSCopying
協議,該協議只有一個方法:
- (id)copyWithZone:(nullable NSZone *)zone;
複製代碼
固然,若是要求返回對象是可變的那就要實現NSMutableCopying
協議,對應方法:
- (id)mutableCopyWithZone:(nullable NSZone *)zone;
複製代碼
同時,在拷貝對象時,要注意是執行淺拷貝仍是深拷貝
那麼引出了一個概念:什麼是深拷貝?什麼是淺拷貝?
指針
又拷貝出新的Object
)指針
指向原來的Object
)這裏有張很經典的圖解:
[NSMutableArray copy]
拷貝出 =>
NSArray
(不可變)
[NSArray mutableCopy]
拷貝出 =>
NSMutableArray
(可變) 這種操做能夠在可變版本和不可變版本間切換。
說太多,不如給一個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只是一個實例變量不是一個屬性,因此不能用點語法
return copy;
}
@end
複製代碼
最後,特別緻謝《Effective Objective-C 2.0》第三章