Fork me on GitHub

iOS开发之自定义View的一些坑

我们做几个简单的例子哈

自定义一个View

View的m文件中有代码如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
@implementation BMView

- (instancetype)init {
if (self = [super init]) {
NSLog(@"init");
}
return self;
}

- (instancetype)initWithFrame:(CGRect)frame {
if (self = [super initWithFrame:frame]) {
NSLog(@"initWithFrame");
}
return self;
}

@end

使用如下代码:

1
2
3
4
5
6
7
8
9
10
11
NSLog(@"new ");
[BMView new];
NSLog(@"\n ");

NSLog(@"alloc init");
[[BMView alloc] init];
NSLog(@"\n ");

NSLog(@"alloc initWithFrame");
[[BMView alloc] initWithFrame:CGRectMake(100, 100, 100, 100)];
NSLog(@"\n ");

输出如下:

1
2
3
4
5
6
7
8
9
10
11
12
02.165 ViewDemo[32765:1250187] new 
02.165 ViewDemo[32765:1250187] initWithFrame
02.165 ViewDemo[32765:1250187] init
02.166 ViewDemo[32765:1250187]

02.166 ViewDemo[32765:1250187] alloc init
02.166 ViewDemo[32765:1250187] initWithFrame
02.166 ViewDemo[32765:1250187] init
02.166 ViewDemo[32765:1250187]

02.167 ViewDemo[32765:1250187] alloc initWithFrame
02.167 ViewDemo[32765:1250187] initWithFrame

我们发现

  • 使用newalloc init会调用 initWithFrame init方法
  • 使用initWithFrame会只会调用 initWithFrame方法

自定义BMTableView

UITableView1的m文件中有代码如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
@implementation BMTableView

- (instancetype)init {
if (self = [super init]) {
NSLog(@"init");
}
return self;
}

- (instancetype)initWithFrame:(CGRect)frame {
if (self = [super initWithFrame:frame]) {
NSLog(@"initWithFrame");
}
return self;
}

- (instancetype)initWithFrame:(CGRect)frame style:(UITableViewStyle)style {
if (self = [super initWithFrame:frame style:style]) {
NSLog(@"initWithFrame style");
}
return self;
}

@end

使用如下代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
NSLog(@"new");
[BMTableView new];
NSLog(@"\n");

NSLog(@"alloc init");
[[BMTableView alloc] init];
NSLog(@"\n");

NSLog(@"alloc initWithFrame");
[[BMTableView alloc] initWithFrame:CGRectMake(100, 100, 100, 100)];
NSLog(@"\n");

NSLog(@"alloc initWithFrame style");
[[BMTableView alloc] initWithFrame:CGRectMake(100, 100, 100, 100) style:UITableViewStylePlain];
NSLog(@"\n");

输出如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
43.615 ViewDemo[32847:1259789] new
43.637 ViewDemo[32847:1259789] initWithFrame style
43.637 ViewDemo[32847:1259789] initWithFrame
43.637 ViewDemo[32847:1259789] init
43.638 ViewDemo[32847:1259789]
43.638 ViewDemo[32847:1259789] alloc init
43.639 ViewDemo[32847:1259789] initWithFrame style
43.639 ViewDemo[32847:1259789] initWithFrame
43.640 ViewDemo[32847:1259789] init
43.640 ViewDemo[32847:1259789]
43.640 ViewDemo[32847:1259789] alloc initWithFrame
43.641 ViewDemo[32847:1259789] initWithFrame style
43.641 ViewDemo[32847:1259789] initWithFrame
43.642 ViewDemo[32847:1259789]
43.642 ViewDemo[32847:1259789] alloc initWithFrame style
43.642 ViewDemo[32847:1259789] initWithFrame style

我们发现

  • 使用newalloc init会调用 initWithFrame style initWithFrame init方法
  • 使用initWithFrame会调 initWithFrame style initWithFrame方法
  • 使用initWithFrame style会只会调用 initWithFrame style 方法

why

为什么我们只是单纯的调用了 new 或者 init 就会跑到了各种构造方法中呢?感觉有一些奇怪,其实是见怪不怪。我们可以通过Chameleon查看UIViewUITableView的底层实现便一目了然。

UIView.m

UIViewm文件中可看到如下代码UIView.m

1
2
3
4
5
6
7
8
9
10
11
12
- (id)init
{
return [self initWithFrame:CGRectZero];
}

- (id)initWithFrame:(CGRect)theFrame
{
if ((self=[super init])) {
// ...
}
return self;
}

UITableView.m

UITableViewm文件中可看到如下代码: UITableView.m

1
2
3
4
5
6
7
8
9
10
11
12
- (id)initWithFrame:(CGRect)frame
{
return [self initWithFrame:frame style:UITableViewStylePlain];
}

- (id)initWithFrame:(CGRect)frame style:(UITableViewStyle)theStyle
{
if ((self = [super initWithFrame:frame])) {
// ...
}
return self;
}

看到源码后当然就一目了然了,所以我们在自定义一些UI控件时不要盲目的重写各种构造方法,感觉好高大上,其实就是一个大坑😀,之前博主也踩过😭(可能是我孤陋寡闻了)。感谢团队中阿海的指出。随便提醒下,我们可以通过https://github.com/BigZaphod/Chameleon知道天天使用的UITableView为什么那么纵享丝滑,或者各种好玩的东西。

如何编写initWith... 方法

自定义一个类BMObj,编写如下代码,是否有不合适的地方?

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
- (instancetype)init {
if (self = [super init]) {
//...
}
return self;
}

- (instancetype)initWithName:(NSString *)name age:(int)age {
if (self = [super init]) {
_name = name;
_age = age;
//...
}
return self;
}

上面的写法其实有一些问题,正确的编写如下:

1
2
3
4
5
6
7
8
9
10
11
- (instancetype)init {
return [self initWithName:nil age:0];
}

- (instancetype)initWithName:(NSString *)name age:(int)age {
if (self = [super init]) {
_name = name;
_age = age;
}
return self;
}

之所以这样编写init...是为了达到和系统init...一样的目的,可移步这里查看究竟。同时可以查看一些第三方的写法也如此YYCache

参考

- END -
关注微信公众号,发现更多精彩

文章作者:梁大红

特别声明:若无特殊声明均为原创,转载请注明,侵权请联系

版权声明:署名-非商业性使用-禁止演绎 4.0 国际