一句话简介:CTMediator为casa大神针对iOS组件化方案的一个架构实例。
架构详解: 传送门
Github: 传送门
PS: 本拾遗系列文章只专注于代码以及工程层面知识点拾遗,架构层面作者文章已经进行了详细的讲解。
1. UIViewController常用分段
#pragma mark - life cycle
#pragma mark - UITableViewDelegate
#pragma mark - CustomDelegate
#pragma mark - event response
#pragma mark - private methods
#pragma mark - getters and setters
这里有个争论就是关于是不是应该将属性实例的初始化放在getter中,这里个人倾向于casa的做法(放在getter中),所以贴一下相关的解释:
我比较习惯一个对象的"私有"属性写在extension里面,然后这些属性的初始化全部放在getter里面做,在init和dealloc之外,是不会出现任何类似
_property
这样的写法的。唐巧说他喜欢的做法是用
_property
这种,然后关于_property
的初始化通过[self setupProperty]
这种做法去做。从刚才上面的代码来看,就是要在viewDidLoad里面多调用一个setup方法而已,然后我推荐的方法就是不用多调一个setup方法,直接走getter。嗯,怎么说呢,其实两种做法都能完成需求。但是从另一个角度看,苹果之所以选择让
[self getProperty]
和self.property
可以互相通用,这种做法已经很明显地表达了苹果的倾向:希望每个property都是通过getter方法来获得
。早在2003年,Allen Holub就发了篇文章《Why getter and setter methods are evil》,自此之后,业界就对此产生了各种争议,虽然是从Java开始说的,但是发展到后面各种语言也参与了进来。然后虽然现在关于这个问题讨论得少了,但是依旧属于没有定论的状态。setter的情况比较复杂,也不是我这一节的重点,我这边还是主要说getter。我们从objc的设计来看,苹果的设计者更加倾向于
getter is not evil
。认为
getter is evil
的原因有非常之多,或大或小,随着争论的进行,大家慢慢就聚焦到这样的一个原因:Getter和Setter提供了一个能让外部修改对象内部数据的方式,这是evil的,正常情况下,一个对象自己私有的变量应该是只有自己关心
。然后我们回到iOS领域来,objc也同样面临了这样的问题,甚至更加严重:
objc并没有像Java那么严格的私有概念
。但在实际工作中,我们不太会去操作头文件里面没有的变量,这是从规范上就被禁止的。认为
getter is not evil
的原因也可以聚焦到一个:高度的封装性
。getter事实上是工厂方法,有了getter之后,业务逻辑可以更加专注于调用,而不必担心当前变量是否可用。我们可以想一下,假设一个ViewController有20个subview要加入view中,这20个subview的初始化代码是肯定逃不掉的,放在哪里比较好?放在哪里都比放在addsubview的地方好,我个人认为最好的地方还是放在getter里面,结合单例模式之后,代码会非常整齐,生产的地方和使用的地方得到了很好的区分。所以放到iOS来说,我还是觉得使用getter会比较好,因为evil的地方在iOS这边基本都避免了,not evil的地方都能享受到,还是不错的。
2. 应该在哪里配置View的位置?(_继续引用casa的原文)
-
关于在哪儿写Constraints?
苹果在文档中指出,
updateViewConstraints
是用来做add constraints的地方。但是在这里有一个回答者说
updateViewConstraints
并不适合做添加Constraints的事情。综合我自己和评论区各位关心这个问题的兄弟们的各种测试和各种文档,我现在觉得还是在
viewDidLoad
里面开一个layoutPageSubviews的方法,然后在这个里面创建Constraints并添加,会比较好。就是像下面这样:- (void)viewDidLoad { [super viewDidLoad]; [self.view addSubview:self.firstView]; [self.view addSubview:self.secondView]; [self.view addSubview:self.thirdView]; [self layoutPageSubviews]; } - (void)layoutPageSubviews { [self.view addConstraints:xxxConstraints]; [self.view addConstraints:yyyConstraints]; [self.view addConstraints:zzzConstraints]; }
-
生命周期方法选择
其实在viewWillAppear这里改变UI元素不是很可靠,Autolayout发生在viewWillAppear之后,严格来说这里通常不做视图位置的修改,而用来更新Form数据。改变位置可以放在viewWilllayoutSubview或者didLayoutSubview里,而且在viewDidLayoutSubview确定UI位置关系之后设置autoLayout比较稳妥。另外,viewWillAppear在每次页面即将显示都会调用,viewWillLayoutSubviews虽然在lifeCycle里调用顺序在viewWillAppear之后,但是只有在页面元素需要调整时才会调用,避免了Constraints的重复添加。
3. TableView didSelect习惯
// 选择的开始先取消选择状态
[tableView deselectRowAtIndexPath:indexPath animated:YES]
4. URL相关api
NSMutableDictionary *params = [[NSMutableDictionary alloc] init];
// 1. query: key1=value1&key2=value2
NSString *urlString = [url query];
// 2. 解析参数
for (NSString *param in [urlString componentsSeparatedByString:@"&"]) {
NSArray *elts = [param componentsSeparatedByString:@"="];
if([elts count] < 2) continue;
[params setObject:[elts lastObject] forKey:[elts firstObject]];
}
// 3. path: /index.html
NSString *actionName = [url.path stringByReplacingOccurrencesOfString:@"/" withString:@""];
if ([actionName hasPrefix:@"native"]) {
return @(NO);
}
5. 忽略不必要的警告⚠️
#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Warc-performSelector-leaks"
return [target performSelector:action withObject:params];
#pragma clang diagnostic pop