iOS面试总结

我是一只勤劳的小蜜蜂。闲暇之余,总结下一直以来自己面试过程中所遇到的一些高概率问题,分享给大家,共同成长。该贴后续将会持续更新~~

1.BAD_ACCESS在什么情况下出现?

①访问一个僵尸对象,访问僵尸对象的成员变量或者向其发消息;②死循环。

2.简述下Objective-C中调用方法的过程(runtime)

Objective-C是动态语言,每个方法在运行时会被动态转为消息发送,即:objc_msgSend(receiver, selector),整个过程介绍如下:
①objc在向一个对象发送消息时,runtime库会根据对象的isa指针找到该对象实际所属的类
②然后在该类中的方法列表以及其父类方法列表中寻找方法运行
③如果,在最顶层的父类(一般也就NSObject)中依然找不到相应的方法时,程序在运行时会挂掉并抛出异常unrecognized selector sent to XXX
④但是在这之前,objc的运行时会给出三次拯救程序崩溃的机会,这三次拯救程序奔溃的说明见问题《什么时候会报unrecognized selector的异常》中的说明
• 补充说明:Runtime 铸就了Objective-C 是动态语言的特性,使得C语言具备了面向对象的特性,在程序运行期创建,检查,修改类、对象及其对应的方法,这些操作都可以使用runtime中的对应方法实现。

3.什么是method swizzling(俗称黑魔法)

简单说就是进行方法交换。
①在Objective-C中调用一个方法,其实是向一个对象发送消息,查找消息的唯一依据是selector的名字。利用Objective-C的动态特性,可以实现在运行时偷换selector对应的方法实现,达到给方法挂钩的目的
②每个类都有一个方法列表,存放着方法的名字和方法实现的映射关系,selector的本质其实就是方法名,IMP有点类似函数指针,指向具体的Method实现,通过selector就
![Uploading 4a6c4741-9186-4ba2-b354-cde08612df6c_550019.png . . .]可以找到对应的IMP

  • 交换方法的几种实现方式
    • 利用 method_exchangeImplementations 交换两个方法的实现
    • 利用 class_replaceMethod 替换方法的实现
    • 利用 method_setImplementation 来直接设置某个方法的IMP


      示意图.png
4.objc中向一个nil对象发送消息将会发生什么?

在Objective-C中向nil发送消息是完全有效的——只是在运行时不会有任何作用
①如果一个方法返回值是一个对象,那么发送给nil的消息将返回0(nil);
② 如果方法返回值为指针类型,其指针大小为小于或者等于sizeof(void*);
③ float,double,long double 或者long long的整型标量,发送给nil的消息将返回0;
④ 如果方法返回值为结构体,发送给nil的消息将返回0。结构体中各个字段的值将都是0;
⑤ 如果方法的返回值不是上述提到的几种情况,那么发送给nil的消息的返回值将是未定义的。
• 具体原因分析
①objc是动态语言,每个方法在运行时会被动态转为消息发送,即:objc_msgSend(receiver, selector);
② 为了方便理解这个内容,还是贴一个objc的源代码:

struct objc_class

{

    // isa指针指向Meta Class,因为Objc的类的本身也是一个Object,

    // 为了处理这个关系,runtime就创造了Meta Class,

    // 当给类发送[NSObject alloc]这样消息时,实际上是把这个消息发给了Class Object

    Class isa OBJC_ISA_AVAILABILITY;

#if !__OBJC2__

    Class super_class OBJC2_UNAVAILABLE; // 父类

    const char *name OBJC2_UNAVAILABLE; // 类名

    long version OBJC2_UNAVAILABLE; // 类的版本信息,默认为0

    long info OBJC2_UNAVAILABLE; // 类信息,供运行期使用的一些位标识

    long instance_size OBJC2_UNAVAILABLE; // 该类的实例变量大小

    struct objc_ivar_list *ivars OBJC2_UNAVAILABLE; // 该类的成员变量链表

    struct objc_method_list **methodLists OBJC2_UNAVAILABLE; // 方法定义的链表

    // 方法缓存,对象接到一个消息会根据isa指针查找消息对象,

    // 这时会在method Lists中遍历,

    // 如果cache了,常用的方法调用时就能够提高调用的效率。

    // 这个方法缓存只存在一份,不是每个类的实例对象都有一个方法缓存

    // 子类会在自己的方法缓存中缓存父类的方法,父类在自己的方法缓存中也会缓存自己的方法,而不是说子类就不缓存父类方法了

