iOS面试题及答案(二)

一:@@@《基础篇》@@@

二:@@@《进阶篇》@@@

1. 堆和栈的区别?

OC语言是C语言的超集。C语言的内存模型分为5个区:栈区、堆区、静态区、常量区、代码区。每个区存储的内容如下:

1、堆区:就是通过new、malloc、realloc分配的内存块,编译器不会负责它们的释放工作,需要用程序区释放。分配方式类似于数据结构中的链表。在iOS开发中所说的“内存泄漏”说的就是堆区的内存。

2、栈区:存放函数的参数值、局部变量等,由编译器自动分配和释放,通常在函数执行完后就释放了,其操作方式类似于数据结构中的栈。栈内存分配运算内置于CPU的指令集,效率很高,但是分配的内存量有限,比如iOS中栈区的大小是2M。

3、静态区:全局变量和静态变量(在iOS中就是用static修饰的局部变量或者是全局全局变量)的存储是放在一块的,初始化的全局变量和静态变量在一块区域,未初始化的全局变量和未初始化的静态变量在相邻的另一块区域。程序结束后,由系统释放。
4、常量区:常量存储在这里,不允许修改。
5、代码区:存放函数体的二进制代码。

堆和栈的区别:

1.1、堆空间的内存是动态分配的(一般程序员分配释放),一般存放对象,并且需要手动释放内存。当然了,iOS引入了ARC(自动引用计数)之后,就不就不需要用代码管理对象的内存了。

1.2、栈空间的内存是由系统自动分配,一般存放局部变量。比如对象的地址等值,不需要程序员对这块内存进行管理,比如,函数中的局部变量的作用范围(生命周期)就是在调完这个函数之后就结束了。

2、堆空间比较大,栈空间比较小。
3、堆空间一般存放对象本身,block的copy等。栈空间中一般存储基本数据类型,对象的地址。
4、堆(数据结构):堆可以被看成是一棵树,如:堆排序。栈(数据结构):一种先进后出的数据结构。

2. 什么是程序、进程、线程? 说一下进程和线程的关系以及区别?

1.1 程序(Application):
由源代码生成的可执行应用。(例如:QQ 微信)

1.2 进程:
进程: 是指在操作系统(OS)中正在运行的一个应用程序(一般一个App就一个进程)。程序并不能单独运行,只有将程序装载到内存中,系统为它分配资源才能运行。
所以一个正在运行的程序可以看做一个进程。(例如: 正在运行的QQ就是一个进程)

1.3 线程:
线程: 程序(进程)中独立运行的代码段。(例如: 接收QQ消息的代码)
线程是进程的基本执行单元,一个进程的所有任务(操作)都是在线程中执行
进程要想执行任务,必须至少有一条线程(程序启动会默认开始一条线程,这条线程被称为主线程(UI线程))

大概说一下:
每个进程之间是独立的,每个进程都运行在其专用的且受保护的内存中(每个进程拥有独立运行所需的全部资源)。
一个程序至少包含一个进程(一般一个App就一个进程),一个进程至少包含一个线程,一个进程中的所有线程共享当前进程所拥有的资源。
进程有独立的地址空间,一个进程崩溃后,在保护模式的影响下不会对其他进程产生影响。而线程只是一个进程中的不同执行路径,线程有自己的堆栈和局部变量,线程之间没有单独的地址空间。

进程与线程的区别:
1.同一进程内的线程共享本进程的资源(如内存,I/O,CPU等),但进程之间的资源是独立的。
2.一个进程崩溃后,在保护模式下不会对其他进程产生影响,但是一个线程崩溃整个进程都死掉。所以多进程要比多线程健壮。
3.进程切换时,消耗的资源大,效率低。所以频繁的切换时,使用线程要高于进程。
4.每个进程都有一个运行的入口,顺序执行。但是线程不能独立执行,必须依存在进程中,由应用程序提供多个线程操作。
5.线程是处理器调度(执行任务)的基本单元,但是进程不是。

3. 线程的状态有哪些?

