NS_DESIGNATED_INITIALIZER

警告
  • 1: Method override for the designated initializer of the superclass '-init' not found
  • 2: Convenience initializer missing a 'self' call to another initializer
  • 3: Convenience initializer should not invoke an initializer on 'super
原因

以上3個(gè)警告是由于使用NS_DESIGNATED_INITIALIZER不規(guī)范引起的。使用場景如下:

@interface Warning : NSObject
- (instancetype)initWithName:(NSString *)name NS_DESIGNATED_INITIALIZER;
@end

@implementation Warning
- (instancetype)initWithName:(NSString *)name
{
  self = [super init];
  if (self) {
    _name = name;
  }
  return self;
}

- (instancetype)initWithNickName:(NSString *)name
{
  self = [super init];
  if (self) {
    _name = name;
  }
  return self;
}
@end

我們先看一下NS_DESIGNATED_INITIALIZER的來歷。Objective-C對(duì)象的初始化是通過[[SomeClass alloc] initXXX]來進(jìn)行的,一個(gè)類可以有多個(gè)初始化方法,通過init作為前綴來標(biāo)識(shí)。通常我們希望調(diào)用者使用指定的初始化方法來創(chuàng)建對(duì)象,因?yàn)樵谥付ǖ某跏蓟椒ㄖ袝?huì)做一些初始設(shè)置,比如給屬性設(shè)置初始值等。iOS 8以前,開發(fā)者只能通過注釋的方式來標(biāo)明哪個(gè)方法是指定的初始化方法,使用起來很不方便。在iOS 8開始,Clang提供了一個(gè)標(biāo)志__attribute__((objc_designated_initializer))來標(biāo)明指定初始化方法,也就是NS_DESIGNATED_INITIALIZER這個(gè)宏,聲明在NSObjCRuntime.h中:

#ifndef NS_DESIGNATED_INITIALIZER
#if __has_attribute(objc_designated_initializer)
#define NS_DESIGNATED_INITIALIZER __attribute__((objc_designated_initializer))
#else
#define NS_DESIGNATED_INITIALIZER
#endif
#endif

使用NS_DESIGNATED_INITIALIZER需要遵循三個(gè)規(guī)范:

  • A designated initializer must call (via super) a designated initializer of the superclass. Where NSObject is the superclass this is just [super init].
  • Any convenience initializer must call another initializer in the class - which eventually leads to a designated initializer.
  • A class with designated initializers must implement all of the designated initializers of the superclass.

這三個(gè)規(guī)范主要是為了確保使用指定初始化方法能夠得到完整初始化過的對(duì)象,尤其是在子類繼承父類的場景中。對(duì)應(yīng)的警告正是上面列出的三個(gè)警告內(nèi)容,修改后的代碼如下:

@interface Warning : NSObject
- (instancetype)initWithName:(NSString *)name NS_DESIGNATED_INITIALIZER;
@end

@implementation Warning
//Implement super class (NSObject) 's designated initializer
- (instancetype)init 
{
  return [self initWithName:@"No One"];
}

- (instancetype)initWithName:(NSString *)name
{
  self = [super init]; // Call Super Class's designated initializer
  if (self) {
    _name = name;
  }
  return self;
}

- (instancetype)initWithNickName:(NSString *)name
{
  self = [self initWithName:name]; // Call other initializer via self
  if (self) {
    _name = name;
  }
  return self;
}
@end

簡單總結(jié)起來其實(shí)就是兩條:

  • 無論通過哪個(gè)初始化方法,都要調(diào)用到該類的指定初始化方法。
  • 指定初始化方法一定要調(diào)用到父類的指定初始化方法。

Swift中其實(shí)也有同樣的規(guī)則,官方指南中有一張圖描述了上述規(guī)則:

Designated Initializers and Convenience Initializers

NS_UNAVAILABLE

有時(shí)父類的指定初始化很多,子類重寫起來比較麻煩,這時(shí)可以通過NS_UNAVAILABLE來標(biāo)明禁用父類的某些指定初始化方法即可,調(diào)用者調(diào)用NS_UNAVAILABLE標(biāo)記的方法時(shí),在編譯階段會(huì)報(bào)錯(cuò)提示。

參考文章1
參考文章2
參考文章3

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

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

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