《禪與Objective-C編程藝術(shù)》閱讀總結(jié)

注:《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)存和初始化被分離為兩步,allocinit。這個特性叫兩步創(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)建,使用copyblock拷貝到堆里面去。
(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是否存在的問題。
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容