1. 新建
2. 就绪 : 线程对象加入线程池中等待 CPU 调度
3. 运行 : CPU负责调度线程中线程的执行, 线程执行完成前, 状态可能在就绪和运行之间来回切换
4. 阻塞 : 满足某个预定条件, 使用休眠或锁, 阻塞线程执行
5. 死亡 : 线程执行完毕, 或者内部中止执行线程对象

4. 什么是线程安全?分别有哪些锁?

线程安全: 多个线程同时访问一块资源, 容易引发数据错乱和数据安全。

1. 互斥锁(加锁): 新线程访问时, 发现其他线程正在执行锁定的代码, 新线程会进入休眠
   1.1 NSLock
   1.2 pthread_mutex
   1.3 @synchronized
2. 自旋锁: 忙等的锁, 新线程会用死循环的方式, 一直等待锁定的代码执行完成, 数据量少的时候用
3. 条件锁: 不满足就休眠, 资源分配到了, 条件锁打开, 进程继续运行, 例如:NSConditionLock
4. 读写锁: 用于解决多线程对公共资源读写问题。 读操作可以并发重入, 写操作是互斥的
5. 信号量: 

5. iOS开启多线程的方法有哪些?

1.pthread  2.NSThread  3.GCD  4.NSOperation

6. 简单的说一下NSThread的使用 具体使用详情

1. NSThread是基于Objective-C的,更加面向对象。
2. 它可以通过三种方法开启子线程,分别是: initWithTarget:  detachNewThreadSelector:(detachNewThreadWithBlock: 至少iOS10系统) 和 performSelectorInBackground: 。 其中 initWithTarget: 需要调用 start() 方法才能开启子线程。
3. NSThread可以通过类方法 currentThread() 获取当前线程,通过类方法 mainThread()获得主线程,还可以退出线程(exit)和睡眠线程(sleepForTimeInterval)。
4. 在实际开发中一般很少用其开始子线程,常用的也就[NSThread currentThread];(获取当前线程)和[NSThread sleepForTimeInterval:3.0];(睡眠线程)

7. 简单的说一下GCD的使用 具体使用详情

1. GCD是基于C语言的,更加偏向于底层。
2. 使用GCD就两个步骤:
   2.1 创建一个队列(串行队列或并发队列)
   2.2 将任务追加到队列中,系统就会根据任务类型执行任务(同步执行或异步执行)
3. 队列的创建和获取
   3.1 队列可以通过 dispatch_queue_create 来创建,通过设置第二个参数用来识别是串行队列还是并发队列(串行队列: DISPATCH_QUEUE_SERIAL; 并发队列: DISPATCH_QUEUE_CONCURRENT),
   3.2 队列也可以通过 dispatch_get_global_queue 获取全局的并发队列 和 dispatch_get_main_queue 获取特殊的串行队列(主队列)。
4.任务都是放在Block中执行的,执行任务分为同步和异步,同步是:dispatch_sync(queue, ^{}); 异步是:dispatch_async(queue, ^{});异步(async)具备开启子线程的能力,但是在主队列中不会开启子线程。同步(sync)不具备开启子线程的能力,在主队列中会造成死锁(线程相互等待)
5. 组合使用情况如下: 
   5.1 同步(sync)  + 并发队列 (没有开启子线程,串行执行任务,在主线程)
   5.2 同步(sync)  + 串行队列 (没有开启子线程,串行执行任务,在主线程)
   5.3 同步(sync)  + 主线程   (造成死锁,主线程和同步任务相互等待)
   5.4 异步(async) + 并发队列 (有开启子线程 ,并发执行任务,在子线程)
   5.5 异步(async) + 串行队列 (只开启一条子线程,串行执行任务)
   5.6 异步(async) + 主线程   (没有开启子线程,串行执行任务,在主线程)
6. GCD的其他函数(方法)的使用
   6.1 栅栏函数:dispatch_barrier_sync (同步) 和 dispatch_barrier_async (异步)
   6.2 延时执行函数:dispatch_after  一定是将任务追加到主队列中(主线程)
   6.3 一次性函数:dispatch_once
   6.4 队列组函数: dispatch_group_async  通过: dispatch_group_notify 监听
   6.5 暂停当前线程函数: dispatch_group_wait