    struct objc_cache *cache OBJC2_UNAVAILABLE;

    struct objc_protocol_list *protocols OBJC2_UNAVAILABLE; // 协议链表

#endif

} OBJC2_UNAVAILABLE;

分析:
①objc在向一个对象发送消息时,runtime库会根据对象的isa指针找到该对象实际所属的类,然后在该类中的方法列表以及其父类方法列表中寻找方法运行,然后再发送消息的时候,objc_msgSend方法不会返回值,所谓的返回内容都是具体调用时执行的。
② 如果向一个nil对象发送消息,首先在寻找对象的isa指针时就是0地址返回了,所以不会出现任何错误。

5.objc中向一个对象发送消息[obj foo]和objc_msgSend()函数之间有什么关系?

[obj foo];在objc动态编译时,会被转意为:
objc_msgSend(obj, @selector(foo));

6.什么时候会报unrecognized selector的异常?

当调用该对象上某个方法,而该对象上没有实现这个方法的时候, 可以通过“消息转发”进行解决,如果还是不行就会报unrecognized selector异常。objc是动态语言,每个方法在运行时会被动态转为消息发送,即:objc_msgSend(receiver, selector),整个过程介绍如下:
①objc在向一个对象发送消息时,runtime库会根据对象的isa指针找到该对象实际所属的类;
②然后在该类中的方法列表以及其父类方法列表中寻找方法运行
③如果,在最顶层的父类中依然找不到相应的方法时,程序在运行时会挂掉并抛出异常unrecognized selector sent to XXX 。但是在这之前,objc的运行时会给出三次拯救程序崩溃的机会:

○ Method resolution

§ objc运行时会调用+resolveInstanceMethod:或者 +resolveClassMethod:,让你有机会提供一个函数实现。

§ 如果你添加了函数并返回 YES,那运行时系统就会重新启动一次消息发送的过程

§ 如果 resolve 方法返回 NO ,运行时就会移到下一步,消息转发

○ Fast forwarding

§ 如果目标对象实现了-forwardingTargetForSelector:,Runtime 这时就会调用这个方法,给你把这个消息转发给其他对象的机会

§ 只要这个方法返回的不是nil和self,整个消息发送的过程就会被重启,当然发送的对象会变成你返回的那个对象。

§ 否则,就会继续Normal Fowarding。

§ 这里叫Fast,只是为了区别下一步的转发机制。因为这一步不会创建任何新的对象,但Normal forwarding转发会创建一个NSInvocation对象,相对Normal forwarding转发更快点,所以这里叫Fast forwarding

○ Normal forwarding

§ 这一步是Runtime最后一次给你挽救的机会。

§ 首先它会发送-methodSignatureForSelector:消息获得函数的参数和返回值类型。

§ 如果-methodSignatureForSelector:返回nil,Runtime则会发出-doesNotRecognizeSelector:消息,程序这时也就挂掉了。

§ 如果返回了一个函数签名,Runtime就会创建一个NSInvocation对象并发送-forwardInvocation:消息给目标对象
7.HTTP协议中POST方法和GET方法有那些区别?

①GET用于向服务器请求数据,POST用于提交数据;
②GET请求,请求参数拼接形式暴露在地址栏,而POST请求参数则放在请求体里面,因此GET请求不适合用于验证密码等操作;
③GET请求的URL有长度限制,POST请求不会有长度限制。

8.猜想runloop内部是如何实现的?

从字面意思看:运行循环、跑圈;
本质:内部就是do-while循环,在这个循环内部不断地处理各种事件(任务),比如:Source、Timer、Observer;
• 每条线程都有唯一一个RunLoop对象与之对应,主线程的RunLoop默认已经启动,子线程的RunLoop需要手动启动;
• 每次RunLoop启动时,只能指定其中一个 Mode,这个Mode被称作 CurrentMode,如果需要切换Mode,只能退出Loop,再重新指定一个Mode进入,这样做主要是为了隔离不同Mode中的Source、Timer、Observer,让其互不影响;

9.苹果是如何实现autoreleasepool的?

autoreleasepool以一个队列数组的形式实现,主要通过下列三个函数完成。
objc_autoreleasepoolPush
objc_autoreleasepoolPop
objc_aurorelease

10.这个写法会出什么问题: @property (copy) NSMutableArray *array;

