本文基于数据字典和数据流图两种工具讲述一个完整微博客户端的实现。数据字典和数据流图都可以用来表达线程的执行流程,同时定义了需要的类,是进一步设计类的基础。
数据字典实际上是一张表,表的第一个字段是程序代码中的标识符,其它字段具体描述它在线程中被如何使用,以及它所依赖的其它元素,数据字典中各个标识符基本上也是按照线程的执行流程来排序。
数据流图是一个平面拓扑结构,每个节点或者是外部数据,或者是可被线程执行的代码模块。从外部数据到代码模块的边意味着线程在执行代码模块的时候需要用到外部数据,从代码模块到代码模块的边则表示线程执行完成一个代码模块后跳转到另一个代码模块开始执行。其它类型的边没有具体含义。
数据字典
· 线程启动,执行函数main。
· int main(int argc, char *argv[])
—— 线程首先跳转到函数main所在的代码块,然后可以跳转到函数UIApplicationMain。
· int UIApplicationMain (
int argc,
char *argv[],
NSString *principalClassName,
NSString *delegateClassName
);
—— 线程调转到函数UIApplicationMain所在的代码块,然后在内存中创建两个分别引用了应用本身和应用代理的内存块,其实就是UIApplication类(子类)和SAAppDelegate类(子类)的实例;然后调转到 app delegate 实例,执行 applicationDidFinishLaunching 方法中定义的代码;然后线程跳转到UIApplication的实例,根据实例中的字段和类中定义的代码,进入等待接收事件的状态;如果线程接收到界面控件点击事件,就跳转到引用了这个控件的内存块(控件对象),根据内存块中的字段值和控件类中定义的代码执行相应的过程,最后重新等待其它事件;如果线程接收到用户终止了进程的事件,就跳转到 app delegate 实例,执行 applicationWillTerminate 中的代码,然后再跳转到main函数,执行 return 语句终止自己。
· UIApplication
—— 类UIApplication以及它在内存中的实例中定义的代码和数据, 可以告诉线程应该如何修改和获得与应用程序相关的信息,如何进入事件循环,线程等待各种事件,跳转到相应的代码位置。
· SAAppDelegate
—— 类SAAppDelegate以及它在内存中的实例中定义的代码和数据,可以告诉线程如何处理与应用程序相关的事件,如何获取数据模型等等。
· applicationDidFinishLaunching
—— 线程从UIApplication类或实例中的代码跳转到这个函数中,然后在内存中创建一个UIWindow类的实例,这个实例可以引用一个可以承载可视化元素的窗口,然后线程在内存创建一个控制器类SAMainController或SAOAuthController或SANewfeatureController类的实例,在把UIView的实例创建到内存后,调用 viewDidLoad 方法配置 view 对象中的各个字段。然后跳转到UIWindow类的代码中,设置内存实例的字段值,并把这个ViewController的实例作为窗口的根元素,然后跳转到 makeKeyAndVisible的代码中,根据根元素实例包含的UIView实例,画出窗口中的界面元素,期间会跳转到控制器实例的 viewWilAppear 等方法,执行完成后跳转回UIApplication实例,进入等待事件的状态。
数据流图(顺序图)
ps:
关于为什么要调用 performSelectorOnMainThread 才能更新界面: 因为程序员无法直接调用绘图函数,如果让程序员创建的线程来更改控件对象,那么就仅仅是改变了内存中某个实例的字段值,主线程却没有收到界面更新的事件,仍然处于阻塞状态,界面就不会更新。问题在于主线程是否会在下一次事件循环结束后更新界面。