精通iOS开发--第15章 Grand Central Dispatch和后台处理之程序生命周期 NSNotificationCenter和线程

来源:互联网 发布:手机fps软件 编辑:程序博客网 时间:2024/05/19 22:47

第15章 Grand Central Dispatch和后台处理之程序生命周期

15.6.1 应用生命周期

我们来探讨一下应用的生命周期有哪些状态。


一个空白demo,默认设置:

- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {

    // Override point for customization after application launch.

    returnYES;

}


- (void)applicationWillResignActive:(UIApplication *)application {

    // 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 throttle down OpenGL ES frame rates. Games should use this method to pause the game.

}


- (void)applicationDidEnterBackground:(UIApplication *)application {

    // 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 {

    // Called as part of the transition from the background to the inactive state; here you can undo many of the changes made on entering the background.

}


- (void)applicationDidBecomeActive:(UIApplication *)application {

    // 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 {

    // Called when the application is about to terminate. Save data if appropriate. See also applicationDidEnterBackground:.

    // Saves changes in the application's managed object context before the application terminates.

    [selfsaveContext];

}


1活跃(Active):这是应用在屏幕上显示时正常运行的状态。它可以接受用户输入并更新显示。

示例1:以上面的代理方法为例,点击一个应用上面的方法中首先调用

application:didFinishLaunchingWithOptions:方法

随后调用

applicationDidBecomeActive:方法


2不活跃(Inactive):应用仅仅在其他状态之间临时过渡阶段处于不活跃状态。

如:显示传入呼叫或SMS提示

示例2:用户双击Home键将调用:

applicationWillResignActive:方法

随后选择此应用以回到此应用将调用

applicationDidBecomeActive:

示例3:或者用户点击电源按钮,关闭屏幕:

则会先后调用

applicationWillResignActive:

applicationDidEnterBackground:

方法。


再次打开设备时,会先后滴啊用

applicationWillEnterForeground:

applicationDidBecomeActive:

方法



示例4:还例如如果用户收到一条短信,单用户点击了短信关闭按钮,则会先后调用:

applicationWillResignActive:

applicationDidBecomeActive:

方法


如果用户没有点击关闭,而是点击了回复,则:

applicationWillResignActive:

applicationDidBecomeActive:

applicationWillResignActive:

applicationDidEnterBackground:

出乎意料。


3:后台(Background):在此状态,应用可以请求获得了一定的时间来执行一些代码,但它无法直接访问屏幕或获取任何用户输入。在用户按下主频幕按钮后不久,所有应用都会进入此状态,他们中的大部分会迅速进入挂起状态。需要在后台执行各中操作的应用会一直处于此状态,直到被再次激活。


示例5:如:应用在应用活跃时点击Home按钮,会以此调用:

applicationWillResignActive:

applicationDidEnterBackground:

若此时用户双击Home按钮,杀死程序,则会显示程序崩溃,测试发现程序applicationDidEnterBackground:后在杀死程序,会显示程序崩溃,而不是调用像预想的那样applicationWillTerminate:


示例6:若用户在活跃时双击Home

applicationWillResignActive:

随后杀死程序:

applicationDidEnterBackground:

applicationWillTerminate:(其实这个方法很少真正被调用,上面就是其中一种,,有参考书表示:此方法在被终结的应用处于后台状态时(注意不是挂起状态)也会被调用,如在后台播放音乐时被推出,那么可能调用此方法)



4挂起(Suspended)状态:挂起的应用被冻结执行。普通应用在处于后台状态后不久就会变为此状态。应用在活跃时使用所有内存将原封不动得得以保留。用过用户将应用切换回活跃状态,他将恢复到之前的状态。如果系统需要为当前活跃的应用提供更多内存,任何挂起的应用都有可能被终结,他们的内存将被释放用于其他用途。


5:未运行(Not Running):此状态表明所有应用都位于一个刚刚重新启动的设备上。在设备打开状态下,不论应用在何时启动,只有遇到以下状态才会显示为未运行状态:

    应用的Info.plist包含UIApplicationExitsOnSuspend键(并且其值设置为YES);

    应用之前被挂起且系统需要清除一些内存(此应用可能会因此被杀死)。

    应用在运行中崩溃。


15.6.5 利用执行状态更改

对于一般默认设置的程序,进入后台后很快就会被挂起:

- (void)applicationDidEnterBackground:(UIApplication *)application {

    // 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.

    [selfenterBackgroundTest];

}


- (void)enterBackgroundTest

{

    NSLog(@"%@",NSStringFromSelector(_cmd));

    dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT,0), ^{

        NSDate *date = [NSDatedate];

        for (;;) {

            [NSThreadsleepForTimeInterval:0.01];

            NSLog(@"%f",[datetimeIntervalSinceNow]);

        }

    });

}


2016-06-26 11:51:15.545 GCD[598:163194] enterBackgroundTest