因为copy策略拷贝出来的是一个不可变对象,然而却把它当成可变对象使用,很容易造成程序奔溃。
这里还有一个问题,该属性使用了同步锁,会在创建时生成一些额外的代码用于帮助编写多线程程序,这会带来性能问题,通过声明nonatomic可以节省这些虽然很小但是不必要额外开销,在iOS开发中应该使用nonatomic替代atomic。

11.如何让自定义类可以用 copy 修饰符?如何重写带 copy 关键字的 setter?

若想令自己所写的对象具有拷贝功能,则需实现NSCopying协议。如果自定义的对象分为可变版本与不可变版本,那么就要同时实现NSCopyiog与NSMutableCopying协议,不过一般没什么必要,实现NSCopying协议就够了

 实现不可变版本拷贝

- (id)copyWithZone:(NSZone *)zone;

 实现可变版本拷贝

- (id)mutableCopyWithZone:(NSZone *)zone;

 重写带 copy 关键字的setter

- (void)setName:(NSString *)name

{

_name = [name copy];

}
12.+(void)load; +(void)initialize;有什么用处?
 +(void)load;```

① 当类对象被引入项目时, runtime会向每一个类对象发送 load 消息;
②load 方法会在每一个类甚至分类被引入时仅调用一次,调用的顺序:父类优先于子类, 子类优先于分类;
③由于load 方法会在类被import 时调用一次,而这时往往是改变类的行为的最佳时机,在这里可以使用例如method swizlling 来修改原有的方法;
④load 方法不会被类自动继承。

+(void)initialize;```
也是在第一次使用这个类的时候会调用这个方法,也就是说 initialize也是懒加载。
• 总结:
① 在Objective-C中,runtime会自动调用每个类的这两个方法;
② +load会在类初始加载时调用;
③+initialize会在第一次调用类的类方法或实例方法之前被调用;
④这两个方法是可选的,且只有在实现了它们时才会被调用;
⑤两者的共同点:两个方法都只会被调用一次。

13.如何关闭默认的KVO的默认实现,KVO的实现原理?

所谓的“手动触发”是区别于“自动触发”:
自动触发是指类似这种场景:在注册 KVO 之前设置一个初始值,注册之后,设置一个不一样的值,就可以触发了。

想知道如何手动触发,必须知道自动触发 KVO 的原理:
键值观察通知依赖于 NSObject 的两个方法: willChangeValueForKey: 和 didChangevlueForKey: 。在一个被观察属性发生改变之前, willChangeValueForKey: 一定会被调用,这就 会记录旧的值。而当改变发生后, didChangeValueForKey: 会被调用,继而 observeValueForKey:ofObject:change:context: 也会被调用。如果可以手动实现这些调用,就可以实现“手动触发”了。

当你观察一个对象时,一个新的类会被动态创建。这个类继承自该对象的原本的类,并重写了被观察属性的 setter 方法。重写的 setter 方法会负责在调用原 setter 方法之前和之后,通知所有观察对象:值的更改。最后通过 isa 混写(isa-swizzling) 把这个对象的 isa 指针 ( isa 指针告诉 Runtime 系统这个对象的类是什么 ) 指向这个新创建的子类,对象就神奇的变成了新创建的子类的实例。如下所示:

示意图.png
14.若一个类有实例变量NSString *_foo,调用setValue:forKey:时,是以foo还是_foo作为key?

• 都可以

15.KVC和KVO的keyPath一定是属性么?

• 可以是成员变量

16.直接调用_objc_msgForward函数将会发生什么?

_objc_msgForward是 IMP 类型,用于消息转发的:当向一个对象发送一条消息,但它并没有实现的时候,_objc_msgForward会尝试做消息转发。

直接调用_objc_msgForward是非常危险的事,如果用不好会直接导致程序Crash,但是如果用得好,能做很多非常酷的事。

一旦调用_objc_msgForward,将跳过查找 IMP 的过程,直接触发“消息转发”,如果调用了_objc_msgForward,即使这个对象确实已经实现了这个方法,你也会告诉objc_msgSend:“我没有在这个对象里找到这个方法的实现”。

17.断点续传如何实现的?

断点续传的理解可以分为两部分:一部分是断点,一部分是续传。断点的由来是在下载过程中,将一个下载文件分成了多个部分,同时进行多个部分一起的下载,当 某个时间点,任务被暂停了,此时下载暂停的位置就是断点了。续传就是当一个未完成的下载任务再次开始时,会从上次的断点继续传送。

