注:《Zen and the Art of the Objective-C Craftsmanship》 中文翻譯版閱讀總結(jié)
原文在這里 ,感謝作者
1、常量命名方法
常量的命名方法 以駝峰法命名,并以相關(guān)類名作為前綴
static const NSTimeInterval ZOCSignInViewControllerFadeOutAnimationDuration = 0.4;
static NSString * const ZOCCacheControllerDidClearCacheNotification = @"ZOCCacheControllerDidClearCacheNotification";
常量在頭文件中以這樣的形式暴露給外部
extern NSString *const ZOCCacheControllerDidClearCacheNotification;
2、類名的規(guī)范
(1)類名以是哪個大寫字母作為前綴,雙字母前綴為Apple的類預留。解決Objective-C沒有命名空間所帶來的問題。
(2)創(chuàng)建一個子類時,把說明性的部分放在前綴和父類名的中間。如一個 UIViewController 的子類會是 ZOCTimelineViewController。
3、Initializer 和 dealloc
(1)推薦將dealloc方法放在實現(xiàn)文件的最前面(直接在 @synthesize 以及 dynamic 之后),
(2)為什么設(shè)置 self 為 [super init] 的返回值?
申請分配內(nèi)存和初始化被分離為兩步,alloc和init。這個特性叫兩步創(chuàng)建
-
alloc負責創(chuàng)建對象,這個過程包括分配足夠的內(nèi)存來保存對象,寫入isa指針,初始化引用計數(shù),以及重置所有實例變量。 -
init負責初始化對象,這意味著使對象處于可用狀態(tài)。通常意味著為對象的實例變量賦予合理有用的值。
alloc方法將返回一個有效的未初始化的對象實例。每一個對這個實例發(fā)送的消息會被轉(zhuǎn)換成一次obj_msgSend()函數(shù)的調(diào)用,形參self的實參是alloc返回的指針,這樣self在所有方法的作用域內(nèi)都能夠被訪問。
init方法可以通過返回 nil來告訴調(diào)用者,初始化失敗了。
4、Designated(指定) 和 Secondary (間接)初始化方法
(1)designated 初始化方法是提供所有的參數(shù),每一個類總有且只有一個。Seconary 初始化方法可以是一個或多個,他們僅僅是提供一個或者更多的默認參數(shù)來調(diào)用designated初始化的初始化方法。
(2)在類繼承中調(diào)用任何designated初始化方法都是合法的,應該保證所有的 designated initializer 在類繼承中是從祖先(通常是NSObject)到你的類向下調(diào)用的。
定義一個新類的三種方式:
- 不需要重載任何的初始化函數(shù)
- 重載 designated initializer
- 定義一個新的 designated initializer
第一個方案最簡單,不需要天劍類的任何初始化邏輯,只需要依照父類的designated initializer
@implementation YOEViewController
- (id)initWithNibName:(NSString *)nibNameOrNil bundle:(NSBundle *)nibBundleOrNil
{
self = [super initWithNibName:nibNameOrNil bundle:nibBundleOrNil];
if (self) {
// custom initialization(自定義的初始化過程)
}
return self;
}
第三種 希望提供自己的初始化函數(shù)時,遵循三個步驟來保證獲得正確的行為:
- 定義自己designated initializer,確保調(diào)用了直接超類的designated initializer
- 重載直接超類的 designated initializer。調(diào)用新的 designated initializer
- 為新的designated initializer寫文檔
正確的實現(xiàn)例子:
- (id)initWithNews:(NSString *)news
{
//call the immediate superclass's designated initializer(調(diào)用直接超類的designated initializer)
self = [super initWithNibName:nil bundle:nil];
if (self) {
_news = news;
}
return self;
}
// Override the immediate superclass's designated initializer(重載直接父類的 designated initializer)
- (id)initWithNibName:(NSString *)nibNameOrNil bundle:(NSBundle *)nibBundleOrNil
{
//Call the new designated initializer
return [self initWithNews:@"defaultNews"];
}
注:如果沒有重載initWithNibName: bundle:方法,而調(diào)用者決定使用這個方法來初始化這個類(這是完全合法的)。initWithNews:永遠也不會調(diào)用,這就導致了不正確的初始化流程,新類的特定初始化邏輯沒有被執(zhí)行。
可以使用編譯器的指令 NS_DESIGNATED_INITIALIZER 或__attribute__((objc_designated_initializer))來明確的支出那個方法是designated initializer。這樣如果新的designated initializer沒有調(diào)用超類的designated initializer,編譯器會發(fā)出警告。
寫法:
- (id)initWithNews:(NSString *)news __attribute__((objc_designated_initializer));
- (id)initWithNews:(NSString *)news NS_DESIGNATED_INITIALIZER;
通過另一個編譯器指令attribute((unavailable("Invoke the designated initializer")))來修飾一個方法,這樣會使在試圖調(diào)用這個方法的時候產(chǎn)生一個編譯錯誤(實際上代碼提示都不會有該方法了)。
寫法:
- (id)initWithNibName:(NSString *)nibNameOrNil bundle:(NSBundle *)nibBundleOrNil attribute((unavailable("Invoke the designated initializer")));
處理一個例外:
如果一個對象遵循NSCoding協(xié)議,并且它通過initWithCoder:初始化。此時應該看超類是否符合NSCoding協(xié)議來區(qū)別對待。符合的時候,如只是調(diào)用了[super initWithCoder:],就需要在designated initializer里面寫一些通用的初始化代碼,處理這種方法的是把這些代碼放在私有方法里面。當超類不符合NSCoding協(xié)議的時候,推薦把initWithCoder:作為Secondary initializer來對待,并且調(diào)用self的designated initializer。
5、單例的寫法
+ (instancetype)sharedInstance
{
static id sharedInstance = nil;
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
sharedInstance = [[self alloc]init];
});
return sharedInstance;
}
6、屬性定義
(1)屬性定義參數(shù)按順序排列:原子性,讀寫,內(nèi)存管理。
如 @property(nonatomic,readwrite,copy) NSString *name;
(2)屬性可以存儲一個代碼塊,必須使用copy。block最早在棧里面創(chuàng)建,使用copy讓block拷貝到堆里面去。
(3)聲明一個公有的 getter 和一個私有的 setter,應該聲明公開的屬性為 readonly,并在類的擴展中重新定義屬性為readwrite。
//.h文件中
@interface YOEClass : NSObject
@property(nonatomic,readonly,strong) NSObject *obj;
@end
//.m文件中
@interface YOEClass ()
@property(nonatomic,readwrite,strong) NSObject *obj;
@end
@implementation YOEClass
- (id)init{
self = [super init];
if (self) {
_obj = @"AA";
}
return self;
}
@end
(4)聲明一個BOOL屬性,setter不應該帶is前綴,對應的getter應該帶上前綴,So:
@property(assign,getter=isEditable) BOOL editable;
注:在實現(xiàn)文件中應該避免使用@synthesize,編譯器已經(jīng)實現(xiàn)了
7、NSNotification
定義一個NSNotification 的時候,應該把通知的名字作為一個字符串常量
//.h
extern NSString * const ZOCFooDidBecomeBarNotification;
//.m
NSString * const ZOCFooDidBecomeBarNotification = @"ZOCFooDidBecomeBarNotification";
8、利用代碼塊
代碼塊如果在閉合的圓括號里,會返回最后語句的值
NSURL *url = ({
NSString *urlStr = @"http://www.baidu.com";
[NSURL URLWithString:urlStr];
});
9、self的循環(huán)引用
當使用代碼塊和異步分發(fā)的時候,需要避免引用循環(huán)。應使用 weak 來引用對象,避免引用循環(huán)。
__weak __typeof(self) weakSelf = self;
例子:
單個語句時:
__weak __typeof(self) weakSelf = self;
[self executeBlock:^(NSData *data, NSError *error) {
[weakSelf doSomethingWithData:data];
}];
多個語句時:
[self executeBlock:^(NSData *data, NSError *error) {
__strong __typeof(weakSelf) strongSelf = weakSelf;
if (strongSelf) {
[strongSelf doSomethingWithData:data];
[strongSelf doSomethingWithData:data];
}
}];
簡單記錄self在block倆面的三種情況
- 直接在block里面使用關(guān)鍵字 self。只能在 block 不是作為一個property 的時候使用,否則會導致 retain cycle。
- 在 block 外定義一個
__weak的引用到self,并且在 block 里面使用這個弱引用。 當 block 被聲明一個 property 的時候使用。 - 在block 外定義一個
__weak的引用到 self,并且在 block 內(nèi)部通過這個弱引用定義一個__strong的引用。 和并發(fā)執(zhí)行有關(guān)。當涉及異步的服務(wù)的時候,block 可以在之后被執(zhí)行,并且不會發(fā)生關(guān)于self是否存在的問題。