8. 简单的说一下NSOperation的使用 具体使用详情

1. NSOperation是基于GCD更高一层的封装,完全面向对象。
2. NSOperation的使用三个步骤: 
   2.1 创建操作:先将需要执行的操作封装到 NSOperation 的子类中。
   2.2 创建队列:创建 NSOperationQueue 对象。
   2.3 将操作加入到队列中:将 NSOperation 子类添加到 NSOperationQueue(队列) 对象中。
3. 创建操作。在不使用NSOperationQueue的情况下,单独使用 NSOperation 的子类封装操作, 不会开启子线程(同步执行操作)。
   3.1 NSOperation的子类有 NSInvocationOperation  NSBlockOperation 和自定义继承自NSOperation(通过实现方法(main)来封装操作)。
4. 创建队列。NSOperationQueue 一共有两种队列:主队列、自定义队列。其中自定义队列同时包含了串行、并发功能。
   4.1 通过 [NSOperationQueue mainQueue]; 获取主队列
   4.2 通过 [[NSOperationQueue alloc] init]; 创建自定义队列(非主队列,包含了串行、并发功能)。
5. 将操作加入到队列中。NSOperation 需要配合 NSOperationQueue 来实现多线程,我们需要将创建好的操作加入到队列中去。总共有两种方法:
   5.1 通过 addOperation: 添加操作到队列中 [queueObj addOperation:operationObj];
   5.2 通过 addOperationWithBlock: 直接创建操作 [queueObj addOperationWithBlock:^{}];
6. NSOperationQueue 创建的自定义队列同时具有串行、并发功能。通过设置属性 maxConcurrentOperationCount (最大并发操作数) 的个数 决定队列类型,默认情况下为 -1,表示不进行限制,可进行并发执行;为 1 时,队列为串行队列。只能串行执行;大于 1 时,队列为并发队列。
7. NSOperation其他操作
  7.1 操作之间添加依赖 addDependency:
  7.2 操作完成时回调  completionBlock = ^{};
  7.3 取消队列的所有操作 cancelAllOperations
  7.4 判断队列是否处于暂停状态  isSuspended
  7.5 向队列中添加操作数组 addOperations
  7.6 可取消操作 cancel (实质是标记 isCancelled 状态)
  7.7 判断操作状态 isFinished(操作是否已经完成)  isCancelled(操作是否已经取消) isExecuting(操作是否正在运行)  isReady(操作是否处于准备就绪状态)

9. NSThread GCD 和 NSOperation 之间的优缺点

1. NSThread
   优点:比其他两种更加轻量级,使用简单。
   缺点:线程之间的通信比较麻烦(最大缺点)。需要自己管理线程的生命周期([thread cancel];取消子线程)、线程同步、加锁、睡眠以及唤醒等。

2. GCD 
   优点:性能最高效(更接近底层)。不需要管理线程的生命周期,数据同步的事情。
   缺点: 基于C语言实现,不好理解。添加异步操作之间的事务性,顺序性和依赖关系 需要写更多的代码来实现。
     
3. Operation:
   优点:是对GCD的封装,更加面向对象。也是不需要管理线程的生命周期,数据同步的事情。
        可以使用自定义继承 NSOperation 的类,重写 main 方法,在其里面添加操作(任务)。
        可以更好的添加操作之间依赖和查看操作的状态,还可以设置操作的优先级和更好的取消正在执行中的操作。
   缺点:开启操作一般需要用到 NSOperation的两个子类 NSInvocationOperation 和 NSBlockOperation
        开启的操作需要添加到 NSOperationQueue 中才开启子线程。

10. iOS 代码加锁的几种方式

1. @synchronized(self)
2. NSConditionLock
3. NSCondition
4. NSLock
5. dispatch_semaphore_t (GCD中的函数)
6. OSSpinLock

这几种锁都可以带来原子性,性能的损耗从上至下依次更小。
  1. 使用 @synchronized(self) 给代码加锁

@interface ViewController ()

@property (nonatomic,assign) NSInteger ticketNumber;

@end
  