使用多线程断点续传下载的时候,将下载或上传任务(一个文件或一个压缩包)人为的划分为几个部分,每一个部分采用一个线程进行上传或下载,多个线程并发可以占用服务器端更多资源,从而加快下载速度。

在下载(或上传)过程中,如果网络故障、电量不足等原因导致下载中断,这就需要使用到断点续传功能。下次启动时,可以从记录位置(已经下载的部分)开始,继续下载以后未下载的部分,避免重复部分的下载。断点续传实质就是能记录上一次已下载完成的位置。

断点续传的过程:
①断点续传需要在下载过程中记录每条线程的下载进度;
②每次下载开始之前先读取数据库,查询是否有未完成的记录,有就继续下载,没有则创建新记录插入数据库;
③在每次向文件中写入数据之后,在数据库中更新下载进度;
④下载完成之后删除数据库中下载记录。

18.当 TableView 的 Cell 改变时,如何让这些改变以动画的形式呈现?
@interface ViewController ()

@property (nonatomic, strong) NSIndexPath *index;

@end

@implementation ViewController

static NSString *ID = @"cell";

- (void)viewDidLoad {

   [super viewDidLoad];

}
 
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath

{

    UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:ID];

    cell.textLabel.text = [NSString stringWithFormat:@"%ld",(long)indexPath.row];

    return cell;

}

- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section

{

    return 10;

}

