app ios 前后台状态
2016-05-13 15:34:45 zhongbeida_xue 阅读数 2885

iOS的应用程序的生命周期,还有程序是运行在前台还是后台,应用程序各个状态的变换,这些对于开发者来说都是很重要的。 iOS系统的资源是有限的,应用程序在前台和在后台的状态是不一样的。在后台时,程序会受到系统的很多限制,这样可以提高电池的使用和用户体验。 
//开发app,我们要遵循apple公司的一些指导原则,原则如下: 
1、应用程序的状态 
状态如下: 
Not running 未运行 程序没启动 
Inactive 未激活 程序在前台运行,但不能接受事件的处理.当应用要从一个状态切换到另一个不同状态时,中途 过度会短暂停留在此状态. 
Active 激活 程序在前台运行而且接收到了事件。这是应用在前台运行时所处的正常状态. 
Backgroud 后台 程序在后台而且还在执行代码.大多数将要进入 Suspended 状态的应用,会先短暂进入此状态.如果应用请求更多额外的执行时间,该应用会在此状态保持更长一段时间.另外,如果一个应用要求启动时直接进入后台运行,这样的应用会直接从 NotRunning 状态进入 Background 状态,中途不会经过 Inactive 状态.比如,没有界面的应用(当然并不特指没有界面的应用),总之,如果应用直接进入 Background 状态,该应用界面不会被显示出来. 
Suspended 挂起 应用处在后台,并且没有执行任何代码.系统会自动将应用转入该状态,并且不会发出任何通知.当处在该状态时,应用依然驻留内存,但不执行任何程序代码.当系统发生低内存警告时,系统会将出于 Suspended 状态的应用彻底移出内存,从而为前台应用释放更多的内存.

下图是程序状态变化图: 
这里写图片描述

- (BOOL)application:(UIApplication *)application willFinishLaunchingWithOptions:(NSDictionary *)launchOptions // 告诉代理进程启动但还没进入状态保存. 应用程序将要启动时自动调用该方法,该方法是应用程序启动时第一次执行自定义代码的机会.


- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions // 告诉代理启动基本完成程序准备开始运行. 应用程序启动时自动调用该方法,开发者可以在该方法中执行初始化相关的代码.


- (void)applicationDidBecomeActive:(UIApplication *)application //应用程序入活动状态执行. 应用在转入前台,并进入活动状态时回调该方法(当应用从启动到进入前台,或从后台转入前台都会调用该方法),可重写该方法执行最后的准备工作.


- (void)applicationWillResignActive:(UIApplication *)application // 当应用程序将要入非活动状态执行(应用正要从前台运行状态离开时将会调用该方法),在此期间,应用程序不接收消息或事件,比如来电话了


- (void)applicationDidEnterBackground:(UIApplication *)application // 用程序正出于 Background 状态,且随时可能进入Suspended 状态时将会调用该方法. 所以要设置后台继续运行,则在这个函数里面设置即可


- (void)applicationWillEnterForeground:(UIApplication *)application//当程序从后台将要重新回到前台,但暂时还没有到达 Active 状态时将会调用该方法. 这个刚好跟上面的那个方法相反。


- (void)applicationWillTerminate:(UIApplication *)application//该应用程序即将被终止时调用该方法,如果应用当前处在 Suspended 状态,此方法将不会被调用..通常是用来保存数据和一些退出前的清理工作。这个需要要设置UIApplicationExitsOnSuspend的键值。


- (void)applicationDidFinishLaunching:(UIApplication*)application当程序载入后执行



在上面8个方法对应的方法中键入NSLog打印。 
现在启动程序看看执行的顺序: 
启动程序 
lifeCycle[40428:11303] willFinishLaunchingWithOptions 
lifeCycle[40428:11303] didFinishLaunchingWithOptions 
lifeCycle[40428:11303] applicationDidBecomeActive 
按下home键 
lifeCycle[40428:11303] applicationWillResignActive 
lifeCycle[40428:11303] applicationDidEnterBackground 
双击home键,再打开程序 
lifeCycle[40428:11303] applicationWillEnterForeground 
lifeCycle[40428:11303] applicationDidBecomeActive 