2016-06-26 11:51:15.560 GCD[598:163215] -0.011277

2016-06-26 11:51:15.572 GCD[598:163215] -0.023685

2016-06-26 11:51:15.585 GCD[598:163215] -0.036447

2016-06-26 11:51:15.599 GCD[598:163215] -0.050304




如果把上面的代码改为:

- (void)enterBackgroundTest

{

    NSLog(@"%@",NSStringFromSelector(_cmd));

    dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT,0), ^{

        NSDate *date = [NSDatedate];

        for (;;) {

            [NSThreadsleepForTimeInterval:1];

            NSLog(@"%f",[datetimeIntervalSinceNow]);

        }

    });

    

    dispatch_async(dispatch_get_main_queue(), ^{

        NSDate *date = [NSDatedate];

        for (;;) {

            [NSThreadsleepForTimeInterval:1];

            NSLog(@"%f",[datetimeIntervalSinceNow]);

        }

    });

}

程序也会持续运行,但会阻塞主线程,但这肯定不是我们想要的(测试发现重新打开应用时,主线程阻塞,上一次的推出页面会和新页面重叠等等)。


如下面,主线程阻塞10秒后被挂起

- (void)enterBackgroundTest

{

    NSLog(@"%@",NSStringFromSelector(_cmd));

    dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT,0), ^{

        NSDate *date = [NSDatedate];

        for (;;) {

            [NSThreadsleepForTimeInterval:1];

            NSLog(@"%f",[datetimeIntervalSinceNow]);

        }

    });

    

    dispatch_async(dispatch_get_main_queue(), ^{

        NSDate *date = [NSDatedate];

        for (int i=0; i<10; i++) {

            [NSThreadsleepForTimeInterval:1];

            NSLog(@"%f",[datetimeIntervalSinceNow]);

        }

    });

}


    参考书中说如果applicationDidEnterBackground:被阻塞太长时间(超过5秒),系统将断定应用的行为异常并终止它(但上面的测试发现,应用完全没有被终止)。实际上使用iOS6的操作系统是发现,及时主线程已经结束,并行队列中的代码依然在运行。

    不过为了安全起见,建议还是按照苹果的要求来做,尽量在短时间内返回applicationDidEnterBackground:,如果需要更多的时间则使用beginBackgroundTaskWithExpirationHandler:




15.6.6 处理不活跃状态:

- (void)viewDidLoad {

    [superviewDidLoad];

    self.myLabel = [[UILabelalloc]initWithFrame:CGRectMake(0,0, 200,50)];

    self.myLabel.center =self.view.center;

    [self.viewaddSubview:self.myLabel];

    self.myLabel.backgroundColor = [UIColor grayColor];

    [selfrotateLabelDown];

}


- (void)rotateLabelUp

{

    [UIViewanimateWithDuration:0.5animations:^{

        self.myLabel.transform = CGAffineTransformMakeRotation(0);

    } completion:^(BOOL finished) {

        [selfrotateLabelDown];

    }];

}


- (void)rotateLabelDown

{

    [UIViewanimateWithDuration:0.5animations:^{

        self.myLabel.transform = CGAffineTransformMakeRotation(M_PI);

    } completion:^(BOOL finished) {

        [selfrotateLabelUp];

    }];

}


看上面的代码,程序运行后上面的Label会不停的旋转,双击Home键,使应用进入不活跃状态,但你依然可以看到Label在旋转




15.6.7 处理后台状态


- (void)viewDidLoad {

    [superviewDidLoad];


    [[NSNotificationCenterdefaultCenter]addObserver:selfselector:@selector(applicationWillResignActive:)name:UIApplicationWillResignActiveNotificationobject:nil];

}


- (void)applicationWillResignActive:(NSNotification*)noti

{

    NSLog(@"%@",[NSThreadcurrentThread]);//<NSThread: 0x156604eb0>{number = 1, name = main} //次线程是主线程

}


看下面这段代码:

[[NSNotificationCenterdefaultCenter]addObserver:selfselector:@selector(applicationWillResignActive:)name:@"MyTest"object:nil];

    dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT,0), ^{

        [NSThreadsleepForTimeInterval:5.0];

        NSLog(@"%@",[NSThreadcurrentThread]);

        [[NSNotificationCenterdefaultCenter]postNotificationName:@"MyTest"object:nil];

    });

}


- (void)applicationWillResignActive:(NSNotification*)noti

{

    NSLog(@"%@",[NSThreadcurrentThread]);

}


2016-06-26 14:42:44.023 GCD[712:196615] <NSThread: 0x12f608e20>{number = 2, name = (null)}

2016-06-26 14:42:46.634 GCD[712:196615] <NSThread: 0x12f608e20>{number = 2, name = (null)}



所以在通知中心中处理UI时要注意是不是在主线程。


0 0
原创粉丝点击