#pragma mark -使用GCD创建线程
-(void)startGCDAction{
    
    self.ticketNumber=50;  // 50张票
    
    // 1. 创建并发队列
    dispatch_queue_t queueObj=dispatch_queue_create("nameValue",DISPATCH_QUEUE_CONCURRENT);
    
    // 2. 开启多条异步线程
    for (NSInteger i=1; i<=5; i++) {
        dispatch_async(queueObj, ^{
            NSLog(@"做任务(%zi),当前线程:%@",i,[NSThread currentThread]);
            [self startSaleTicket]; // 开始卖票
        });
    }
}

// 通过@synchronized()加锁
-(void)startSaleTicket{
    do {
        [NSThread sleepForTimeInterval:0.3];
        // 下面是加锁的代码内容
        @synchronized (self) {
            if (self.ticketNumber<=0) break;
            NSLog(@"余票是: %zi 当前线程是: %@",self.ticketNumber,[NSThread currentThread]);
            self.ticketNumber-=1;
        }
        
    }
    while (self.ticketNumber);
}

最终运行结果:

2019-08-12 14:15:58.189393+0800 TestModel[87804:13566450] 做任务(2),当前线程:{number = 3, name = (null)}
2019-08-12 14:15:58.189548+0800 TestModel[87804:13566452] 做任务(1),当前线程:{number = 4, name = (null)}
2019-08-12 14:15:58.189868+0800 TestModel[87804:13566451] 做任务(3),当前线程:{number = 5, name = (null)}
2019-08-12 14:15:58.189917+0800 TestModel[87804:13566453] 做任务(4),当前线程:{number = 6, name = (null)}
2019-08-12 14:15:58.190395+0800 TestModel[87804:13566459] 做任务(5),当前线程:{number = 7, name = (null)}
2019-08-12 14:15:58.490183+0800 TestModel[87804:13566450] 余票是: 50 当前线程是: {number = 3, name = (null)}
2019-08-12 14:15:58.490588+0800 TestModel[87804:13566452] 余票是: 49 当前线程是: {number = 4, name = (null)}
2019-08-12 14:15:58.495170+0800 TestModel[87804:13566451] 余票是: 48 当前线程是: {number = 5, name = (null)}
2019-08-12 14:15:58.495440+0800 TestModel[87804:13566453] 余票是: 47 当前线程是: {number = 6, name = (null)}
2019-08-12 14:15:58.495664+0800 TestModel[87804:13566459] 余票是: 46 当前线程是: {number = 7, name = (null)}
2019-08-12 14:15:58.795679+0800 TestModel[87804:13566450] 余票是: 45 当前线程是: {number = 3, name = (null)}
2019-08-12 14:15:58.796089+0800 TestModel[87804:13566452] 余票是: 44 当前线程是: {number = 4, name = (null)}
2019-08-12 14:15:58.800381+0800 TestModel[87804:13566451] 余票是: 43 当前线程是: {number = 5, name = (null)}
2019-08-12 14:15:58.800643+0800 TestModel[87804:13566453] 余票是: 42 当前线程是: {number = 6, name = (null)}
2019-08-12 14:15:58.801037+0800 TestModel[87804:13566459] 余票是: 41 当前线程是: {number = 7, name = (null)}
2019-08-12 14:15:59.097379+0800 TestModel[87804:13566450] 余票是: 40 当前线程是: {number = 3, name = (null)}
2019-08-12 14:15:59.098778+0800 TestModel[87804:13566452] 余票是: 39 当前线程是: {number = 4, name = (null)}
2019-08-12 14:15:59.104982+0800 TestModel[87804:13566451] 余票是: 38 当前线程是: {number = 5, name = (null)}
2019-08-12 14:15:59.105416+0800 TestModel[87804:13566453] 余票是: 37 当前线程是: {number = 6, name = (null)}
2019-08-12 14:15:59.105727+0800 TestModel[87804:13566459] 余票是: 36 当前线程是: {number = 7, name = (null)}
2019-08-12 14:15:59.401558+0800 TestModel[87804:13566450] 余票是: 35 当前线程是: {number = 3, name = (null)}
2019-08-12 14:15:59.404078+0800 TestModel[87804:13566452] 余票是: 34 当前线程是: {number = 4, name = (null)}
2019-08-12 14:15:59.406779+0800 TestModel[87804:13566451] 余票是: 33 当前线程是: {number = 5, name = (null)}
2019-08-12 14:15:59.407160+0800 TestModel[87804:13566453] 余票是: 32 当前线程是: {number = 6, name = (null)}
2019-08-12 14:15:59.407764+0800 TestModel[87804:13566459] 余票是: 31 当前线程是: {number = 7, name = (null)}
2019-08-12 14:15:59.706818+0800 TestModel[87804:13566450] 余票是: 30 当前线程是: {number = 3, name = (null)}
2019-08-12 14:15:59.707735+0800 TestModel[87804:13566451] 余票是: 29 当前线程是: {number = 5, name = (null)}
2019-08-12 14:15:59.709405+0800 TestModel[87804:13566452] 余票是: 28 当前线程是: {number = 4, name = (null)}
2019-08-12 14:15:59.712616+0800 TestModel[87804:13566453] 余票是: 27 当前线程是: {number = 6, name = (null)}
2019-08-12 14:15:59.713001+0800 TestModel[87804:13566459] 余票是: 26 当前线程是: {number = 7, name = (null)}
2019-08-12 14:16:00.016941+0800 TestModel[87804:13566450] 余票是: 25 当前线程是: {number = 3, name = (null)}
2019-08-12 14:16:00.017259+0800 TestModel[87804:13566451] 余票是: 24 当前线程是: {number = 5, name = (null)}
2019-08-12 14:16:00.017491+0800 TestModel[87804:13566452] 余票是: 23 当前线程是: {number = 4, name = (null)}
2019-08-12 14:16:00.018511+0800 TestModel[87804:13566453] 余票是: 22 当前线程是: {number = 6, name = (null)}
2019-08-12 14:16:00.018640+0800 TestModel[87804:13566459] 余票是: 21 当前线程是: {number = 7, name = (null)}
2019-08-12 14:16:00.317675+0800 TestModel[87804:13566450] 余票是: 20 当前线程是: {number = 3, name = (null)}
2019-08-12 14:16:00.318162+0800 TestModel[87804:13566451] 余票是: 19 当前线程是: {number = 5, name = (null)}
2019-08-12 14:16:00.322806+0800 TestModel[87804:13566452] 余票是: 18 当前线程是: {number = 4, name = (null)}
2019-08-12 14:16:00.323192+0800 TestModel[87804:13566453] 余票是: 17 当前线程是: {number = 6, name = (null)}
2019-08-12 14:16:00.323446+0800 TestModel[87804:13566459] 余票是: 16 当前线程是: {number = 7, name = (null)}
2019-08-12 14:16:00.623299+0800 TestModel[87804:13566450] 余票是: 15 当前线程是: {number = 3, name = (null)}
2019-08-12 14:16:00.623643+0800 TestModel[87804:13566451] 余票是: 14 当前线程是: {number = 5, name = (null)}
2019-08-12 14:16:00.628154+0800 TestModel[87804:13566452] 余票是: 13 当前线程是: {number = 4, name = (null)}
2019-08-12 14:16:00.628523+0800 TestModel[87804:13566453] 余票是: 12 当前线程是: {number = 6, name = (null)}
2019-08-12 14:16:00.628825+0800 TestModel[87804:13566459] 余票是: 11 当前线程是: {number = 7, name = (null)}
2019-08-12 14:16:00.928590+0800 TestModel[87804:13566450] 余票是: 10 当前线程是: {number = 3, name = (null)}
2019-08-12 14:16:00.928799+0800 TestModel[87804:13566451] 余票是: 9 当前线程是: {number = 5, name = (null)}
2019-08-12 14:16:00.933344+0800 TestModel[87804:13566452] 余票是: 8 当前线程是: {number = 4, name = (null)}
2019-08-12 14:16:00.933492+0800 TestModel[87804:13566459] 余票是: 7 当前线程是: {number = 7, name = (null)}
2019-08-12 14:16:00.933836+0800 TestModel[87804:13566453] 余票是: 6 当前线程是: {number = 6, name = (null)}
2019-08-12 14:16:01.231961+0800 TestModel[87804:13566450] 余票是: 5 当前线程是: {number = 3, name = (null)}
2019-08-12 14:16:01.232049+0800 TestModel[87804:13566451] 余票是: 4 当前线程是: {number = 5, name = (null)}
2019-08-12 14:16:01.233846+0800 TestModel[87804:13566452] 余票是: 3 当前线程是: {number = 4, name = (null)}
2019-08-12 14:16:01.234564+0800 TestModel[87804:13566453] 余票是: 2 当前线程是: {number = 6, name = (null)}
2019-08-12 14:16:01.238114+0800 TestModel[87804:13566459] 余票是: 1 当前线程是: {number = 7, name = (null)}

  1. 使用 NSLock 给代码加锁