2016-12-08 09:48:08 potato512 阅读数 508

对于发布到app store的App,苹果在iTunes Connect后台设置了三种颜色的指示器(红、黄、绿),每种颜色代表不同的状态,这样就可以清晰地呈现给开发者


绿色

绿色指示器代表Apple已批准该App版本,并已将其发布到应用商店中。也就是说,当App下方是绿色指示器时,表示该应用正在App Store中销售且一切正常。


黄色指示器

当App下方是黄色指示器时,可能代表以下8种状态:

1、准备提交

已为App创建iTunes Connect记录,但还未做好提交App以供审核的准备。可能开发者仍在配置元数据、屏幕快照、App预览、定价、App内购买项目、Game Center、iAd App 网络设置等等。

2、正在等待审核

已提交新的App或更新的版本,而且Apple已收到,但Apple尚未开始审核该 App。

当App正在等待审核时,你可以:

  • 拒绝您的二进制文件,以将其从Apple审核队列中移除。

  • 编辑某些App信息。

3、正在审核

Apple正在审核你的App。此时,可以通过“拒绝二进制文件”将你的“二进制文件”从审核队列中移除。

4、待定合约

你的App已通过审核,且已经上架,但是你的合约尚未生效或已失效(例如,账号过期需要续费时)。此时,你可以在“协议、税务和银行业务”部分检查合约进度。

5、正在等待出口合规性检查

你的App已通过审核,而且已经上架,但Apple正在根据出口合规信息审核你的CCATS文件。

6、等待开发者发布

你的App版本已获得Apple的批准,正在等待你进行发布。准备就绪后,就可以将此版本发布到应用商店了!

7、正在为 App Store 进行处理

正在处理你的二进制文件,并且将在24小时内上架。

8、等待Apple发布

Apple保留了你的App版本,直到相应的Apple iOS或OS版本向公众发布。

此时,如果你认为App应在现有的iOS或OS版本上发布,则需要检查你的App“二进制文件“中设置的部署目标。如果你使用不同的部署目标重建App“二进制文件”,则需拒绝此版本的“二进制文件”,并提交新的版本。


红色指示器

红色指示器应该是最好理解的,如果应用被拒或者被下架,App的下方就会显示该指示器。当然,除了下架、被拒之外,该指示器也能代表“二进制文件无效”:

1、被拒绝

Apple 拒绝了该“二进制文件”。此时,具有管理员或技术职能的iTunes Connect用户会收到包含拒绝原因的通知。

2、元数据被拒绝

除“二进制文件”外,其他元数据项目尚未通过审核。详情可参见【解决方案中心】 。若要解决该问题,需要编辑iTunes Connect中的元数据。 解决问题后,可以再次提交App以供审核。

3、被下架

你的App已被下架。通常情况下,如果你的App有被下架的风险,Apple会与你联系,以尽量在App下架前解决相关问题。不过,如果是情节特别严重的违规行为等,Apple会先斩后奏。

4、被开发者拒绝

你已拒绝处于审核流程的“二进制文件”,主动将其从审核队列中移除。准备就绪后,可以重新提交或提交新的“二进制文件”。

5、被开发者下架

你主动将App从应用商店下架了。

6、二进制文件无效

Apple已收到你的二进制文件,但它未满足所有上传要求。



2017-01-05 16:13:00 weixin_33766168 阅读数 39

前言

最近项目需求加上状态恢复, 记得之前在书上看过, 这次单独抽出这个功能实现详细梳理一下, 方便自己温习一下, 也方便不知道的 developer 学习.


状态恢复?

举个栗子:

