首页 » iOS编程基础:Swift、Xcode和Cocoa入门指南 » iOS编程基础:Swift、Xcode和Cocoa入门指南全文在线阅读

《iOS编程基础:Swift、Xcode和Cocoa入门指南》11.9 事件泥潭

关灯直达底部

你的代码之所以能运行是因为Cocoa发送了事件,而你已经创建好了方法来接收这个事件。Cocoa会发送大量事件,告诉你用户做了什么事情,通知你应用进入到了生命周期中的哪个阶段及其目标是什么,等待你的输入以便继续。要想接收到监听的事件,你需要通过叫作入口点的方法来达成所愿,所谓入口点指的是这样一些方法:它们拥有正确的名字,位于正确的类中,这样就可以通过事件被Cocoa所调用。事实上,很容易就会想到,在很多情况下,一个类中的代码几乎都是入口点。

作为一名iOS程序员来说,合理安排这些入口点是面临的主要挑战之一。你知道要做什么,但却不能“想做就做”。你需要划分好应用的功能,使之与Cocoa调用你的代码的时间与方式保持一致。在编写自己的代码前,类的框架结构其实已经大致勾画出来了,这是根据要接收的Cocoa事件而实现的。

假设一个iPhone应用要显示出一个包含了表视图的界面(这种情况其实很常见)。你可能要有一个相应的UITableViewController子类;UITableViewController是个内建的UIViewController子类,你所定义的UITableViewController子类的实例将会拥有并控制表视图,同时还可能会将这个类作为表视图的数据源与委托。在这个类中,你至少需要实现如下方法:

initWithCoder:或initWithNibName:bundle:

UIViewController生命周期方法,在这里进行实例初始化。

viewDidLoad

UIViewController生命周期方法,在这里进行视图相关的初始化。

viewDidAppear:

UIViewController生命周期方法,在这里设置一些界面显示后需要使用的状态。比如,如果要注册通知或创建定时器,那么这就是一个很适合的地方。

viewDidDisappear:

UIViewController生命周期方法,这里所做的事情与viewDidAppear:正好相反。比如,可以在这里取消通知注册,或禁用在viewDidAppear:中所创建的定时器。

supportedInterfaceOrientations

UIViewController查询方法,在这里指定该视图控制器的主视图可以使用哪些设备方向。

numberOfSectionsInTableView:

tableView:numberOfRowsInSection:

tableView:cellForRowAtIndexPath:

UITableView数据源查询方法,在这里指定表的内容。

tableView:didSelectRowAtIndexPath:

UITableView委托用户动作方法,在这里对用户轻拍表的一行这一动作进行响应。

deinit

Swift类实例生命周期方法,在这里执行生命结束的清理工作。

假设你使用viewDidAppear:注册通知并创建了一个定时器。该通知有一个选择器(如果没有使用块),定时器也有一个选择器;因此,你还需要实现这两个选择器所指定的方法。

我们已经有很多方法了,其存在的目的只是作为样板代码而已。它们并不是你定义的方法;你也永远不会调用它们。它们是Cocoa的方法,放在这里就是为了能在应用生命周期的某个恰当时刻对其进行调用。

按照这种方式,程序的逻辑将变得很难理解!我这里并不是要批评Cocoa,事实上,我们很难想象其他的应用框架是如何工作的;不过,客观上来讲,Cocoa程序,甚至是你自己编写的程序,在开发时都是难以阅读的,因为包含了大量分离的入口点,每个入口点都有自己存在的意义,并且会在某个时刻被调用,然后这一切从程序的角度来看是非常晦涩的。要想理解我们假设的这个类到底在做什么,你需要知道viewDidAppear:何时会被调用,它是如何使用的,诸如此类;否则,你就完全无法理解程序的逻辑与行为,更不必说程序的代码含义了。在阅读其他人的代码时,这种痛苦还会加剧(这也是我在第8章曾说过示例代码对于初学者来说帮助并不大的原因所在)。

查看一个iOS程序的代码(甚至是你自己的代码),当看到那么多在各种情况下会被Cocoa自动调用的方法时,我相信你一定会惊呆。然而,经验会告诉你诸如重写的UIViewController方法、表视图委托以及数据源方法等。另外,即便经验再多,你也不可能知道某个方法会作为按钮的动作或通过通知被调用。注释是很有用的,我强烈建议你在开发任何iOS应用时都要对每个方法进行注释,如果需要,注释还要很详尽,写清楚方法要做的事情,以及在什么情况下会被调用:特别是如果方法是一个入口点,那么谁会调用它。

也许在编写Cocoa应用时,最常犯的错误并不是代码本身有Bug,而是将代码放到了错误的地方。代码没有运行、在错误的时间运行,或运行的顺序不对。我发现在各种在线用户论坛中,这类问题一直都有人在问(下面就是用户常问的一些问题):

·在视图出现与按钮呈现其文本之间存在延迟。

这是因为你将设置按钮文本的代码放到了viewDidAppear:中,这太迟了;代码应该更早一些运行,放在viewWillAppear:中比较合理。

·我的子视图是通过代码定位的,不过其位置全都错乱了。

这是因为你将定位子视图的代码放到了viewDidLoad中。这太早了;代码应该晚一些在视图的大小确定后再运行。

·虽然视图控制器的supportedInterfaceOrientations不允许,但视图还是可以旋转。

这是因为你在错误的类中实现了supportedInterfaceOrientations;应该在包含了视图控制器的UINavigationController中实现(或如本章前面所述,使用委托的navigationControllerSupportedInterfaceOrientations)。

·我为文本框中的Value Changed创建了动作连接,但当用户编辑时,代码并未得到调用。

这是因为你连接了错误的控件事件;文本框会发出Editing Changed而非Value Changed事件。

另外的挑战在于你不可能精确知道入口点何时会被调用。文档会给出概要性的介绍,不过在大多数情况下,对于事件何时会发生,以什么顺序发生并没有保证。你可能觉得某个事件会发生,而且文档也使你相信这个事件会发生,但可能并不会发生。你自己的代码可能会触发一些意料之外的事件。文档可能并没有清楚说明何时会发出通知。Cocoa中可能还会存在Bug,导致事件的调用方式与文档不符。你没法看到Cocoa源代码,因此也搞不清底层实现细节。因此,我建议在开发应用时,使用原始调试(println与NSLog,如第9章所示)来分析代码。在测试代码时,请密切关注控制台输出,寻找有意义的消息。你可能会对自己的发现感到惊讶。