iOS 应用程序生命周期

iOS 应用程序一般都是由自己编写的代码和系统框架(system frameworks)组成,系统框架提供一些基本infrastructure给所有 app 来运行,而你提供自己编写的代码来定制app的外观和行为。因此,了解iOS infrastructure 和它们如何工作对编写app是很有帮助的。

iOS 应用程序的启动执行顺序

启动顺序

首先,来了解一下这张图


iOS 程序启动图

以上,就是一个应用程序的执行顺序。接下来,让我们具体的了解一下这个流程

  1. 程序入口:
    执行 main 函数,设置 AppDelegate 称为函数的代理
  2. 程序完成加载
    [AppDelegate application:didFinishLaunchingWithOptions:]
  3. 创建 window 窗口
  4. 程序被激活
    [AppDelegate applicationDidBecomeActive:]
  5. 当点击 home 键时:
    程序取消激活状态
    [AppDelegate applicationWillResignActive:]
    程序进入后台
    [AppDelegate applicationDidEnterBackground:]
  6. 点击进入项目工程中:
    程序进入前台
    [AppDelegate applicationWillEnterForeground:]
    程序重新激活
    [AppDelegate applicationDidBecomeActive:]

iOS 程序的状态

从上面的这个流程,我们可以发现它包括几个状态:后台、前台、激活、未激活。其实,iOS的应用程序共有5种状态:

  • Not running(未运行):程序未启动
  • Inactive(未激活):其他两个状态切换时出现的短暂状态。当用户锁屏或者系统提示用户去响应 Alert窗口(如来电、信息等)时
  • Active(激活):在屏幕下显示正常的运行状态,该状态下可以接受用户输入并及时更新显示
  • Background(后台)::程序在后台且能执行代码。用户按下Home键不久后进入此状态(先进入了Inactive状态,再进入Background状态),然后会迅速进入挂起状态(Suspended)。有的程序经过特殊的请求后可以长期处于Backgroud状态
  • Suspended (挂起):程序在后台不能执行代码。普通程序在进入Background状态不久后就会进入此状态。当挂起时,程序还是停留在内存中的,当系统内存低时,系统就把挂起的程序清除掉,为前台程序提供更多的内存

转换过程如图:


状态转换

程序的入口

首先,下面的函数就是我们经常看到的 main 函数

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

main函数的两个参数,iOS中没有用到,包括这两个参数是为了与标准ANSI C保持一致。我们接着看 UIApplicationMain 的参数,前两个和 main 函数的相同,重点是后面的两个,在官方文档中是这样说明的。

/ If nil is specified for principalClassName, the value for NSPrincipalClass from the Info.plist is used. If there is no  
// NSPrincipalClass key specified, the UIApplication class is used. The delegate class will be instantiated using init.  
UIKIT_EXTERN int UIApplicationMain(int argc, char *argv[], NSString *principalClassName, NSString *delegateClassName);  

后两个参数分别表示程序的主要类(principal class)和代理类(delegate class)。如果主要类(principal class)为nil,将从Info.plist中获取,如果Info.plist中不存在对应的key,则默认为UIApplication;如果代理类(delegate class)将在新建工程时创建。

AppDelegate类

不知道大家有没有认真去研究过我们工程里的 AppDelegate 这个类里的内容呢?其实,它关乎着整个应用程序的生命周期,它包含的6个类方法就是在这几个状态切换过程中调用的。
以下代码就是 AppDelegate.m 中的方法

- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
    // 程序完成载入
    NSLog(@"--- %s ---",__func__); //__func__打印方法名
    self.window = [[UIWindow alloc] initWithFrame:[UIScreen mainScreen].bounds];
    PCCTabBarController *tabBarVC = [[PCCTabBarController alloc] init];
    self.window.rootViewController = tabBarVC;
    [self.window makeKeyAndVisible];
    
    return YES;
}