在使用名字为 A 的 app 时, 从列表页面进入详情页面, 这时你不想看了, 点击 Home 键, 回到后台, 打开 B 开始玩. 过了一段时间之后, 由于 A 没有写后台运行的功能, 这时, 系统会关闭 A, 再打开时, 你看到的是之前进入的详情页面.

系统一点的话说就是, 系统在进入后台时会保存 app 的层次结构, 在下一次进入的时候会恢复这个结构中所有的 controller. 系统在终止之前会遍历结构中每一个节点, 恢复标识, 类, 保存的数据. 在终止应用之后, 系统会把这些信息存储在系统文件中.


恢复标识

一般和对象的类名相同, 其类被称为恢复类.


实现

下面通过一个 demo 演示状态恢复的实现, 这个 demo 是一个保存联系人信息的 demo. 以下代码以 demo 中控制器为例. 建议 demo 和本文一起看, 更好理解.

demo地址

1. 开启

默认情况下, app 的状态恢复是关闭的, 需要我们手动开启.
在 AppDelegate.m 中手动打开:

#pragma mark - open state restoration

// 和NSCoding协议方法有点像, encode, decode
- (BOOL)application:(UIApplication *)application shouldSaveApplicationState:(NSCoder *)coder {
    return YES;
}

- (BOOL)application:(UIApplication *)application shouldRestoreApplicationState:(NSCoder *)coder {
    return YES;
}

系统在保存 app 状态时, 会先从 root VC 去查询是否有restorationIdentifier属性, 如果有, 则保存状态, 继续查询其子控制器, 有则保存. 直到找不到带有restorationIdentifier的子控制器, 系统会停止保存其与其子控制器的状态.

画个图解释一下:

1323461-210f8d25cfad0aae.jpeg
示意图

上图三级 VC 即使有restorationIdentifier也不会恢复.

application:willFinishLaunchingWithOptions:方法会在启用状态恢复之前调用, 我们需要将触发启用方法之前的代码写在这个方法中.

- (BOOL)application:(UIApplication *)application willFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
    
    self.window = [[UIWindow alloc] init];
    self.window.frame = [UIScreen mainScreen].bounds;
    self.window.backgroundColor = [UIColor whiteColor];
    
    return YES;
}

然后为根视图控制器添加恢复标识:

- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
    
    // 如果没有触发恢复, 则重新设置根控制器
    if (!self.window.rootViewController) {
        
        YNMainTableController *table = [[YNMainTableController alloc] init];
        
        UINavigationController *nav = [[UINavigationController alloc] initWithRootViewController:table];
        
        nav.restorationIdentifier = NSStringFromClass([nav class]);
        
        self.window.rootViewController = nav;
    }
    
    [self.window makeKeyAndVisible];
    
    return YES;
}

2. 为子控制器实现

a. 设置恢复标识和恢复类

在一级控制器初始化方法中为其设置:

#pragma mark - initial

- (instancetype)init {
    
    self = [super init];
    
    if (self) {
        
        // 设置恢复标识和恢复类
        self.restorationIdentifier = NSStringFromClass([self class]);
        self.restorationClass = [self class];
    }
    
    return self;
}

在子控制器中设置:

- (instancetype)initWithNewItem:(BOOL)isNew {
    
    self = [super initWithNibName:nil bundle:nil];
    
    if (self) {
        
        // 设置恢复类和恢复标识
        self.restorationIdentifier = NSStringFromClass([self class]);
        self.restorationClass = [self class];
        
        if (isNew) {
            
            UIBarButtonItem *doneItem = [[UIBarButtonItem alloc] initWithBarButtonSystemItem:UIBarButtonSystemItemDone target:self action:@selector(save:)];
            self.navigationItem.rightBarButtonItem = doneItem;
            
            UIBarButtonItem *cancelItem = [[UIBarButtonItem alloc] initWithBarButtonSystemItem:UIBarButtonSystemItemCancel target:self action:@selector(cancel:)];
            self.navigationItem.leftBarButtonItem = cancelItem;
        }
    }
    
    return self;
}

