美文网首页ios基础
2019 iOS 面试 -中级篇之 RunLoop

2019 iOS 面试 -中级篇之 RunLoop

作者: 赫子丰 | 来源:发表于2019-08-21 18:59 被阅读0次

    1. runloop是来做什么的?runloop和线程有什么关系?主线程默认开启了runloop么?子线程呢?

    RunLoop(消息循环):说白了就是一种事件监听循环。就好比是一个while循环,监听到事件就起来,没有就休息。
    基本作用:

    • 1.保持程序的持续运行(这也是iOS程序为什么能一直不会死的原因)。

    • 2.处理App中的各种事件(比如触摸事件、selector事件、定时器事件等)。

    • 3.节省CPU资源,提高程序性能,有事件就起来,没有就休息。

    Runloop与线程:

    • 1.Runloop和线程的关系:一个Runloop对应着一条唯一的线程。

    • 2.Runloop的创建:主线程Runloop已经创建好了,子线程的runloop需要手动创建。

    • 3.Runloop的生命周期:在第一次获取时创建,在线程结束时销毁。

    参考链接:都在说RunLoop...... 到底什么是RunLoop?

    2. runloop的mode是用来做什么的?有几种mode?

    iOS有五种模式,

    • 其中UIInitializationRunLoopModel应用程序启动时会使用,启动完成后将不再使用;
    • GSEventReceiveRunLoopMode这个是接受系统内部的Model,通常做不到。
    • 还有UITrackingRunLoopMode、NSDefaultRunLoopMode、NSRunLoopCommonModes三种模式是我们通常用到的,

    下文中会详细讲解,其中NSRunLoopCommonModes是一个占位符,NSDefaultRunLoopMode和UITrackingRunLoopMode都会绑定这个占位符。

    → UITrackingRunLoopMode : (优先切换!!)这个模式就是当UI事件交互的时候Runloop切换到的模式!!!
    场景:这一模式优先级最高,当UI事件交互的时候,都会优先切换到这一模式。
    
    → NSDefaultRunLoopMode :Runloop的默认模式!只要有事件就处理!
    场景:默认模式,只要有事件就会自动切换到此模式。
    
    → NSRunLoopCommonModes :占位符!!(在默认下和UITrackingRunLoopMode下!)
    场景:这个主要用在添加一个NSTimer到RunLoop中。是一个tag,本质上不是一个Mode,默认NSDefaultRunLoopMode和 NSTrackingRunLoopMode都绑定这个tag。
    

    参考:《招聘一个靠谱的iOS》面试题参考答案(下)

    3. 为什么把NSTimer对象以NSDefaultRunLoopMode(kCFRunLoopDefaultMode)添加到主运行循环以后,滑动scrollview的时候NSTimer却不动了?

    RunLoop只能运行在一种mode下,如果要换mode,当前的loop也需要停下重启成新的。利用这个机制,ScrollView滚动过程中NSDefaultRunLoopMode(kCFRunLoopDefaultMode)的mode会切换到UITrackingRunLoopMode来保证ScrollView的流畅滑动:只能在NSDefaultRunLoopMode模式下处理的事件会影响scrllView的滑动。

    如果我们把一个NSTimer对象以NSDefaultRunLoopMode(kCFRunLoopDefaultMode)添加到主运行循环中的时候, ScrollView滚动过程中会因为mode的切换,而导致NSTimer将不再被调度。

    同时因为mode还是可定制的,所以:

    Timer计时会被scrollView的滑动影响的问题可以通过将timer添加到NSRunLoopCommonModes(kCFRunLoopCommonModes)来解决。

    4. 苹果是如何实现Autorelease Pool的?

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

    objc_autoreleasepoolPush

    objc_autoreleasepoolPop

    objc_aurorelease

    看函数名就可以知道,对autorelease分别执行push,和pop操作。销毁对象时执行release操作。

    类结构

    1. isa指针?(对象的isa,类对象的isa,元类的isa都要说)

                   nil  
                    |
    (NSObject)根类对象 isa --> 根类元类 isa
                    |             | |
                父类对象 isa --> 父类元类 isa
                    |             | 
    

    实例对象 isa --> 类对象 isa --> 元类 isa

    2. 类方法和实例方法有什么区别?

    • 1.类方法以+号开头 对象方法以-号开头
    • 2.类方法直接 [类名 类方法名]调用 对象方法要创建对象后 [对象名 对象方法]调用
    • 3.类方法不能访问实例变量 对象方法可以访问实例变量
    • 4.类方法只能由类来调用 对象方法只能用对象来调用

    3. 介绍一下分类,能用分类做什么?内部是如何实现的?它为什么会覆盖掉原来的方法?

    类别(Category)主要有3个作用:

    • 将类的实现分散到多个不同文件或多个不同框架中。
    • 创建对私有方法的前向引用。
    • 向对象添加非正式协议。

    声明:@interface 类名(分类名称) @end
    实现:@implementation 类名(分类名称) @end

    注意:

    • (1)在分类只能增加方法,不能增加成员变量,如果要增加成员变量的话该考虑用继承去实现

    • (2)在分类实现方法中可以访问类中的成员变量但是不能访问类中的属性@property

    • (3)在分类中可以重新实现原类中的方法,但会将原类中的方法覆盖而失效。因为在执行对象成员方法的时候会优先去分类中查找,然后再去原类中去查找,最后去父类中去查找

    • (4)如果一个类有多个分类,而且分类中有同名的方法那么最后编译的分类会将前面编译的分类覆盖而执行输出

    4. 运行时能增加成员变量么?能增加属性么?如果能,如何增加?如果不能,为什么?

    ①给NSObject添加分类,在分类中添加属性。问题:@property在分类中作用:仅仅是生成get,set方法声明,不会生成get,set方法实现和下划线成员属性,所以要在.m文件实现setter/getter方法,用static保存下滑线属性,这样一来,当对象销毁时,属性无法销毁
    ②用runtime动态添加属性:本质是让属性与某个对象产生一段关联
    使用场景:给系统的类添加属性时
    

    分类 Category 可以添加属性,不能添加成员变量。分类中是可以为一个类添加属性的,但是一定做不到添加成员变量,Property是Property,Ivar是Ivar。分类里面不能添加Ivar是因为分类本身并不是一个真正的类,它并没有自己的ISA。

    在Objective-C提供的runtime函数中,确实有一个class_addIvar()函数用于给类添加成员变量,但是阅读过苹果的官方文档的人应该会看到:

    This function may only be called after objc_allocateClassPair 
    and before objc_registerClassPair.
    Adding an instance variable to an existing class is not supported.
    

    大概的意思说,这个函数只能在“构建一个类的过程中”调用。一旦完成类定义,就不能再添加成员变量了。经过编译的类在程序启动后就被runtime加载,没有机会调用addIvar。程序在运行时动态构建的类需要在调用objc_registerClassPair之后才可以被使用,同样没有机会再添加成员变量。

    因为方法和属性并不“属于”类实例,而成员变量“属于”类实例。我们所说的“类实例”概念,指的是一块内存区域,包含了isa指针和所有的成员变量。所以假如允许动态修改类成员变量布局,已经创建出的类实例就不符合类定义了,变成了无效对象。但方法定义是在objc_class中管理的,不管如何增删类方法,都不影响类实例的内存布局,已经创建出的类实例仍然可正常使用。

    然而如果在运行时动态生成一个类,就可以为其添加成员变量和方法。

    5. objc中向一个nil对象发送消息将会发生什么?(返回值是对象,是标量,结构体)

    • 如果一个方法返回值是一个对象,那么发送给nil的消息将返回0(nil)。例如:Person * motherInlaw = [ aPerson spouse] mother]; 如果spouse对象为nil,那么发送给nil的消息mother也将返回nil。
    • 如果方法返回值为指针类型,其指针大小为小于或者等于sizeof(void*),float,double,long double 或者long long的整型标量,发送给nil的消息将返回0。
    • 如果方法返回值为结构体,正如在《Mac OS X ABI 函数调用指南》,发送给nil的消息将返回0。结构体中各个字段的值将都是0。其他的结构体数据类型将不是用0填充的。
    • 如果方法的返回值不是上述提到的几种情况,那么发送给nil的消息的返回值将是未定义的。

    参考链接:iOS面试题总结

    相关阅读:

    1、iOS 面试题 --- 基础部分
    2、iOS 面试题 --- 中级篇 Block
    3、iOS 面试题 --- 中级篇 Runtime
    4、iOS 面试题 --- 高级篇

    相关文章

      网友评论

        本文标题:2019 iOS 面试 -中级篇之 RunLoop

        本文链接:https://www.haomeiwen.com/subject/pbnisctx.html