@interface ViewController ()

@property (nonatomic,strong) NSLock *addLock;

@end

-(NSLock *)addLock{
    if (_addLock == nil ) {
        _addLock = [[NSLock alloc]init];
    }
    return _addLock;
}
 
#pragma mark -使用GCD创建线程
-(void)startGCDAction{
    
    self.ticketNumber=50;  // 50张票
    
    // 1. 创建并发队列
    dispatch_queue_t queueObj=dispatch_queue_create("nameValue",DISPATCH_QUEUE_CONCURRENT);
    
    // 2. 开启多条异步线程
    for (NSInteger i=1; i<=5; i++) {
        dispatch_async(queueObj, ^{
            NSLog(@"做任务(%zi),当前线程:%@",i,[NSThread currentThread]);
            [self startSaleTicket]; // 开始卖票
        });
    }
}

// 通过 NSLock 加锁
-(void)startSaleTicket{

    do {
        
        [NSThread sleepForTimeInterval:0.3];
        
        [self.addLock lock];   // 加锁
        if (self.ticketNumber<=0) break;
        NSLog(@"余票是: %zi 当前线程是: %@",self.ticketNumber,[NSThread currentThread]);
        self.ticketNumber-=1;
        [self.addLock unlock]; // 解锁
    }
    while (self.ticketNumber);

}