- (void)applicationWillResignActive:(UIApplication *)application {
    NSLog(@"--- %s ---",__func__);
    /* 当应用程序从活动状态(active)变到非活动状态(inactive时被触发调用, 这可能发生在一些临时中断下(例如:来电话、来短信)又或者程序退出时,他会先过渡到后台然后terminate 使用这方法去暂停正在进行的任务,禁用计时器,节流OpenGL ES 帧率。在游戏中应该在这个方法里面暂停游戏。 */
    // Sent when the application is about to move from active to inactive state. This can occur for certain types of temporary interruptions (such as an incoming phone call or SMS message) or when the user quits the application and it begins the transition to the background state.
    // Use this method to pause ongoing tasks, disable timers, and invalidate graphics rendering callbacks. Games should use this method to pause the game.
}


- (void)applicationDidEnterBackground:(UIApplication *)application {
    NSLog(@"--- %s ---",__func__); //__func__打印方法名
    /* 使用这种方法来释放共享资源,保存用户数据,无效计时器,存储足够多的应用程序状态信息来恢复您的应用程序的当前状态,以防它终止丢失数据。 如果你的程序支持后台运行,那么当用户退出时不会调用applicationWillTerminate。 */
    // Use this method to release shared resources, save user data, invalidate timers, and store enough application state information to restore your application to its current state in case it is terminated later.
    // If your application supports background execution, this method is called instead of applicationWillTerminate: when the user quits.
}


- (void)applicationWillEnterForeground:(UIApplication *)application {
    /* 先从后台切换到非活动状态,然后进入活动状态。 */
    NSLog(@"--- %s ---",__func__);
    // Called as part of the transition from the background to the active state; here you can undo many of the changes made on entering the background.
}


- (void)applicationDidBecomeActive:(UIApplication *)application {
    /* 重启所有的任务,不管是从非活动状态还是刚启动程序,还是后台状态。 */
    NSLog(@"--- %s ---",__func__);
    // Restart any tasks that were paused (or not yet started) while the application was inactive. If the application was previously in the background, optionally refresh the user interface.
}


- (void)applicationWillTerminate:(UIApplication *)application {
    /* 终止*/
    NSLog(@"--- %s ---",__func__);
    // Called when the application is about to terminate. Save data if appropriate. See also applicationDidEnterBackground:.
}

接下来,就让我们新建一个工程,通过实践看一下它的状态转换过程是否和我们上面所说的一样呢?

当我们启动程序的时候,打印的结果如下:

2017-11-01 16:47:32.092347+0800 PCConnect[917:142442] --- -[AppDelegate application:didFinishLaunchingWithOptions:] ---
2017-11-01 16:47:32.432588+0800 PCConnect[917:142442] --- -[AppDelegate applicationDidBecomeActive:] ---

按下home 键时

2017-11-01 16:49:15.906334+0800 PCConnect[917:142442] --- -[AppDelegate applicationWillResignActive:] ---
2017-11-01 16:49:16.736169+0800 PCConnect[917:142442] --- -[AppDelegate applicationDidEnterBackground:] ---

重新打开 APP 时:

2017-11-01 17:51:57.815175+0800 PCConnect[2056:660773] --- -[AppDelegate applicationWillEnterForeground:] ---
2017-11-01 17:51:58.102222+0800 PCConnect[2056:660773] --- -[AppDelegate applicationDidBecomeActive:] ---

我们发现,它和我们上面说的一致。这就是 iOS 程序的状态转化的过程。最后,当我们的程序完全要退出时,将调用 applicationWillTerminate,来保存用户的一些重要数据以便下次启动时恢复到 APP 原来的状态 。

iOS 视图的生命周期

iOS 应用的视图状态分为以下几种:


视图状态

当一个视图控制器被创建到在屏幕上显示的时候,代码的执行顺序是:

  1. alloc 创建对象、分配空间
  2. init(initWithNibName) 初始化对象,初始化数据
  3. loadView 从 nib 载入视图,通常这步不需要去干涉。除非你使用纯代码布局。
  4. viewDidLoad 载入完成,可以进行自定义数据以及动态创建其他控件;
  5. viewWillAppear 视图将出现在屏幕之前
  6. viewDidAppear 视图已在屏幕上渲染完成
    而当一个视图被移除屏幕并被销毁的时候执行的顺序和上面的差不多相反,具体的如下:
  7. viewWillDisappear 视图将被从屏幕上移除之前执行
  8. viewDidDisappear 视图已经被从屏幕上移除
  9. dealloc 视图被销毁,此处需要对你在 init 和 viewDidLoad 中创建的对象进行释放

