iOS 指定初始化方法
級別:★☆☆☆☆
標籤:「NS_DESIGNATED_INITIALIZER 」「NS_UNAVAILABLE」「iOS指定初始化方法」
作者: WYW
審校:QiShare團隊
前言:筆者最近了解了部分SDK開發相關的內容,在涉及指定檢視控制器(下文用VC代指檢視控制器)的初始化方法的時候,遇到了一些問題。這裡筆者給大家分享下設定方法,如有不同見解,敬請指教。
筆者遇到的問題如下:
筆者希望 使用SDK的業務能夠使用 SDK中確定的初始化VC的方法; 下邊筆者以需要傳入VC的導航欄標題並初始化相應VC為例,來闡明相關問題。 對於上述情況,有多種處理方式,如:
- 在SDK暴露的標頭檔案中,文字說明,用哪個初始化方法;在文件中說明, 用哪個初始化方法;在提供的Demo中,寫明相應地示例程式碼。
- 利用系統給的巨集NS_DESIGNATED_INITIALIZER指定vc初始化方法 指定初始化方法部分,可以使用巨集NS_DESIGNATED_INITIALIZER指定,那麼如果業務沒有看SDK標頭檔案,直接使用new 或者 alloc init的方式指定初始化方法,那麼我們初始化VC所需的導航欄標題就不能得以正常傳入。
對於這種情況,我們可以提供如下3種方法:
- 把不想業務使用的初始化方法,使用系統巨集 NS_UNAVAILABLE 把相應初始化方法設定為不可用;如禁用new ,這樣的效果是:業務如果想使用new 初始化VC,會發現有錯誤提示,使用這種方法比較簡單。
- 對於不想業務使用的初始化方法,在實現檔案中,實現相應的方法,並且給予預設值。比如說對於初始化VC的導航欄標題的情況,給定預設標題為defaultNavigationTitle。不過這種初始化方法,不適用傳入重要引數的情況,不然業務會覺得怪怪的。另外這種方式,以後如果有相關改動,可能改動程式碼會較多。
- 對於不想業務使用的初始化方法,在實現檔案中,實現相應的方法,並且丟擲崩潰,並且指定崩潰原因,在崩潰原因中說明應該使用的初始化VC的方法。對於這種方式,如果以後有改動,可能改動程式碼會較多。 相比如上三種處理方式,筆者更傾向於使用第一種,如果你有其他見解,歡迎討論。
下邊我們仍以初始化VC時,指定傳入導航欄標題為例,貼上相關的示例程式碼。
第一種方式:
// .h 檔案 - (instancetype)initWithSomething:(id)someThing NS_DESIGNATED_INITIALIZER; - (instancetype)initWithNibName:(nullable NSString *)nibNameOrNil bundle:(nullable NSBundle *)nibBundleOrNil NS_UNAVAILABLE; - (instancetype)initWithCoder:(NSCoder *)aDecoder NS_UNAVAILABLE; - (instancetype)init NS_UNAVAILABLE; + (instancetype)new NS_UNAVAILABLE; 複製程式碼
// .m 檔案 /*! @brief 導航欄title */ @property (nonatomic, copy) NSString *navTitle; - (instancetype)initWithSomething:(id)someThing { // Cannot assign to 'self' outside of a method in the init family // 指定初始化方法需要以 1. init開頭 2. 並且init後邊緊挨著地字母是大寫的 self = [super initWithNibName:nil bundle:nil]; if (!self) { return nil; } _navTitle = someThing; [self commonInit]; return self; } - (void)commonInit { self.navigationItem.title = _navTitle; } - (void)viewDidLoad { [super viewDidLoad]; self.view.backgroundColor = [UIColor whiteColor]; } 複製程式碼
第二種方式:
.h - (instancetype)initWithSomething:(id)someThing; 複製程式碼
// .m 檔案 /*! @brief 導航欄title */ @property (nonatomic, copy) NSString *navTitle; - (instancetype)initWithSomething:(id)someThing { self = [super init]; if (!self) { return nil; } _navTitle = someThing; return self; } - (instancetype)init { self = [super init]; if (!self) { return nil; } [self commonInit]; return self; } - (instancetype)initWithNibName:(NSString *)nibNameOrNil bundle:(NSBundle *)nibBundleOrNil { self = [super initWithNibName:nibNameOrNil bundle:nibBundleOrNil]; if (!self) { return nil; } [self commonInit]; return self; } - (instancetype)initWithCoder:(NSCoder *)aDecoder { self = [super initWithCoder:aDecoder]; if (!self) { return nil; } [self commonInit]; return self; } - (void)commonInit { _navTitle = @"Default"; } - (void)viewDidLoad { [super viewDidLoad]; self.navigationItem.title = _navTitle; self.view.backgroundColor = [UIColor whiteColor]; } 複製程式碼
第三種方式:
// .h 檔案 - (instancetype)initWithSomething:(id)someThing; 複製程式碼
// .m 檔案 static NSString *const kExceptionName = @"初始化方法有誤"; static NSString *const kExceptionReason = @"請使用initWithSomething:進行初始化"; /*! @brief 導航欄title */ @property (nonatomic, copy) NSString *navTitle; - (void)viewDidLoad { [super viewDidLoad]; self.navigationItem.title = _navTitle; self.view.backgroundColor = [UIColor whiteColor]; } + (instancetype)new { @throw [[self class] initExceptioin]; return nil; } - (instancetype)init { @throw [[self class] initExceptioin]; return nil; } - (instancetype)initWithCoder:(NSCoder *)aDecoder { @throw [[self class] initExceptioin]; return nil; } - (instancetype)initWithSomething:(id)someThing { self = [super init]; if (!self) { return nil; } _navTitle = someThing; return self; } + (NSException *)initExceptioin { return [NSException exceptionWithName:kExceptionName reason:kExceptionReason userInfo:nil]; } 複製程式碼