最终运行结果:

2019-08-12 14:31:28.968327+0800 TestModel[87809:13567989] 做任务(2),当前线程:{number = 4, name = (null)}
2019-08-12 14:31:28.968620+0800 TestModel[87809:13567988] 做任务(1),当前线程:{number = 3, name = (null)}
2019-08-12 14:31:28.969657+0800 TestModel[87809:13567986] 做任务(3),当前线程:{number = 5, name = (null)}
2019-08-12 14:31:28.969713+0800 TestModel[87809:13567987] 做任务(4),当前线程:{number = 6, name = (null)}
2019-08-12 14:31:28.970159+0800 TestModel[87809:13567992] 做任务(5),当前线程:{number = 7, name = (null)}
2019-08-12 14:31:29.273489+0800 TestModel[87809:13567989] 余票是: 50 当前线程是: {number = 4, name = (null)}
2019-08-12 14:31:29.273594+0800 TestModel[87809:13567988] 余票是: 49 当前线程是: {number = 3, name = (null)}
2019-08-12 14:31:29.273889+0800 TestModel[87809:13567992] 余票是: 48 当前线程是: {number = 7, name = (null)}
2019-08-12 14:31:29.273965+0800 TestModel[87809:13567987] 余票是: 47 当前线程是: {number = 6, name = (null)}
2019-08-12 14:31:29.274026+0800 TestModel[87809:13567986] 余票是: 46 当前线程是: {number = 5, name = (null)}
2019-08-12 14:31:29.577287+0800 TestModel[87809:13567989] 余票是: 45 当前线程是: {number = 4, name = (null)}
2019-08-12 14:31:29.577437+0800 TestModel[87809:13567988] 余票是: 44 当前线程是: {number = 3, name = (null)}
2019-08-12 14:31:29.577606+0800 TestModel[87809:13567992] 余票是: 43 当前线程是: {number = 7, name = (null)}
2019-08-12 14:31:29.577718+0800 TestModel[87809:13567987] 余票是: 42 当前线程是: {number = 6, name = (null)}
2019-08-12 14:31:29.577993+0800 TestModel[87809:13567986] 余票是: 41 当前线程是: {number = 5, name = (null)}
2019-08-12 14:31:29.882881+0800 TestModel[87809:13567989] 余票是: 40 当前线程是: {number = 4, name = (null)}
2019-08-12 14:31:29.883291+0800 TestModel[87809:13567988] 余票是: 39 当前线程是: {number = 3, name = (null)}
2019-08-12 14:31:29.883545+0800 TestModel[87809:13567987] 余票是: 38 当前线程是: {number = 6, name = (null)}
2019-08-12 14:31:29.883949+0800 TestModel[87809:13567986] 余票是: 37 当前线程是: {number = 5, name = (null)}
2019-08-12 14:31:29.884300+0800 TestModel[87809:13567992] 余票是: 36 当前线程是: {number = 7, name = (null)}
2019-08-12 14:31:30.188301+0800 TestModel[87809:13567989] 余票是: 35 当前线程是: {number = 4, name = (null)}
2019-08-12 14:31:30.188728+0800 TestModel[87809:13567988] 余票是: 34 当前线程是: {number = 3, name = (null)}
2019-08-12 14:31:30.189026+0800 TestModel[87809:13567987] 余票是: 33 当前线程是: {number = 6, name = (null)}
2019-08-12 14:31:30.189603+0800 TestModel[87809:13567986] 余票是: 32 当前线程是: {number = 5, name = (null)}
2019-08-12 14:31:30.190006+0800 TestModel[87809:13567992] 余票是: 31 当前线程是: {number = 7, name = (null)}
2019-08-12 14:31:30.489850+0800 TestModel[87809:13567988] 余票是: 30 当前线程是: {number = 3, name = (null)}
2019-08-12 14:31:30.490175+0800 TestModel[87809:13567987] 余票是: 29 当前线程是: {number = 6, name = (null)}
2019-08-12 14:31:30.493747+0800 TestModel[87809:13567989] 余票是: 28 当前线程是: {number = 4, name = (null)}
2019-08-12 14:31:30.494048+0800 TestModel[87809:13567986] 余票是: 27 当前线程是: {number = 5, name = (null)}
2019-08-12 14:31:30.494877+0800 TestModel[87809:13567992] 余票是: 26 当前线程是: {number = 7, name = (null)}
2019-08-12 14:31:30.795292+0800 TestModel[87809:13567988] 余票是: 25 当前线程是: {number = 3, name = (null)}
2019-08-12 14:31:30.795687+0800 TestModel[87809:13567987] 余票是: 24 当前线程是: {number = 6, name = (null)}
2019-08-12 14:31:30.795958+0800 TestModel[87809:13567989] 余票是: 23 当前线程是: {number = 4, name = (null)}
2019-08-12 14:31:30.796489+0800 TestModel[87809:13567986] 余票是: 22 当前线程是: {number = 5, name = (null)}
2019-08-12 14:31:30.800184+0800 TestModel[87809:13567992] 余票是: 21 当前线程是: {number = 7, name = (null)}
2019-08-12 14:31:31.099967+0800 TestModel[87809:13567986] 余票是: 20 当前线程是: {number = 5, name = (null)}
2019-08-12 14:31:31.100050+0800 TestModel[87809:13567988] 余票是: 19 当前线程是: {number = 3, name = (null)}
2019-08-12 14:31:31.100090+0800 TestModel[87809:13567987] 余票是: 18 当前线程是: {number = 6, name = (null)}
2019-08-12 14:31:31.100198+0800 TestModel[87809:13567989] 余票是: 17 当前线程是: {number = 4, name = (null)}
2019-08-12 14:31:31.102069+0800 TestModel[87809:13567992] 余票是: 16 当前线程是: {number = 7, name = (null)}
2019-08-12 14:31:31.405114+0800 TestModel[87809:13567986] 余票是: 15 当前线程是: {number = 5, name = (null)}
2019-08-12 14:31:31.405197+0800 TestModel[87809:13567988] 余票是: 14 当前线程是: {number = 3, name = (null)}
2019-08-12 14:31:31.405238+0800 TestModel[87809:13567987] 余票是: 13 当前线程是: {number = 6, name = (null)}
2019-08-12 14:31:31.405293+0800 TestModel[87809:13567989] 余票是: 12 当前线程是: {number = 4, name = (null)}
2019-08-12 14:31:31.407180+0800 TestModel[87809:13567992] 余票是: 11 当前线程是: {number = 7, name = (null)}
2019-08-12 14:31:31.705607+0800 TestModel[87809:13567986] 余票是: 10 当前线程是: {number = 5, name = (null)}
2019-08-12 14:31:31.705747+0800 TestModel[87809:13567988] 余票是: 9 当前线程是: {number = 3, name = (null)}
2019-08-12 14:31:31.705857+0800 TestModel[87809:13567987] 余票是: 8 当前线程是: {number = 6, name = (null)}
2019-08-12 14:31:31.705953+0800 TestModel[87809:13567989] 余票是: 7 当前线程是: {number = 4, name = (null)}
2019-08-12 14:31:31.707719+0800 TestModel[87809:13567992] 余票是: 6 当前线程是: {number = 7, name = (null)}
2019-08-12 14:31:32.007136+0800 TestModel[87809:13567986] 余票是: 5 当前线程是: {number = 5, name = (null)}
2019-08-12 14:31:32.007306+0800 TestModel[87809:13567988] 余票是: 4 当前线程是: {number = 3, name = (null)}
2019-08-12 14:31:32.007378+0800 TestModel[87809:13567987] 余票是: 3 当前线程是: {number = 6, name = (null)}
2019-08-12 14:31:32.007499+0800 TestModel[87809:13567989] 余票是: 2 当前线程是: {number = 4, name = (null)}
2019-08-12 14:31:32.012931+0800 TestModel[87809:13567992] 余票是: 1 当前线程是: {number = 7, name = (null)}

