UIViewController的误用

简介:

转载本文请保留以下原作者信息: 原作:OneVhttp://www.onevcat.com/2012/02/uiviewcontroller/

什么是UIViewController的误用

UIViewController是iOS开发中最常见也最重要的部件之一,可以说绝大多数的app都用到了UIViewController来管理页面的view。它是MVC的核心结构和桥梁构成,可以说UIViewController是绝大多数开发者所花时间最多的部分了。

但是正是这样一个重要的类却经常被误用,从而导致app的不稳定,莫名奇妙的bug甚至无法通过appstore的审查。最常见和最可怕的误用在于在一个UIViewController里加入本来不应该由它管理的其他UIViewController,也即违反了Apple在开发者文档中关于UIViewController的描述:

Each custom view controller object you create is responsible for managing all of the views in a single view hierarchy. In iPhone applications, the views in a view hierarchy traditionally cover the entire screen, but in iPad applications they may cover only a portion of the screen. The one-to-one correspondence between a view controller and the views in its view hierarchy is the key design consideration. You should not use multiple custom view controllers to manage different portions of the same view hierarchy. Similarly, you should not use a single custom view controller object to manage multiple screens worth of content.

一个ViewController应该且只应该管理一个view hierarchy,而通常来说一个完整的view hierarchy指的是整整占满的一个屏幕。而很多app满屏中会有各个区域分管不同的功能,一些开发者喜欢直接新建一个ViewController和一套相应的View来完成所要的功能(比如我自己=_=)。虽然十分方便,但是却面临很多风险..

一般来说,只要你的代码中含有类似这样的语句,那你一定是误用UIViewController了

viewController.view.bounds = CGRectMake(50, 50, 100, 200);
[viewController.view addSubview:someOtherViewController.view];

这样的代码可能导致莫名的bug,也会令接手的开发者无所适从。

小题大做吧,这样用会有什么问题呢

一个很麻烦的问题是,这将会导致你的app在不同的iOS版本上有不同的表现。在iOS5之前,能够对viewController进行管理的类有UINavigationController,UITabBarController和iPad专有的UISplitViewController。而在iOS5中加入了可自定义的ViewControllers的容器。由于新的SDK的处理机制,iOS4前通过addSubview加到当前controller的view上的view的呈现,将不会触发被加入view hierarchy的view的controller的viewWillAppear:方法。而且,新加入的viewController也不会接收到诸如didReceiveMemoryWarning等委托方法,而且也不能响应所有的旋转事件!而iOS5中由于所谓的custom container VC的出现,上述方法又能够运行良好,这导致了同样代码在不同终端产生不同的行为,为之后的维护和进一步开发埋下了隐患。另外,用这样的方法所添加的viewController显然违背了Apple的本意,它的parentViewController,interfaceOrientation显然都是错误的,有时候甚至会出现触摸事件无法响应等严重问题。

好吧,那我们要怎么办

如果你已经在一个app里这样误用了大量的viewController,那可能的办法也许是尽力去自行处理各种非正常的状况,比如在addSubview之后手动调用加入的vc的viewWillAppear:,以及在收到didReceiveMemoryWarning后顺次调用子VC的didReceiveMemoryWarning(显然都是很蛋疼的做法啊)。但是需要注意的是iOS5中这些方法的调用似乎是没有问题的(至少我测试是这样),因此需要对不同版本系统进行分别处理。可以用UIDevice的方法确定运行环境的系统版本:

// System Versioning Preprocessor Macros
#define SYSTEM_VERSION_EQUAL_TO(v) ([[[UIDevice currentDevice] systemVersion] compare:v options:NSNumericSearch] == NSOrderedSame)
#define SYSTEM_VERSION_GREATER_THAN(v) ([[[UIDevice currentDevice] systemVersion] compare:v options:NSNumericSearch] == NSOrderedDescending)
#define SYSTEM_VERSION_GREATER_THAN_OR_EQUAL_TO(v) ([[[UIDevice currentDevice] systemVersion] compare:v options:NSNumericSearch] != NSOrderedAscending)
#define SYSTEM_VERSION_LESS_THAN(v) ([[[UIDevice currentDevice] systemVersion] compare:v options:NSNumericSearch] == NSOrderedAscending)
#define SYSTEM_VERSION_LESS_THAN_OR_EQUAL_TO(v) ([[[UIDevice currentDevice] systemVersion] compare:v options:NSNumericSearch] != NSOrderedDescending)

在合适的时机判定判定系统版本,手动调用对应方法:

if (SYSTEM_VERSION_LESS_THAN(@“5.0”))
{ 
    //viewWillAppear或didReceiveMemoryWarning或其他
}

