init:惯裕、initWithFrame: 調(diào)用選擇

前段時間準(zhǔn)備面試時温数,突然被問到一個問題:init:initWithFrame: 方法應(yīng)該調(diào)用哪個?為什么蜻势?

其實(shí)這個問題涉及的是 Objective-C 語法的 指定初始化方法(Designated Initializer) 和 間接初始化方法(Secondary Initializer) 的相關(guān)知識撑刺。

指定初始化方法

類本身

一個類應(yīng)該只有一個 Designated Initializer,該初始化方法提供所有參數(shù)以供初始化握玛。其他初始化方法中應(yīng)該盡可能地調(diào)用 指定初始化方法够傍。

下面以知名開源庫 AFNetworking 為例,驗(yàn)證下 Designated Initializer 與 Secondary Initializer 之間的關(guān)系挠铲。

@interface AFURLSessionManager : NSObject

- (instancetype)initWithSessionConfiguration:(nullable NSURLSessionConfiguration *)configuration NS_DESIGNATED_INITIALIZER;

@end


@implementation AFURLSessionManager

- (instancetype)init {
    return [self initWithSessionConfiguration:nil];
}

- (instancetype)initWithSessionConfiguration:(NSURLSessionConfiguration *)configuration {
    self = [super init];
    if (!self) {
        return nil;
    }

    if (!configuration) {
        configuration = [NSURLSessionConfiguration defaultSessionConfiguration];
    }

    self.sessionConfiguration = configuration;

    // 以下代碼省略
    ....
}

...
@end

AFURLSessionManager 繼承自 NSObject 冕屯,除了默認(rèn)的 init: 方法外,還定義了 initWithSessionConfiguration: 指定初始化方法拂苹。

在其內(nèi)部實(shí)現(xiàn)中安聘,在 init: 方法中調(diào)用了 Designated Initializer。

其中 NS_DESIGNATED_INITIALIZER 是編譯器指令 __attribute__((objc_designated_initializer)) 的宏定義瓢棒。用來標(biāo)記 指定初始化方法浴韭。

#define NS_DESIGNATED_INITIALIZER __attribute__((objc_designated_initializer))

在一個類中,封裝 API 時脯宿,保證一個Designated Initializer念颈,Secondary Initializer 調(diào)用 Designated Initializer。

類繼承

那么對于繼承關(guān)系時连霉,應(yīng)該如何處理呢榴芳?Apple 有這么一句話:

the object should first invoke its superclass's designated initializer to initialize inherited state

在類繼承時調(diào)用任何 Designated Initializer 都是合法的嗡靡,而且應(yīng)該保證父類的 Designated Initializer 能夠得到調(diào)用,這樣會使繼承關(guān)系上的類都得以正確的初始化翠语。

雖然沒有這樣的明確規(guī)定叽躯,但 Apple 的框架都遵守這個約定财边,我們也應(yīng)該盡力遵守肌括。

當(dāng)定義一個新類 API 時,有時會有不同的方式:

  1. 不需要重寫任何初始化方法
  2. 重寫 Designated Initializer
  3. 定義一個新的 Designated Initializer

對于第一種酣难,不需要再做其他操作谍夭,只需要規(guī)范調(diào)用父類 Designated Initializer 方法即可。

對于第二種憨募,需要在重寫方法里調(diào)用 super 方法以初始化父類參數(shù)紧索,然后再進(jìn)行特定初始化。

對于第三種菜谣,確保調(diào)用了父類的 Designated Initializer珠漂,并且本類的 Secondary Initializer 調(diào)用自己的 Designated Initializer歪架。

依舊以 AFNetworking 為例存谎。AFHTTPSessionManager 繼承于 AFURLSessionManager,并定義了自己的 Designated Initializer骡送。

@interface AFHTTPSessionManager : AFURLSessionManager <NSSecureCoding, NSCopying>

+ (instancetype)manager;

- (instancetype)initWithBaseURL:(nullable NSURL *)url;

// 指定初始化方法冈敛,且與父類 AFURLSessionManager 的指定初始化方法不同
- (instancetype)initWithBaseURL:(nullable NSURL *)url
           sessionConfiguration:(nullable NSURLSessionConfiguration *)configuration NS_DESIGNATED_INITIALIZER;

@end


@implementation AFHTTPSessionManager

+ (instancetype)manager {
    return [[[self class] alloc] initWithBaseURL:nil];
}

- (instancetype)init {
    return [self initWithBaseURL:nil];
}

- (instancetype)initWithBaseURL:(NSURL *)url {
    return [self initWithBaseURL:url sessionConfiguration:nil];
}

- (instancetype)initWithSessionConfiguration:(NSURLSessionConfiguration *)configuration {
    return [self initWithBaseURL:nil sessionConfiguration:configuration];
}

- (instancetype)initWithBaseURL:(NSURL *)url
           sessionConfiguration:(NSURLSessionConfiguration *)configuration
{
    // 調(diào)用了父類的 Designated Initializer
    self = [super initWithSessionConfiguration:configuration];
    if (!self) {
        return nil;
    }

    // 省略一些代碼
    ....

    return self;
}

#pragma mark - NSSecureCoding
- (instancetype)initWithCoder:(NSCoder *)decoder {
    ...

    self = [self initWithBaseURL:baseURL sessionConfiguration:configuration];
    if (!self) {
        return nil;
    }

        ...

    return self;
}

