用结构体初始化大量布局常数

前言

在平时UI适配开发中,可能会用到Masonry或者手写布局,在这些布局中,经常会用到一些常量,例如:

1
2
3
4
5
6
- (void)addSubviewsConstraints
[self.myView mas_makeConstraints:^(MASConstraintMaker *make) {
make.left.top.mas_equalTo(self.view).offset(10);
make.width.height.mas_equalTo(100);
}];
}

或者:

1
2
3
4
5
- (void)layoutSubviews
{
[super layoutSubviews];
self.myView.frame = CGRectMake(10, 10, 100, 100);
}

上面只是两个固定值的例子,在Masonry中,offset(10)是一个间距值,对应手写frame布局就是origin.xorigin.y,这些值很可能是一些常数,一般不会更改,不过,如果肆意任这些常数散落在代码中,对于后期维护,可能会带来不小的麻烦。

内容

1.一般做法

针对上述的麻烦,一般的做法是,布局时,统一初始化一些常数,后期维护,只是更改这些常数就好了。例如:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
//布局常数
CGFloat myViewLeftGap = 10, myViewTopGap = 10;
CGSize myViewSize = CGSizeMake(100, 100);
//Masonry
- (void)addSubviewsConstraints
[self.myView mas_makeConstraints:^(MASConstraintMaker *make) {
make.left.mas_equalTo(self.view).offset(myViewLeftGap);
make.top.mas_equalTo(self.view).offset(myViewTopGap);
make.size.mas_equalTo(myViewSize);
}];
}
//frame
- (void)layoutSubviews
{
[super layoutSubviews];
self.myView.frame = CGRectMake(myViewLeftGap, myViewTopGap, myViewSize.width, myViewSize.height);
//或者
self.myView.frame = (CGRect){myViewLeftGap, myViewTopGap, myViewSize};
}

这样做看似麻烦,但是后期改起来,或者是调试bug时,我们只要关心常量的初始化部分就好了,不用再去看对应的布局代码,这就很省心了。

2.使用结构体统一管理的优化

上面的做法相对已经好了很多,但是,对于复杂的页面,页面的视图元素比较多时,这种方法会显得很臃肿,你要在布局代码前,初始化大量的常量,这种做法很不好,而且,在系统执行布局刷新时,对应的常数,再次被初始化了一遍,即便值没有更改,这就造成了不必要的开销。
对于这个问题,我们这里引入了结构体,结构体在iOS开发中,一般会很少使用,但其实系统中有很多例子,之前提到的CGRectCGSize都是结构体这种数据结构。
下面是利用结构体对之前的做法的一种优化:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
/**
声明布局常量结构体
*/
typedef struct {
CGFloat myViewLeftGap;
CGFloat myViewTopGap;
CGSize myViewSize;
} MyLayoutConsts;

@interface ViewController ()
@property (nonatomic, strong) UIView * myView;
@end

@implementation ViewController
{
MyLayoutConsts _layout; //结构体全局变量
}

#pragma mark - Life Cycle
- (void)viewDidLoad
{
[super viewDidLoad];

//初始化结构体,结构体的初始化有很多种方式,我采用的这种,会显得比较清晰,便于修改
_layout = (MyLayoutConsts){
.myViewLeftGap = 20,
.myViewTopGap = 20,
.myViewSize = CGSizeMake(100, 100)
};
[self.view addSubview:self.myView];
}

#pragma mark - UI
- (void)viewWillLayoutSubviews
{
[super viewWillLayoutSubviews];

self.myView.frame = (CGRect){_layout.myViewLeftGap, _layout.myViewTopGap, _layout.myViewSize};
}


#pragma mark - Lazy Load
- (UIView *)myView
{
if (!_myView) {
_myView = [[UIView alloc] initWithFrame:CGRectZero];
_myView.backgroundColor = [UIColor redColor];
}
return _myView;
}
@end

这里使用MyLayoutConsts _layout;而没有使用属性,是为了节省调用get方法的开销,本质是一样的。
当然,这里要注意,_layout.myViewLeftGap语句本质上是selg->_layout.myViewLeftGap,所以要注意在block中的循环引用的问题。

Demo详见StructForLayoutDemo

参考

  1. iOS开发中结构体的另一种“初始化方法”
  2. 结构体的4种初始化方式

本文作者:霖溦
本文链接:https://kukumalucn.github.io/blog/2018/08/09/用结构体初始化大量布局常数/
版权声明:本博客所有文章除特别声明外,均采用 CC BY-NC-ND 4.0 许可协议。转载请注明出处!

坚持原创技术分享,您的支持将鼓励我继续创作!