本文基于数据字典和数据流图两种工具讲述一个完整微博客户端的实现。数据字典和数据流图都可以用来表达线程的执行流程,同时定义了需要的类,是进一步设计类的基础。

数据字典实际上是一张表,表的第一个字段是程序代码中的标识符,其它字段具体描述它在线程中被如何使用,以及它所依赖的其它元素,数据字典中各个标识符基本上也是按照线程的执行流程来排序。

数据流图是一个平面拓扑结构,每个节点或者是外部数据,或者是可被线程执行的代码模块。从外部数据到代码模块的边意味着线程在执行代码模块的时候需要用到外部数据,从代码模块到代码模块的边则表示线程执行完成一个代码模块后跳转到另一个代码模块开始执行。其它类型的边没有具体含义。

数据字典

· 线程启动,执行函数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:makeKeyAndVisible就是一个设置主窗口的函数,表示这个窗口下的视图对象需要被渲染(rendering)。一个内存对象如果不经过渲染,也就不会对应屏幕上的元素。

数据流图(顺序图)

ps:

关于为什么要调用 performSelectorOnMainThread 才能更新界面: 因为程序员无法直接调用绘图函数,如果让程序员创建的线程来更改控件对象,那么就仅仅是改变了内存中某个实例的字段值,主线程却没有收到界面更新的事件,仍然处于阻塞状态,界面就不会更新。问题在于主线程是否会在下一次事件循环结束后更新界面。

  如果我们使用模板来新建一个project,比如Master Detail Application或者tabbed Application之类,那么Xcode将会为我们自动生成Main.storyboard文件,这样一来那些本来实在源代码中间中被定义的线程执行流程,现在全部在这个故事版文件中被定义了。用故事版当然有故事版的好处,只能用来定义的可视化元素现在允许程序员用可视化的方式来定义,不过对于并不是非常熟悉iOS编程的程序员来说,这种搭建UI的方式并不利于维护修改,因为程序员不容易看到线程的实际执行过程。

如果用代码的方式,程序员就可以看到什么时候线程创建了UIWindow的实例,并定义这个窗口要承载的界面元素,以及什么时候根据UIWindow实例中的代码和数据绘出图形。方法makeKeyAndVisible告诉了线程应该如何绘图。

主线程执行流程

iOS应用主线程执行流程如下图,方块代表了代码集合或者线程需要获得的其它资源,箭头方向代表了线程的转移方向。

上图就是主线程的声明周期。我们看看main函数:

#import <UIKit/UIKit.h>

#import "QSAppDelegate.h"

int main(int argc, char * argv[])
{
@autoreleasepool {
return UIApplicationMain(argc, argv, nil, NSStringFromClass([AppDelegate class]));
}
}

这是线程的入口,之后执行函数UIApplicationMain.

UIApplicationMain()

函数是初始化程序的核心,它接受4个参数。

int UIApplicationMain ( int argc, char *argv[], NSString *principalClassName, NSString *delegateClassName );

argc和argv:

  来自于main()接受的两个参数;

第三个参数:

  主要类的名字(principal class name),必须是UIApplication或其子类的名字,这个类的一个实例引用了当前iPhone应用,定义了事件循环的代码。它会告诉线程去读info.plist文件获取配置信息,以及Main.storyboard;如果这个参数为nil,则默认为@"UIApplication";

第四个参数:

  代理类的名字(delegate class name),线程在内存中创建一个代理类的实例,定义一些处理与操作系统相关的事件的代码。

用代码替换故事版

说了很多iOS程序执行的真正流程,我们来检验一下我们的理解吧。

假如你的工程类都是以TC开头。
1.import你的appdelegate类,并修改第四个参数如下:
UIApplicationMain(argc, argv, nil, NSStringFromClass([TCAppDelegate class]));
2.删除Main.storyboard文件
3.在工程的Info.plist文件中删除Main storyboard file base name

4、修改delegate类中定义的方法:

- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
{
self.window = [[UIWindow alloc] initWithFrame:[[UIScreen mainScreen] bounds]]; UIViewController *webPageViewController = [[WebPageViewController alloc] init];
UIViewController *personalViewController = [[PersonalViewController alloc]init];
UIViewController *meViewController = [[MeViewController alloc]init];
UINavigationController *personalNavigationController = [[UINavigationController alloc]init];
personalNavigationController.viewControllers = [NSArray arrayWithObjects:personalViewController, nil];
UINavigationController *meNavigationController = [[UINavigationController alloc]init];
meNavigationController.viewControllers = [NSArray arrayWithObjects:meViewController, nil];
_tabBarController = [[UITabBarController alloc]init];
_tabBarController.viewControllers = [NSArray arrayWithObjects:webPageViewController, personalNavigationController, meNavigationController,nil];
self.window.rootViewController = _tabBarController; _tabBarController.selectedIndex = ;
self.window.backgroundColor = [UIColor whiteColor];
[self customizeAppearance];
[self.window makeKeyAndVisible];
return YES;
}

这样就避免了根据故事版文件的信息来加载窗口以及它所承载的界面元素。