显然,这样的代码既非优雅亦难维护,而且随着iOS版本的更新,谁也不知道这段代码之后会不会有什么问题,无形中增加了开发成本。

真正的解决之道

当然是严格遵守Apple提供的设计规范,每个VC管理一个view hierarchy。在设计的时候,永远记住你的view和view controller都需要重用,而不恰当的使用view controller会导致重用性大打折扣。而通用的view有时也需要一个类似controller的东西来管理它的subview的行为,或者做出某些相应,这个时候我们不妨想一想一些Apple写的经典的view是如何实现的,比如UITableView和UIPickerView,依靠protocol的各种方法进行配置。 作为自定义的view的controller应当是直接继承自NSObject的类,而不应该是UIViewController。一个UIViewController可以包含若干个这样的controller来控制一个view中的不同部分的功能实现,而对于对应的自定义view是代码写的还是nib出来的就无所谓了。当然,如果是新接触iOS开发的话,我个人不建议使用Interface Builder,除非你确实清楚IB到底在背后为你做了什么。在当你完全清楚之后,IB确实能极大提升开发效率(特别是在Xcode4以后),但是如果你的对IB和view加载连接的概念如同毛线团的话,IB的使用只会让你以及让你的同事茫然失措。 在iOS5中提供了所谓的container of View Controllers,有兴趣的童鞋可以参看WWDC 2011的Session 102 – Implementing UIViewController Containment(需要一个野生开发者账号)

一些资料

作为iOS开发者,Apple的关于UIViewController的文档以及开发者的一些讨论是必读的,简单整理如下:

相关文章
|
8月前
|
Swift iOS开发 开发者
什么是 UIViewController 生命周期?
什么是 UIViewController 生命周期?
60 5
|
8月前
|
iOS开发 容器
什么是 UINavigationController 和 UITabBarController?它们有什么作用?
什么是 UINavigationController 和 UITabBarController?它们有什么作用?
52 2
一个UIWindow引起的奇怪问题
# 问题现象 在2月20日接到了一个bug,系统分享页面无法响应事件。比如在POI详情页,点击底部的分享-更多-备忘录,在这个界面(下图)会使任何交互失效,除了杀app别无他法。 ![](https://ata2-img.oss-cn-zhangjiakou.aliyuncs.com/neweditor/2fd68bb3-8447-4c41-9933-b05ae964339b.png) # 问题定
169 0
一个UIWindow引起的奇怪问题
|
Android开发 iOS开发
iOS开发:KVC与KVO
KVC 就是键值编码(key-value-coding),可以直接访问对象的属性,或者给对象的属性赋值。黑魔法之一,很多高级的iOS开发技巧都是基于KVC实现的。 KVO 是键值观察者(key-value-observing)。实现方式:通过对某个对象的某个属性添加观察者,当该属性改变,就会调用”observeValueForKeyPath:”方法,为我们提供一个“对象值改变了!”的时机进行一些操作。
250 0
iOS开发:KVC与KVO
|
C语言 iOS开发
OC 底层知识(二): KVO
OC 底层知识(二): KVO
197 0
OC 底层知识(二): KVO
|
iOS开发
iOS - isa、superclass指针,元类superclass指向基类本身(上)
本文已同步至掘金:iOS - isa、superclass指针,元类superclass指向基类本身
iOS - isa、superclass指针,元类superclass指向基类本身(上)
|
存储 编译器 iOS开发
iOS - isa、superclass指针,元类superclass指向基类本身(下)
本文已同步至掘金:iOS - isa、superclass指针,元类superclass指向基类本身
iOS - isa、superclass指针,元类superclass指向基类本身(下)
|
Java iOS开发
【iOS 开发】Objective - C 面向对象 - 方法 | 成员变量 | 隐藏封装 | KVC | KVO | 初始化 | 多态(二)
【iOS 开发】Objective - C 面向对象 - 方法 | 成员变量 | 隐藏封装 | KVC | KVO | 初始化 | 多态(二)
141 0
|
存储 安全 C语言
【iOS 开发】Objective - C 面向对象 - 方法 | 成员变量 | 隐藏封装 | KVC | KVO | 初始化 | 多态(一)
【iOS 开发】Objective - C 面向对象 - 方法 | 成员变量 | 隐藏封装 | KVC | KVO | 初始化 | 多态(一)
213 0
KVO分析
KVO 概述 KVO的全称是NSKeyValueObserving,对象采用的一种非正式协议,当其他对象的指定属性发生变化时,通知对象。由于KVO的机制,只对对象的属性起作用,一般继承自NSObject都支持KVO。
2338 0

热门文章

最新文章