在我們的日常開發中我們經常會定義一些自己的子類繼承一些UIKit 庫中的類,那我們應該如何重寫的這些初化方法呢?那我們先看看這些類有哪些初初化方法吧。(這里就用UIView為例)
- (id)init;
- (nullable instancetype)initWithCoder:(NSCoder *)aDecoder NS_DESIGNATED_INITIALIZER;
- (instancetyp)initWithFrame:(CGRect)frame NS_DESIGNATED_INITIALIZER
我們先說說這個幾個方法的執行順序吧,init 方法我們知道它是基類NSObject 類中繼承過來,應該是最基本的方法了,返回一個自己的對象。initWithCoder 這個是我們用IB初始View來調用的。-initWithFrame方法呢我們暫時先不說先往下看。
有時候我們經常寫一些的自定義東西我們想把這些東西開源出去,(當然我們很多時候都在用開源東西)。我們就想寫的很完美,我們就會重載所有的初始方法,我們先來定義一個UIView 子類MyView然后重寫這些方法
#import "MyView.h"
@implementation MyView{
UIView *subView;
}
- (id)initWithFrame:(CGRect)frame {
self = [super initWithFrame:frame];
if (self) {
[self setup];
NSLog(@"初始化-------%@",NSStringFromSelector(_cmd));
}
return self;
}
- (id)init {
self = [super init];
if (self) {
[self setup];
NSLog(@"初始化-------%@",NSStringFromSelector(_cmd));
}
return self;
}
- (id)initWithCoder:(NSCoder *)aDecoder {
self = [super initWithCoder:aDecoder];
if (self) {
NSLog(@"初始化-------%@",NSStringFromSelector(_cmd));
}
return self;
}
- (void)setup {
subView = [[UIView alloc] initWithFrame:CGRectMake(10, 10, 20, 20)];
subView.backgroundColor = [UIColor redColor];
[self addSubview:subView];
}
@end
我們先看看在IB中創建一個view 把class 設成MyView 看看方法的調用情況:
我們跑下程序看看輸出情況是不是像我們所說的那樣:
輸出結果是調用了 initWithCoder.
所以如果我們想讓我們的類支持IB我們可以重寫這個方法然后初始化一些東西。
下面我們來試試的手動添加的我們先用initWithFrame方法:
#import "ViewController.h"
#import "MyView.h"
@interface ViewController ()
@end
@implementation ViewController
- (void)viewDidLoad {
[super viewDidLoad];
// Do any additional setup after loading the view, typically from a nib.
MyView *myView = [[MyView alloc] initWithFrame:CGRectMake(10, 20, 100, 100)];
myView.backgroundColor = [UIColor purpleColor];
[self.view addSubview:myView];
}
- (void)didReceiveMemoryWarning {
[super didReceiveMemoryWarning];
// Dispose of any resources that can be recreated.
}
@end
我們再來運行一下看看結果:
也符合我們的預期。那我們來看看最后一種情況了,因為我們現在的項目中越來越多人采用AutoLayout了初始化的時候可能調用下init就完了。不會去調用initWithFrame方法,那我們看看我們在重寫了這三個方法后調用init初始化會出現什么情況?我稍加修改一下上面的代碼:
- (void)viewDidLoad {
[super viewDidLoad];
// Do any additional setup after loading the view, typically from a nib.
MyView *myView = [[MyView alloc] init];
myView.frame = CGRectMake(10, 20, 100, 100);
myView.backgroundColor = [UIColor purpleColor];
[self.view addSubview:myView];
}
看看輸出結果是什么?
奇怪了竟然先輸出了initWithFrame ,然后才輸出了init 為什么會這樣子呢?如果你看過我的上篇文章你就應該懂了,我們可以回到最上面看看發現這個兩個方法后面的都有NS_DESIGNATED_INITIALIZER這么一行字。什么意思呢?Objective-C 有指定初始化方法(designated initializer)和間接(secondary initializer)初始化方法的觀念。 designated 初始化方法是提供所有的參數,secondary 初始化方法是一個或多個,并且提供一個或者更多的默認參數來調用 designated 初始化的初始化方法。由此我們可以看出init 應該是個secondary initializer 初始方法,當我們調用** [super init] 時候父類應該是去調用designated initializer 方法 initWithFrame 方法。所以我們不應該在我們的類里去重寫secondary initializer 方法**。如果像這樣子都重寫了那就會調用兩遍我們的setup方法。很顯然這樣子是沒有必要的。同時也會出現問題如果像我們上面那樣的寫法我們就會添加兩個subView到同一個地方,這顯然不是我們想要的結果所以我們應該避免這樣的情況出現。這里我打下結果出來的可以自已嘗試下看看:
所以當我們定義一個子類時:
- 不需要重載任何初始化函數(當然這個情況不太常用,我們要初始化一些我們自己東西)
- 重載 designated initializer(上面的我們只要重寫initWithFrame 方法即可,如果要支持IB再重寫initWithCoder 就可以了,完全沒有必要再去重寫init 當然你可以只重寫的 init 不重寫initWithFrame這樣子也不會出現二次調用的問題,但這樣子使用者可以使用initWithFrame方法初始化這樣子就會導致一些東西會被沒有初化)
-
定義一個新的 designated initializer
以此類推讀者可以嘗試的看看UIViewController 的。(這篇文章算是上一篇文章的補充吧)。