UIViewController 视图调用的方法

上述对于视图控制器从创建到销毁的过程通常包含如下几种方法,这些方法都是 UIViewController 类的方法:

  • (void)viewDidLoad;
  • (void)viewDidUnload;
  • (void)viewWillAppear:(BOOL)animated;
  • (void)viewDidAppear:(BOOL)animated;
  • (void)viewWillDisappear:(BOOL)animated;
  • (void)viewDidDisappear:(BOOL)animated;
    那么,具体每个函数的含义以及如何使用,接下来会给大家具体说明:
  1. -(void)viewDidLoad;
    一个 APP 在载入时会先调用 loadView 方法或者载入 XIB 中创建的初始界面的方法,将视图载入到内存中;然后会调用 viewDidLoad 方法来进一步的设置。
    我们会对于各种初始数据的载入,初始设定等很多内容都会在这个方法中实现。这也是,我们最常用的一个方法。
  2. -(void)viewDidUnload;
    内存足够的情况下,软件的视图通常会一直保存在内存中;一旦内存不够,一些没有显示的视图控制器就会收到内存不够的警告,然后就会释放自己拥有的视图,以达到释放内存的目的。但是系统只是释放内存,不会释放对象的所有权,所以通常我们需要在这里将不需要在内存中保留的对象释放所有权,也就是将指针置为 nil.
    这个方法通常并不会在视图变换的时候被调用,而只会在系统退出或者收到内存警告的时候才会被调用。但是由于我们需要保证在收到内存警告的时候能够对其作出反应,所以这个方法通常都需要我们去实现。
    另外,即使在设备上按了Home键之后,系统也不一定会调用这个方法,因为iOS4之后,系统允许将APP在后台挂起,并将其继续滞留在内存中,因此,viewcontroller并不会调用这个方法来清除内存。
  3. -(void)viewWillAppear:(BOOL)animated;
    系统在载入所有数据后,将会在屏幕上显示视图,这时会先调用这个方法。通常我们会利用这个方法,对即将显示的视图做进一步的设置。例如,我们可以利用这个方法来设置设备不同方向时该如何显示。
    另外,当APP有多个视图时,在视图间切换时,并不会再次载入viewDidLoad方法,所以如果在调入视图时,需要对数据做更新,就只能在这个方法内实现了。
  4. -(void)viewDidAppear:(BOOL)animated;
    有时候由于一些特殊的原因,我们不能在viewWillApper方法里对视图进行更新。那么可以重写这个方法,在这里对正在显示的视图进行进一步的设置。
  5. -(void)viewWillDisappear:(BOOL)animated;
    在视图变换时,当前视图在即将被移除、或者被覆盖时,会调用这个方法进行一些善后的处理和设置。
    由于在iOS4之后,系统允许将APP在后台挂起,所以在按了Home键之后,系统并不会调用这个方法,因为就这个APP本身而言,APP显示的view,仍是挂起时候的view,所以并不会调用这个方法。
  6. -(void)viewDidDisappear:(BOOL)animated;
    通过重写该方法,我们可以对已经消失或被覆盖或已经隐藏的视图做一些其他操作。

iOS 中 loadView 和 viewDidLoad 的区别

iOS 开发中必不可少的要用到这两个方法,他们都可以用来在视图载入的时候,初始化一些内容。但是它们的区别是什么?

  • viewDidLoad 无论你是通过xib文件还是重写loadView方法创建UIViewController的view,在view创建完毕后,最终都会调用viewDidLoad方法
  • loadView 每次访问UIViewController的view(比如controller.view、self.view)而且view为nil,loadView方法就会被调用
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 199,519评论 5 468
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 83,842评论 2 376
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 146,544评论 0 330
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 53,742评论 1 271
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 62,646评论 5 359
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,027评论 1 275
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,513评论 3 390
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,169评论 0 254
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,324评论 1 294
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,268评论 2 317
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,299评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 32,996评论 3 315
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,591评论 3 303
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,667评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 30,911评论 1 255
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 42,288评论 2 345
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 41,871评论 2 341

推荐阅读更多精彩内容