- (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath

{
  
    if(self.index == indexPath){

        return 120;

    }

    return 60;

}

- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath

{

    self.index = indexPath;

    [tableView deselectRowAtIndexPath:indexPath animated:TRUE];

    //重点是这2句代码实现的功能

    [tableView beginUpdates];

    [tableView endUpdates];

}
19.如何把一个包含自定义对象的数组序列化到磁盘?
//自定义对象只需要实现NSCoding协议即可

- (void)viewDidLoad

{

    [super viewDidLoad];

   User *user = [User new];

    Account *account = [Account new];

    NSArray *userArray = @[user, account];

   // 存到磁盘

    NSData * tempArchive = [NSKeyedArchiver archivedDataWithRootObject: userArray];

}

 //代理方法

- (instancetype)initWithCoder:(NSCoder *)coder

{

    self = [super initWithCoder:coder];

    if (self) {

        self.user = [aDecoder decodeObjectForKey:@"user"];

        self.account = [aDecoder decodeObjectForKey:@"account"];

    }

    return self;

}

// 代理方法

-(void)encodeWithCoder:(NSCoder *)aCoder{

    [aCoder encodeObject:self.user forKey:@"user"];

    [aCoder encodeObject:self.account forKey:@"account"];

}
20.内存管理的几条原则时什么?按照默认法则.那些关键字生成的对象需要手动释放?在和property结合的时候怎样有效的避免内存泄露?

①谁申请,谁释放;
②遵循Cocoa Touch的使用原则;
③内存管理主要要避免“过早释放”和“内存泄漏”,对于“过早释放”需要注意@property设置特性时,一定要用对特性关键字,对于“内存泄漏”,一定要申请了要负责释放,要细心。
④关键字alloc 或new 生成的对象需要手动释放;
⑤设置正确的property属性,对于retain需要在合适的地方释放。

21.Object C中创建线程的方法是什么?如果在主线程中执行代码,方法是什么?如果想延时执行代码、方法又是什么?

线程创建有三种方法:使用NSThread创建、使用GCD的dispatch、使用子类化的NSOperation,然后将其加入NSOperationQueue;在主线程执行代码,方法是performSelectorOnMainThread,如果想延时执行代码可以用performSelector:onThread:withObject:waitUntilDone:

22.IBOutlet连出来的视图属性为什么可以被设置成weak?

因为父控件的subViews数组已经对它有一个强引用。

23.XIB中User Defined Runtime Attributes如何使用?

①User Defined Runtime Attributes是一个不被看重但功能非常强大的的特性,它能够通过KVC的方式配置一些你在interface builder中不能配置的属性。
② 当你希望在IB中作尽可能多得事情,这个特性能够帮助你编写更加轻量级的viewcontroller。

24.请简述UITableView的复用机制。

①每次创建cell的时候通过dequeueReusableCellWithIdentifier:方法创建cell,它先到缓存池中找指定标识的cell,如果没有就直接返回nil。
②如果没有找到指定标识的cell,那么会通过initWithStyle:reuseIdentifier:创建一个cell。
③ 当cell离开界面就会被放到缓存池中,以供下次复用

25.如何高性能的给UIImageView 加个圆角?

• 不好的解决方案
○ 使用下面的方式会强制Core Animation提前渲染屏幕的离屏绘制, 而离屏绘制就会给性能带来负面影响,会有卡顿的现象出现 。

self.view.layer.cornerRadius = 5;

self.view.layer.masksToBounds = YES;

• 正确的解决方案:使用绘图技术。

- (UIImage *)circleImage

{

    NO代表透明

    UIGraphicsBeginImageContextWithOptions(self.size, NO, 0.0);

    获得上下文

    CGContextRef ctx = UIGraphicsGetCurrentContext();

    添加一个圆

    CGRect rect = CGRectMake(0, 0, self.size.width, self.size.height);

    CGContextAddEllipseInRect(ctx, rect);

    裁剪

    CGContextClip(ctx);

    将图片画上去

    [self drawInRect:rect];

    UIImage *image = UIGraphicsGetImageFromCurrentImageContext();

    关闭上下文

    UIGraphicsEndImageContext();

    return image;

}

• 还有一种方案:使用了贝塞尔曲线"切割"个这个图片, 给UIImageView 添加了的圆角,其实也是通过绘图技术来实现的

 

UIImageView *imageView = [[UIImageView alloc] initWithFrame:CGRectMake(0, 0, 100, 100)];

imageView.center = CGPointMake(200, 300);

UIImage *anotherImage = [UIImage imageNamed:@"image"];

UIGraphicsBeginImageContextWithOptions(imageView.bounds.size, NO, 1.0);

[[UIBezierPath bezierPathWithRoundedRect:imageView.bounds

                            cornerRadius:50] addClip];

[anotherImage drawInRect:imageView.bounds];

imageView.image = UIGraphicsGetImageFromCurrentImageContext();

UIGraphicsEndImageContext();

[self.view addSubview:imageView];
26.使用drawRect有什么影响?

• drawRect方法依赖Core Graphics框架来进行自定义的绘制

• 缺点:它处理touch事件时每次按钮被点击后,都会用setNeddsDisplay进行强制重绘;而且不止一次,每次单点事件触发两次执行。这样的话从性能的角度来说,对CPU和内存来说都是欠佳的。特别是如果在我们的界面上有多个这样的UIButton实例,那就会很糟糕了

• 这个方法的调用机制也是非常特别. 当你调用 setNeedsDisplay 方法时, UIKit 将会把当前图层标记为dirty,但还是会显示原来的内容,直到下一次的视图渲染周期,才会将标记为 dirty 的图层重新建立Core Graphics上下文,然后将内存中的数据恢复出来, 再使用 CGContextRef 进行绘制。

27.如何渲染UILabel的文字?

通过NSAttributedString/NSMutableAttributedString(富文本)

28.控制器的生命周期

• 就是问的view的生命周期,下面已经按方法执行顺序进行了排序

自定义控制器view,这个方法只有实现了才会执行

- (void)loadView

{

    self.view = [[UIView alloc] init];

    self.view.backgroundColor = [UIColor orangeColor];

}
view是懒加载,只要view加载完毕就调用这个方法

- (void)viewDidLoad

{

    [super viewDidLoad];

    NSLog(@"%s",__func__);

}

view即将显示

- (void)viewWillAppear:(BOOL)animated

{

    [super viewWillAppear:animated];

    NSLog(@"%s",__func__);

}

view即将开始布局子控件

- (void)viewWillLayoutSubviews

{

    [super viewWillLayoutSubviews];

    NSLog(@"%s",__func__);

}

view已经完成子控件的布局

- (void)viewDidLayoutSubviews

{

    [super viewDidLayoutSubviews];

    NSLog(@"%s",__func__);

}

view已经出现

- (void)viewDidAppear:(BOOL)animated

{

    [super viewDidAppear:animated];

    NSLog(@"%s",__func__);

}

view即将消失

- (void)viewWillDisappear:(BOOL)animated

{

    [super viewWillDisappear:animated];

    NSLog(@"%s",__func__);

}

view已经消失

- (void)viewDidDisappear:(BOOL)animated

{

    [super viewDidDisappear:animated];

    NSLog(@"%s",__func__);

}

收到内存警告

- (void)didReceiveMemoryWarning

{

    [super didReceiveMemoryWarning];

    NSLog(@"%s",__func__);

}

方法已过期,即将销毁view

- (void)viewWillUnload

{

}

方法已过期,已经销毁view

- (void)viewDidUnload

{

}
29.iOS本地数据存储都有几种方式?

①.NSkeyedArchiver:采用归档的形式来保存数据,该数据对象需要遵守NSCoding协议,并且该对象对应的类必须提供encodeWithCoder:和initWithCoder:方法.前一个方法告诉系统怎么对对象进行编码,而后一个方法则是告诉系统怎么对对象进行解码。
②.NSUserDefaults:用来保存应用程序设置和属性,用户保存的数据.用户再次打开程序或者开机后这些数据仍然存在.NSUserDefaults可以存储的数据类型包括:NSData,NSString,NSNumber,NSDate,NSArray.NSDictionary,其他类型的数据需要先行转换。
③.Write写入方式:永久保存在磁盘中.具体:a.获得文件保存的路径.b.生成该路径下的文件,c,往文件中写入数据.d.从文件中读出数据。
④.SQLite:采用SQLite数据库来存储数据,SQLite作为一种轻量级数据库.具体:a.添加SQLite相关的库以及头文件,b.使用数据库存数数据:打开数据库,编写数据库语句,执行,关闭数据库.另:写入数据库,字符串可以采用char方式,而从数据库中取出char类型,当char类型有表示中文字符时,会出现乱码,这是因为数据库默认使用ascII编码方式,所以想要正确从数据库中取出中文,需要使用NSString来接受从数据库取出的字符串。
⑤.CoreData:原理是对SQLite的封装,开发者不需要接触sql语句,就可以对数据库进行操作。

30.简述应用程序按HOME键进入后台时的生命周期,以及从后台进入前台时的生命周期.
//前者:
- (void)applicationWillResignActive:(UIApplication *)application
- (void)applicationDidEnterBackground:(UIApplication *)application

//后者:
- (void)applicationWillEnterForeground:(UIApplication *)application
- (void)applicationDidBecomeActive:(UIApplication *)application

补充各个程序运行状态时代理的回调:
- (BOOL)application:(UIApplication *)application willFinishLaunchingWithOptions:(NSDictionary *)launchOptions
//告诉代理进程启动但还没进入状态保存
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
//告诉代理启动基本完成程序准备开始运行
- (void)applicationWillResignActive:(UIApplication *)application
//当应用程序将要入非活动状态执行,在此期间,应用程序不接收消息或事件,比如来电话了
- (void)applicationDidBecomeActive:(UIApplication *)application
//当应用程序入活动状态执行,这个刚好跟上面那个方法相反
- (void)applicationDidEnterBackground:(UIApplication *)application
//当程序被推送到后台的时候调用。所以要设置后台继续运行,则在这个函数里面设置即可
- (void)applicationWillEnterForeground:(UIApplication *)application
//当程序从后台将要重新回到前台时候调用,这个刚好跟上面的那个方法相反。
- (void)applicationWillTerminate:(UIApplication *)application
//当程序将要退出是被调用,通常是用来保存数据和一些退出前的清理工作。这个需要要设置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
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 203,547评论 6 477
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,399评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 150,428评论 0 337
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,599评论 1 274
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,612评论 5 365
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,577评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,941评论 3 395
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,603评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,852评论 1 297
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,605评论 2 321
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,693评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,375评论 4 318
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,955评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,936评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,172评论 1 259
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 43,970评论 2 349
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,414评论 2 342

推荐阅读更多精彩内容

  • 被面试问的问题, 和网上找到的我觉得不错的问题 会保持更新 -- 因都写在一起了, 所以可能会穿插添加. 请谅解 ...
    DSperson阅读 405评论 0 2
  • 这篇文章完全是基于南峰子老师博客的转载 这篇文章完全是基于南峰子老师博客的转载 这篇文章完全是基于南峰子老师博客的...
    西木阅读 30,539评论 33 466
  • 参考链接: http://www.cnblogs.com/ioshe/p/5489086.html 简介 Runt...
    乐乐的简书阅读 2,129评论 0 9
  • 为什么承受不了呢,生命中有些痛需要自己承受。大家都有要承受的东西。 对自己要求高一点,做到100%吧。 越努力,越幸运。
    矖軣阅读 169评论 0 0
  • 在学习手绘插画的道路上,需要创造力,更需要长期不懈的坚持。 1、人人可以画插画。手绘插画与专业的绘画不同,专业绘画...
    儿童快乐成长驿站阅读 1,884评论 0 14