Jan 1

小记:重新理解苹果的MVC与ViewController

Lrdcq , 2018/01/01 03:00 , 程序 , 閱讀(4908) , Via 本站原創
大家日常写iOS代码的时候,习惯性的把ViewController当作界面部分的中枢,就像安卓的Activity一样的东西来写东西——所有的view新建改变,实力绑定,反正界面层那些乱七八糟的活,都让ViewController去做了。所以经常在想,MVC的Controller怎么到苹果这里变成了rootView一样界面层的中心一样的东西了这不科学。

正好周末在看OSX应用程序开发。大家都知道同属于苹果Cocoa家,UIKit是脱胎于OSX开发的AppKit的,精简也好,择优也好,总之是一脉相承的东西。稍微系统的看过AppKit的程序结构和文档后,突然顿悟UIKit的ViewController到底咋回事儿。

对比

AppKit和UIKit有这么一套相同的东西:

UIWindow -> UIViewController -> UIView
NSWindow -> NSViewController -> NSView

一般在UIKit中,我们耦合这一套关系的代码是这样的:
self.window = [[UIWindow alloc] initWithFrame:[UIScreen mainScreen].bounds];
self.window.rootViewController = [[UIViewController alloc] init];
[self.window makeKeyAndVisible];

看起来确实是UIWindow耦合UIViewController再耦合到的UIView。

但是在AppKit,事情不是这样的了:
self.window = [[NSWindow alloc] init];
NSViewController *vc = [[NSViewController alloc] init];
[self.window.contentView addSubview:vc.view];
vc.view.frame = self.window.contentView.bounds;

在AppKit里,添加到NSWindow中的不是ViewController,而是其对应的NSView了。这样的依赖关系成了
Window持有View,ViewController持有View,而Window和ViewController一毛钱关系也没有。

考虑到Window是实际上界面树的根,也就是说AppKit中ViewController和界面层一毛钱关系也没有。这才符合MVC的定义嘛。

那么回到UIKit,既然和AppKit一脉相承,那它们的本质是不是一样的呢?

1. 首先从常识上,我们就知道实际的界面树上,就没有UIViewController这么一个实体(ViewController也不是继承于UIView的嘛)。正常情况下,可以看到window.rootViewController.view在controller绑到window上后就被放到了window.subviews中。

2. 我们知道如果UIWindow的rootViewController为空,UIWindow会抛错,那么我给它塞一个view为nil的viewController会怎么样呢?经过测试,继承UIViewController直接重写- (UIView *)view返回nil。运行的结果是程序正常运行了,并且window.subview里确实就没有任何UIView了。同理在这种情况下,我直接在window.subview添加UIView当然可以正常展示,且必然没有经过UIViewController。

这里可以得出结论:UIKit中对于界面绘制UIViewController其实也不是必要的。

3. 那么UIKit到底是为了什么强需求UIViewController呢。对比两个头文件,我们注意到:

UIWindow非常简单,就没几个方法。而NSWindow非常非常复杂,上千行的头文件,还配有NSWindowController,其中包含界面生命周期回调。

相反NSViewController比UIViewController简单多了,并且在OSX10.10之前居然没有生命周期回调。

考虑到界面生命周期也是界面事件的一部分,也是从界面树传递下来了,我们注意到这里的最大的区别:UIKit必须要在ViewController里接生命周期,而AppKit在Window层就可以处理。那就明确了,显然UIWindow非要一个UIViewController是为了保证生命周期相关的事件可以传递到位,而不是为了界面展示。

结论

到此为止,我们可以下一些结论了:

1. 苹果的Cocoa界面系统中,MVC确实就是MVC,ViewController从理论上和界面展示一毛钱关系也没有,它不是界面展示的必要组成部分。

2. 从实际的UIViewController设计考虑,它确实更多是在处理界面事件和数据代理,而不是做界面处理。

3. 所以我们觉得传统写法中ViewController臃肿混乱,并不是苹果这套MVC设计有误,而是我们使用的姿势不对。

那么按照传统的设计思路,ViewController到底应该怎么用呢?我整理出以下点:

1. 代码创建,编辑界面应该在继承的UIView中进行,而不是ViewController中进行。

2. 就像讨论MVVM时说的给逻辑复杂的View配ViewModel不要一个页面一个ViewModel一样,MVC也是应该是给复杂的View配ViewController,而不是单纯的一个页面一个ViewController。

按照这个思路,我们能更清晰的理解UIKit各个类的设计意图,也能在传统的MVC模式下写出非常棒的iOS开发最佳实践了。
关键词:ios , mvc
logo