如果是模态推出带有navigationController的控制器, 需要为这个 nav 设置恢复标识:

- (void)addNewItem:(id)sender {
    
    YNCustomItem *item = [[YNItemHandler sharedStore] createItem];
    
    YNSonViewController *sonVC = [YNSonViewController newItem:YES];
    sonVC.item = item;
    
    UINavigationController *nav = [[UINavigationController alloc] initWithRootViewController:sonVC];
    
    // 为 UINavigationController 设置恢复类
    nav.restorationIdentifier = NSStringFromClass([nav class]);
    
    [self presentViewController:nav animated:YES completion:nil];
}

b. 遵循恢复协议

需要状态恢复的控制器需要遵循<UIViewControllerRestoration>协议:

一级视图控制器中:

#pragma mark - view controller restoration

+ (UIViewController *)viewControllerWithRestorationIdentifierPath:(NSArray *)identifierComponents coder:(NSCoder *)coder {
    return [[self alloc] init];
}

同样, 二级视图控制器中, demo 中添加新联系人信息和查看联系人信息调用的是同一个控制器, 初始化方法为自己封装的方法newItem:(BOOL)isNew, isNew 为 NO 时, 查看联系人, 为 YES 时, 新建联系人. 此时有两种情况:

  1. 新建联系人:

    在恢复状态时newItem:(BOOL)isNew参数传入 YES

  2. 查看联系人:

    参数传入 NO

那么如何判断传入什么参数呢? 通过

+ (UIViewController *)viewControllerWithRestorationIdentifierPath:(NSArray *)identifierComponents coder:(NSCoder *)coder;

方法中的identifierComponents来判断, identifierComponents存储了当前视图控制器及其所有上级视图控制器的恢复标识. 那么现在我们来看一下:

  1. 新建联系人程序中的恢复标识有:
    1. root VC 的 nav 恢复标识
    2. 二级 VC 的 nav 恢复标识(没有一级 VC 的标识是因为 二级 VC 是由一级 VC 的 nav 模态出来的)
    3. 二级 VC 自身的恢复标识
  2. 查看联系人的恢复标识有:
    1. 根 VC 的 nav 恢复标识
    2. 二级 VC 自身的恢复标识(没有一级的和上面同理)

所以新建联系人的 VC 的identifierComponents的个数为3, 查看联系人的为2个. 那么则可以判断参数如何传递:

#pragma mark - view controller restoration

+ (UIViewController *)viewControllerWithRestorationIdentifierPath:(NSArray *)identifierComponents coder:(NSCoder *)coder {
    
    BOOL isNew = NO;
    
    if (identifierComponents.count == 3) {
        isNew = YES;
    }
    
    return [[self alloc] initWithNewItem:isNew];
}

c. 为 nav 设置恢复类

// 如果某个对象没有设置恢复类, 那么系统会通过 AppDelegate 来创建
- (UIViewController *)application:(UIApplication *)application viewControllerWithRestorationIdentifierPath:(NSArray *)identifierComponents coder:(NSCoder *)coder {
    
    UINavigationController *nav = [[UINavigationController alloc] init];
    
    // 恢复标识路径中最后一个对象就是 nav 的恢复标识
    nav.restorationIdentifier = [identifierComponents lastObject];
    
    if (identifierComponents.count == 1) {
        self.window.rootViewController = nav;
    }
    
    return nav;
}

至此, 控制器的状态恢复已完成, 但是现实的数据还需要做持久化处理, 否则只是恢复了一个没有数据的控制器.

d. 数据持久化

使二级页面详情页需要的数据保存:

- (void)encodeRestorableStateWithCoder:(NSCoder *)coder {
    [coder encodeObject:self.item.itemKey forKey:kRestorationKey];
    
    // 保存 textField 中的文本, 以便恢复更改后的文本
    self.item.name = self.nameField.text;
    self.item.phoneNumber = [self.phoneField.text integerValue];
    self.item.sex = self.sexField.text;
    
    // 存入本地
    [[YNItemHandler sharedStore] saveItems];
    
    [super encodeRestorableStateWithCoder:coder];
}