iOS开发实践:一个类微博客户端从启动到与用户交互的过程的更多相关文章

  1. iOS 开发实践之 Auto Layout

    原:http://xuexuefeng.com/autolayout/?utm_source=tuicool 本文是博主 iOS 开发实践系列中的一篇,主要讲述 iOS 中 Auto Layout(自 ...

  2. iOS开发之第三方登录微博-- 史上最全最新第三方登录微博方式实现

    相关资源地址: 本项目demo地址 :  https://github.com/zhonggaorong/weiboSDKDemo 最新SDK下载:  最新微博SDK 官网注册地址:点击打开链接 最新 ...

  3. 升讯威微信营销系统开发实践:(3)功能介绍与此项目推广过程的一些体会( 完整开源于 Github)

    GitHub:https://github.com/iccb1013/Sheng.WeixinConstruction因为个人精力时间有限,不会再对现有代码进行更新维护,不过微信接口比较稳定,经测试至 ...

  4. iOS开发实践-OOM治理

    概览 说起iOS的OOM问题大家第一想到的应该更多的是内存泄漏(Memory Leak),因为无论是从早期的MRC还是2011年Apple推出的ARC内存泄漏问题一直是iOS开发者比较重视的问题,比如 ...

  5. iOS 开发学习-类的创建与实现,与java语言的对比

    Person.h #import <Foundation/Foundation.h> @interface Person : NSObject { //在{}中定义属性(全局变量/实例变量 ...

  6. iOS开发实践之多线程(单例模式)

    单例模式的作用:可以保证在程序运行过程,一个类只有一个实例,而且该实例易于供外界访问,从而方便地控制了实例个数,并节约系统资源. 单例模式的使用场合:在这个应用程序中,共享一份资源(这份资源只需要创建 ...

  7. iOS开发:一个高仿美团的团购ipad客户端的设计和实现(功能:根据拼音进行检索并展示数据,离线缓存团购数据,浏览记录与收藏记录的批量删除等)

    大致花了一个月时间,利用各种空闲时间,将这个客户端实现了,在这里主要是想记录下,设计的大体思路以及实现过程中遇到的坑...... 这个项目的github地址:https://github.com/wz ...

  8. iOS开发笔记14:微博/微信登录与分享、微信/支付宝支付

    产品中接入了微博/微信的第三方登录分享功能.微信和支付宝的第三方支付功能,之前在开发过程中涉及到这些部分,于是抽空将接入过程梳理了一遍. 1.微博.微信.支付宝SDK相关接入设置 (1)微博SDK S ...

  9. iOS 开发自定义一个提示框

    在开发的时候,会碰到很多需要提示的地方,提示的方法也有很多种,ios 8 以前的版本有alertview还是以后用的alertController,都是这种作用, 但是不够灵活,而且用的多了,用户体验 ...

随机推荐

  1. Java多线程——<五>后台线程(daemon)

    一.后台线程(守护线程) 学一个东西,最重要的一点就是,为什么要用它? 后台线程区别于普通线程,普通线程又可以称为用户线程,只完成用户自己想要完成的任务,不提供公共服务.而有时,我们希望编写一段程序, ...

  2. 【Android自学之旅】 Android开发环境的搭建

    [Android自学之旅] Android开发环境的搭建 搭建参考教程: http://www.oracle.com/technetwork/java/javase/downloads/jdk7-do ...

  3. Proxmox虚拟机增加硬盘容量

    1.首先在虚拟机控制台选择调整硬盘容量,弹出窗口为增加的容量 2.重启虚拟机,用fdisk –l查看新增容量是否被识别 3.用cfdisk创建分区,分区格式为Primary 8e (Linux LVM ...

  4. Linq to EF 与Linq to Object 使用心得

    大家都知道Linq既可以用来查询数据库对象(我这里指的是Entity FrameWork里的Model对象),也可以用来查询内存中的IEnumerable对象. 两者单独查询时都不会出现什么问题,不过 ...

  5. [转载]Spring Autowire自动装配介绍

    转自: http://www.cnblogs.com/zhishan/p/3190757.html 在应用中,我们常常使用<ref>标签为JavaBean注入它依赖的对象.但是对于一个大型 ...

  6. 在sklearn上读取人脸数据集保存图片到本地

    程序如下: # -*- coding: utf-8 -*- """ Created on Sat Oct 31 17:36:56 2015 ""&qu ...

  7. Linux中断处理体系结构分析

    Linux中断处理体系结构分析(一) 异常,就是可以打断CPU正常运行流程的一些事情,比如外部中断.未定义指令.试图修改只读的数据.执行swi指令(Software Interrupt Instruc ...

  8. swift学习笔记-UI篇之UIImageView

    1.基本使用 将要使用的图片拖入到项目里,我这里使用的是名为“1.jpg”的图片,然后创建UIImageView,并设置要显示的图片为"1.jpg"//1. 基本使用 let im ...

  9. Yii2 的问题解决方案

    yii2 Class 'app\controllers\AccessControl' not found 一般是命名空间问题, 写成\yii\filters\AccessControl::classN ...

  10. NSSize

    #import <Foundation/Foundation.h>   int main(int argc, const char * argv[]) {    @autoreleasep ...