#pragma mark - NSCopying
- (instancetype)copyWithZone:(NSZone *)zone {
    AFHTTPSessionManager *HTTPClient = [[[self class] allocWithZone:zone] initWithBaseURL:self.baseURL sessionConfiguration:self.session.configuration];

    HTTPClient.requestSerializer = [self.requestSerializer copyWithZone:zone];
    HTTPClient.responseSerializer = [self.responseSerializer copyWithZone:zone];
    HTTPClient.securityPolicy = [self.securityPolicy copyWithZone:zone];
    return HTTPClient;
}

...
@end

從這里可以看出:

  1. 子類雖然定義了新的 Designated Initializer待笑,但是依然會調(diào)用父類的 Designated Initializer;
  2. 子類的 Secondary Initializer 調(diào)用了本類的 Designated Initializer抓谴。

間接初始化方法

對于間接初始化方法暮蹂,應(yīng)盡可能地調(diào)用 指定初始化方法,以保證最終調(diào)用的是 指定初始化方法癌压。調(diào)用時對于一些參數(shù)提供默認(rèn)值或 nil仰泻。

小結(jié)

  1. 指定一個 指定初始化方法,以盡可能多的初始化參數(shù)滩届,避免出現(xiàn)意外集侯;
  2. 對于一個類來說,間接初始化方法 的實(shí)現(xiàn)應(yīng)調(diào)用 指定初始化方法丐吓;
  3. 對于繼承時有三種情況:
    1. 不重寫任何初始化方法

      正常調(diào)用

    2. 重寫指定初始化方法

      調(diào)用父類指定初始化方法

    3. 自定義一個新的初始化方法

      調(diào)用父類指定初始化方法浅悉,并本類遵循“間接初始化方法 的實(shí)現(xiàn)應(yīng)調(diào)用 指定初始化方法”

參考

https://github.com/oa414/objc-zen-book-cn/#designated-initializer

https://developer.apple.com/library/archive/documentation/Cocoa/Conceptual/Archiving/Articles/codingobjects.html#//apple_ref/doc/uid/20000948-BCIHBJDE

https://developer.apple.com/library/archive/documentation/General/Conceptual/DevPedia-CocoaCore/MultipleInitializers.html

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市券犁,隨后出現(xiàn)的幾起案子术健,更是在濱河造成了極大的恐慌,老刑警劉巖粘衬,帶你破解...
    沈念sama閱讀 221,888評論 6 515
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件荞估,死亡現(xiàn)場離奇詭異咳促,居然都是意外死亡,警方通過查閱死者的電腦和手機(jī)勘伺,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 94,677評論 3 399
  • 文/潘曉璐 我一進(jìn)店門跪腹,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人飞醉,你說我怎么就攤上這事冲茸。” “怎么了缅帘?”我有些...
    開封第一講書人閱讀 168,386評論 0 360
  • 文/不壞的土叔 我叫張陵轴术,是天一觀的道長。 經(jīng)常有香客問我钦无,道長逗栽,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 59,726評論 1 297
  • 正文 為了忘掉前任失暂,我火速辦了婚禮彼宠,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘弟塞。我一直安慰自己凭峡,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 68,729評論 6 397
  • 文/花漫 我一把揭開白布宣肚。 她就那樣靜靜地躺著想罕,像睡著了一般。 火紅的嫁衣襯著肌膚如雪霉涨。 梳的紋絲不亂的頭發(fā)上按价,一...
    開封第一講書人閱讀 52,337評論 1 310
  • 那天,我揣著相機(jī)與錄音笙瑟,去河邊找鬼楼镐。 笑死,一個胖子當(dāng)著我的面吹牛往枷,可吹牛的內(nèi)容都是我干的框产。 我是一名探鬼主播,決...
    沈念sama閱讀 40,902評論 3 421
  • 文/蒼蘭香墨 我猛地睜開眼错洁,長吁一口氣:“原來是場噩夢啊……” “哼秉宿!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起屯碴,我...
    開封第一講書人閱讀 39,807評論 0 276
  • 序言:老撾萬榮一對情侶失蹤描睦,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后导而,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體忱叭,經(jīng)...
    沈念sama閱讀 46,349評論 1 318
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡隔崎,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 38,439評論 3 340
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了韵丑。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片爵卒。...
    茶點(diǎn)故事閱讀 40,567評論 1 352
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖撵彻,靈堂內(nèi)的尸體忽然破棺而出钓株,到底是詐尸還是另有隱情,我是刑警寧澤千康,帶...
    沈念sama閱讀 36,242評論 5 350
  • 正文 年R本政府宣布享幽,位于F島的核電站,受9級特大地震影響拾弃,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜摆霉,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,933評論 3 334
  • 文/蒙蒙 一豪椿、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧携栋,春花似錦搭盾、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,420評論 0 24
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至向挖,卻和暖如春蝌以,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背何之。 一陣腳步聲響...
    開封第一講書人閱讀 33,531評論 1 272
  • 我被黑心中介騙來泰國打工跟畅, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人溶推。 一個月前我還...
    沈念sama閱讀 48,995評論 3 377
  • 正文 我出身青樓徊件,卻偏偏與公主長得像,于是被迫代替她去往敵國和親蒜危。 傳聞我的和親對象是個殘疾皇子虱痕,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 45,585評論 2 359