11. 说一下你对Runtime的认识 具体认识

题外话:计算机唯一能识别的语言是机器语言,高级编程语言不能被直接识别,需要先编译为汇编语言,再由汇编语言编译为机器语言才能被计算机识别。而 Objective-C语言不能被直接编译为汇编语言,它必须先编译为C语言,然后再编译为汇编语言,最后再由汇编语言编译为机器语言才能被计算机识别。 从OC到C语言的过渡就是由runtime来实现的。我们使用OC进行面向对象开发,但是C语言更多的是面向过程开发,这就需要将面向对象的类转变为面向过程的结构体。

1. Runtime 是iOS中的一个运行时系统,是苹果用C语言和汇编语言编写的一套底层纯C语言API。
2. Runtime正是 Objective-C这门动态语言的核心(数据类型的确定由编译时推迟到运行时)。从OC到C语言的过渡就是由Runtime来实现的。
3. OC代码最终都会被编译器转化为运行时代码,通过消息机制决定函数调用方式(objc_msgSend)。
4. OC类、对象和方法等都会被Runtime转化成C语言中的结构体。
5. 在Runtime中,id 是一个指向 objc_object 结构体的指针;class 是一个指向 objc_class 结构体的指针
6. SEL 是一个指向 objc_selector 结构体的指针; Ivar 是一个指向 objc_ivar 的结构体的指针
7. Method 是一个指向 objc_method 的结构体的指针;IMP 是一个函数指针

12. 说一下Runtime的使用情景 具体使用

1. 动态方法交换  (method_exchangeImplementations)
2. 给分类添加属性 (objc_setAssociatedObject 和 objc_getAssociatedObject)
3. 获取类的详细信息
   3.1 属性列表(class_copyPropertyList)
   3.2 获取成员变量(class_copyIvarList)
   3.3 获取所有方法(class_copyMethodList)
   3.4 获取当前遵循的所有协议(class_copyProtocolList)
4. 解决同一方法高频率调用的效率问题
5. 方法动态解析与消息转发
   5.1 动态添加方法
   5.2 解决方法无响应崩溃问题
6. 动态操作属性
  6.1 动态修改属性变量
  6.2 实现 NSCoding 的自动归档和解档
  6.3 实现字典与模型的转换

更新中()...

你可能感兴趣的:(iOS面试题及答案(二))