- (void)decodeRestorableStateWithCoder:(NSCoder *)coder {
    
    NSString *itemKey = [coder decodeObjectForKey:kRestorationKey];
    
    for (YNCustomItem *item in [[YNItemHandler sharedStore] allItems]) {
        if ([item.itemKey isEqualToString:itemKey]) {
            self.item = item;
            NSLog(@"name:%@, phone:%ld, sex:%@", self.item.name, self.item.phoneNumber, self.item.sex);
            break;
        }
    }
    
    [super decodeRestorableStateWithCoder:coder];
}

二级页面状态恢复完成, 这时候测试(测试方法: 运行后, cmd + shift + h回到桌面, Xcode停止运行, 然后再运行), 重新打开项目, 发现视图控制器状态是恢复了, 但是数据还是空白. 然后打上断点看了下周期, 把数据获取方法写在viewWillAppear:里就好了.

e. 记录 tableview 状态

为一级 VC 设置其 tableView 的恢复标识:

- (void)viewDidLoad {
    [super viewDidLoad];
    
    self.navigationItem.title = @"State Restoration";
    self.navigationItem.rightBarButtonItem = [[UIBarButtonItem alloc] initWithBarButtonSystemItem:UIBarButtonSystemItemAdd target:self action:@selector(addNewItem:)];
    
    [self.tableView registerNib:[UINib nibWithNibName:NSStringFromClass([YNTableViewCell class]) bundle:nil] forCellReuseIdentifier:kCellIdentifier];
    
    // 给 tableView 设置恢复标识, tableView 自动保存的 contentOffset 会恢复其滚动位置
    self.tableView.restorationIdentifier = kTableViewIdentifier;
}

记录 tableView 是否处于 editing 状态:

// 记录 tableView 是否处于编辑状态
- (void)encodeRestorableStateWithCoder:(NSCoder *)coder {
    [coder encodeBool:self.isEditing forKey:kTableViewEditingKey];
    [super encodeRestorableStateWithCoder:coder];
}

- (void)decodeRestorableStateWithCoder:(NSCoder *)coder {
    self.editing = [coder decodeBoolForKey:kTableViewEditingKey];
    [super decodeRestorableStateWithCoder:coder];
}

通过<UIDataSourceModelAssociation>协议使视图对象在恢复时关联正确的 model 对象. 当保存状态时, 其会根据 indexPath 保存一个唯一标识.

实现<UIDataSourceModelAssociation>协议方法:

- (NSString *)modelIdentifierForElementAtIndexPath:(NSIndexPath *)idx inView:(UIView *)view {
    NSString *identifier = nil;
    
    if (idx && view) {
        YNCustomItem *item = [[YNItemHandler sharedStore] allItems][idx.row];
        identifier = item.itemKey;
    }
    
    return identifier;
}

- (NSIndexPath *)indexPathForElementWithModelIdentifier:(NSString *)identifier inView:(UIView *)view {
    
    NSIndexPath *indexPath = nil;
    
    if (identifier && view) {
        
        for (YNCustomItem *item in [[YNItemHandler sharedStore] allItems]) {
            
            if ([identifier isEqualToString:item.itemKey]) {
                NSInteger row = [[[YNItemHandler sharedStore] allItems] indexOfObjectIdenticalTo:item];
                indexPath = [NSIndexPath indexPathForRow:row inSection:0];
                break;
            }
        }
    }
    
    return indexPath;
}

最后记得在进入后台前持久化当前的 item(实际开发中记得用 cache(项目里使用 YYCache) 或者 db(项目里使用 FMDB) 去即时持久化视图数据, 是一个比较稳妥的方案):

- (void)applicationDidEnterBackground:(UIApplication *)application {
    BOOL success = [[YNItemHandler sharedStore] saveItems];
    
    if (success) {
        NSLog(@"成功保存所有项目");
    } else {
        NSLog(@"保存项目失败");
    }
}

至此, 状态恢复基本使用已经实现.


测试

  1. 添加 n 个新的联系人, 滑动列表到测试位置, 让 tableView 进入到编辑状态. 按下cmd + shift + h进入 home, 用 Xcode 结束程序cmd+., 再次运行看看是否在最后滑动位置, 或者是否处于编辑状态.
  2. 恢复编辑状态, 随便进入一个联系人详情, 重复上面的操作, 看看进入程序之后是否处于上次退出前的详情页面.

最后, 可能说的有些模糊, 还是结合demo看要更明白点.

2017-02-04 18:45:00 weixin_33672400 阅读数 17

1、 https://itunesconnect.apple.com/
2、我的 App
3、点击图片
4、找到App Strore (列表) ios app 版本号(有”准备提交”文字)
5、检查信息 (主要看 构建版本 有没有) 如果只是修改 完成必须点 存储 (按钮 )
6、最后点击右上 提交以供审核(按钮)
7、结果 版本号出现 审核中 类似文字

2017-08-02 15:02:00 weixin_34335458 阅读数 19

一、iOS app 程序执行过程

1、执行main函数
2、在此方法中调用UIApplicationMain
2.1 创建UIApplication对象
2.2 创建UIApplication的delegate对象
2.3 开启消息循环
3、delegate对象开始监听系统事件
3.1 app启动完成后会调用delegate对象的程序加载完成的方法
3.2 如果app的plist设置了main storyboard 会创建UIWindow对象(如 果没有设置则不创建)
3.3 创建main storyboard中绑定的controller对象(stroyboard中箭 头指向的控制器)
3.4 设置给window对象的rootViewController
3.5 设置window对象可见
4、4大对象之间的关系 UIApplication (delegate)
AppDelegate<UIApplicationDelegate> (window)
UIWindow (rootViewController)
UIViewController (view 从加载xib或storyboard或代码创建) UIView

1825567-a60bee91c1500218.png
image.png

二、iOS app 程序执行过程

1. Not running:

应用还没有启动,或者应用正在运行但是途中被系统停止

2.Inactive:

当前应用正在前台运行,但是并不接收事件(当前或许正在执行其它代码)。一般每当应用要从一个状态切换到另一个不同的状态时,中途过渡会短暂停留在此状态。唯一在此状态停留时间比较长的情况是:当用户锁屏时,或者系统提示用户去响应某些(诸如电话来电、有未读短信等)事件的时候。

3.Active:

当前应用正在前台运行,并且接收事件。这是应用正在前台运行时所处的正常状态。

4.Background:

应用处在后台,并且还在执行代码。大多数将 要进入Suspended状态的应用,会先短暂进入此状态。然而,对于请求需要额外的执行时间的应用,会在此状态保持更长一段时间。另外,如果一个应用要求启动时直接进入后台运行,这样的应用会直接从Notrunning状态进入Background状态,中途不会经过Inactive状态。比如没有界面的应用。注此处并不特指没有界面的应用,其实也可以是有界面的应用,只是如果要直接进入background状态的话,该应用界面不会被显示。

5.Suspended:

应用处在后台,并且已停止执行代码。系统自动的将应用移入此状态,且在此举之前不会对应用做任何通知。当处在此状态时,应用依然驻留内存但不执行任何程序代码。当系统发生低内存告警时,系统将会将处 于Suspended状态的应用清除出内存以为正在前台运行的应用提供足够的内存。

三、iOS app 程序执行过程及appdelegate相关事件代理方法

1825567-33040b1cba50082a.png
image.png

iOS app 状态转换图

阅读数 412

博文 来自: qibingzheng
没有更多推荐了,返回首页