美文网首页
iOS面试的题目总结

iOS面试的题目总结

作者: SeanLink | 来源:发表于2022-01-04 18:32 被阅读0次

    UIView和CALlayer的关系:

    UIView和CALayer都遵循单一职责设计原则,UIView为其提供内容,以及负责处理触摸等事件,参与响应链.
    layer负责显示内容的contens

    点击事件传递的流程:

    点击屏幕时,将Touch以UIEvent的方式加入UIApplication事件队列中,UIApplication从事件队列中取出最新的触摸事件进行分发传递到UIWindow进行处理,UIWindow 会通过hitTest:withEvent:方法寻找触碰点所在的视图,视图寻找的顺序如下:

    UIApplication -> UIWindow -> rootView -> subview
    在顶级视图Root View 上调用pointInside:withEvent:方法判断触摸点是否在当前视图内;
    如果返回NO,那么hitTest:withEvent:返回nil;
    如果返回YES,那么它会向当前视图的所有子视图发送hitTest:withEvent:消息
    所有子视图的遍历顺序是从最顶层视图一直到到最底层视图,即从subviews数组的末尾向前遍历,直到有子视图返回非空对象或者全部子视图遍历完毕。

    UI显示过程

    CPU绘制图像 绘制动画
    GPU着色 图元装配 光栅化 片段着色 片段处理

    UI卡顿掉帧原因

    一帧画面CPU+GPU处理时长超过 16.7ms 也就是1/60秒

    滚动流畅优化

    CPU 层面
    子线程创建对象,调整,销毁,预排版(frame计算,文本计算),图片解码 ,
    预渲染(文本异步绘制,图片编解码等)

    GPU层面:
    纹理渲染
    视图混合层级减少

    UI视图异步绘制步骤

    调用setNeedsDisplay时在 [CALayer display]
    是否能响应 layer.delegate responds To
    @selector(displayLayer:) 返回YES能响应进入绘制入口 NO 系统绘制入口

    需要在方法外外面调用 [asLabel.layer setNeedsDisplay]; 不然会走系统的绘制流程
    - (void)displayLayer:(CALayer *)layer{
        //异步绘制入口
    }
    
    什么离屛渲染.

    在屏渲染 GPU的渲染操作在当前用于显示的屏幕缓冲区中进行
    离屛渲染 GPU的渲染操作在当前屏幕缓冲区外新开辟一个缓冲区进行渲染

    为何要避免离屛渲染

    卡顿和掉帧,增加GPU的工作量

    离屛渲染何时触发

    圆角(当和maskToBounds一起使用时)
    图层蒙版
    阴影
    开启光栅化

    如何避免(蒙版和阴影给固定路径)

    利用分类做哪些事情:
    1. 声明私有方法
    2. 分解体积庞大的类文件
    3. 把Framework的私有方法公开
    分类中同样的方法哪一个会生效. (如何调用到你想调用的那个方法)

    最后参加编译的方法会生效

    分类方法移动.
    会调整
    分类实现原理
    运行时决议,谁方法生效.决定为编译顺序,后编译覆盖先的.因为方法的查找原因.

    分类能否添加成员变量

    间接添加,通过关联对象方法(getAssociatedObject,setAssociatedObject,removeAssociatedObiects)

    关联对象的管理

    所有的关联对象全在一个AssociationsManager 通过 AssOciationsHashMap管理

    一般用扩展做什么

    声明私有属性
    私有方法
    私有成员变量

    分类和扩展区别
    分类:运行时决议,有声明,有实现,可以为系统类添加分类
    扩展:编译时决议,只以声明的形式存在,多数情况下寄生于宿主类中,不能为系统类进行扩展

    代理:

    软件设计模式

    传递方式一对一,通知为一对多

    代理为三方:
    协议: 要求代理方实现的接口( 成员属性和方法)
    委托方 : 调用代理方遵从的协议方法
    代理方: 按照协议实现方法

    通知:

    观察者模式 实现跨层传递消息
    传递为一对多

    KVO

    Key-value observing 的缩写,对观察者模式的又一实现
    手动实现kvo 采用isa混写(isa-swizzling)来实现KVO
    willchangevalueforkey
    didchangevalueforkey

    copy关键字

    可变对象的copy和mutableCopy都是深拷贝。
    不可变对象的copy是浅拷贝,mutableCopy是深拷贝。
    copy方法返回的都是不可变对象

    NSMutableArray --> copy--- NSArray(深拷贝)
    NSMutableArray --> mutableCopy --- NSMutableArray(深拷贝)

    NSArray --> copy--- NSArray(浅拷贝)
    NSArray --> mutableCopy--- NSMutableArray(浅拷贝)

    内存数据结构

    isa指针:
    32位:isa代表class的地址
    64位:isa值的部分代表class的地址

    关于对象:
    isa指向类对象
    关于类对象:
    isa指向元类对象

    内存区数据存储位置:
    代码段在低低址值,内核区在高地址值. 堆里面的数据从低往高分布,栈里面的数据从高往低分布.如下图


    image.png
    内存布局:
    

    栈区(stack):存储方法调用
    堆区(heap):通过alloc等分配的对象
    未初始化数据(bss):未初始化的全局变量等
    data:已初始化的全局变量等
    text:程序代码

    TaggedPointer 技术
    64位以后isa是一个联合体+位域的结构,联合体union内部成员为互斥存在.

      uintptr_t nonpointer        : 1;                                       
            uintptr_t has_assoc         : 1;                                       
            uintptr_t has_cxx_dtor      : 1;                                       
            uintptr_t shiftcls          : 33; /*MACH_VM_MAX_ADDRESS 0x1000000000*/ 
            uintptr_t magic             : 6;                                       
            uintptr_t weakly_referenced : 1;                                       
            uintptr_t unused            : 1;                                       
            uintptr_t has_sidetable_rc  : 1;                                       
            uintptr_t extra_rc          : 19
    

    -nonpointer: 是否开启NONPOINTER isa指针优化;
    -has_assoc: 对象是否含有关联引用
    -has_cxx_dtor:对象是否含有 C++ 或者 Objc 的析构器
    -shiftcls: 类的指针(重点)(arm64:33位,x86_64:44位)
    -magic: 对象是否初始化完成 (arm64:0x16 ,x86_64:0x3b)
    -weakly_referenced:是否为弱引用的对象
    -deallocating:对象是否正在执行析构函数(是否在释放内存)
    -has_sidetable_rc:判断是否需要用sidetable去处理引用计数,(extra_rc的大小影响到这个变量)
    -extra_rc: 存储该对象的引用计数值减一后的结果

    __block破解循环引用:
    MRC下,__block修饰对象不会增加其引1用计数,避免了循环引用。
    ARC下,__block修饰对象会被强引用,无法避免循环引用,需手动解环。

    什么是ARC?

    ARC就是自动的引用计数,顾名思义,是自动帮我们填写引用计数代码的一项功能,重写了set方法和dealloc方法.简单地理解ARC,就是通过指定的语法,让编译器(LLVM 3.0)在编译代码时,自动生成实例的引用计数管理部分代码,它只是一种代码静态分析(Static Analyzer)工具.

    实现weak后,为什么对象释放后会自动为nil

    runtime 对注册的类, 会进行布局,对于 weak 对象会放入一个 hash 表中。 用 weak 指向的对象内存地址作为 key,当此对象的引用计数为 0 的时候会 dealloc,假如 weak 指向的对象内存地址是 a ,那么就会以 a 为键, 在这个 weak 表中搜索,找到所有以 a 为键的 weak 对象,从而设置为 nil 。
    当weak指向的对象被释放时,处理weak指针的过程如下:
    1.调用objc_release
    2.因为对象的引用计数为0,所以执行dealloc
    3.在dealloc中,调用了_objc_rootDealloc函数
    4.在_objc_rootDealloc中,调用了object_dispose函数
    5.调用objc_destructInstance
    6.最后调用objc_clear_deallocating,详细过程如下:
    - 从weak表中获取废弃对象的地址为键值的记录
    - 将包含在记录中的所有附有 weak修饰符变量的地址,赋值为 nil
    - 将 weak表中该记录删除
    - 从引用计数表中删除废弃对象的地址为键值的记录

    苹果是如何实现AutoreleasePool的

    首先,AutoreleasePool是以链表的形式保存在内存中的.主要底层数据结构是:__AtAutoreleasePool、AutoreleasePoolPage.
    每个AutoreleasePoolPage对象占用4096个字节,在内存中的形式如下:


    image.png

    1.每一个AutoreleasePoolPage都有一个parent、child指针指向上一个和下一个.
    2.id *next指向了下一个能存放autorelease对象地址的区域
    3.调用push方法会将一个POOL_BOUNDARY(哨兵对象)入栈,并且返回其存放的内存地址
    4.调用pop方法时传入一个POOL_BOUNDARY的内存地址,会从最后一个入栈的对象开始发送release消息,直到遇到这个POOL_BOUNDARY
    5.POOL_BOUNDARY 就是哨兵对象,它是一个宏,值为nil,标志着一个自动释放池的边界。
    6.autoreleasepoolpage 是以双向链表的形式连接起来的,只有新建一个pool会执行push操作,这时候会在page中插入一个POOL_BOUNDARY,并不是每页都插

    什么是循环引用?你遇到过哪些循环引用,是怎样解决的?

    1. 相互持有情况下weak解决
    2. block情况下 __weak解决
    3. NSTimer可以使用第三方对象解决,或者不使用scheduledTimerWithTimeInterval,改用timerWithTimeInterval方法,

    什么是Block

    block是将函数及其执行上下文封装起来的对象

    多线程:

    NSOperation实现以下方案:
    需要和NSOperationQueue配合使用来实现多线程方案
    任务执行状态控制:

    isReady 当前任务是否处于就绪状态
    isExecuting 当前任务是否处于正在执行中状态
    isFinished 当前任务是否已执行完毕
    isCancelled 当前任务是否已取消
    

    最大并发量控制:
    queue.maxConcurrentOperationCount = 2;
    任务依赖:

     // 创建队列
        NSOperationQueue *queue = [[NSOperationQueue alloc]init];
        // 需要操作对象
        NSBlockOperation *op0 = [NSBlockOperation blockOperationWithBlock:^{
            NSLog(@"登录...");
        }];
        NSBlockOperation *op1 = [NSBlockOperation blockOperationWithBlock:^{
            [NSThread sleepForTimeInterval:2];
            NSLog(@"扣费...");
        }];
        NSBlockOperation *op2 = [NSBlockOperation blockOperationWithBlock:^{
            NSLog(@"下载文件...");
        }];
        // 添加依赖 -- 添加依赖的时候要注册不要循环依赖
        // 添加依赖的时候,条件要充分,
        [op1 addDependency:op0];
        [op2 addDependency:op1];
        op2.completionBlock = ^{
            NSLog(@"所有的都搞完了");
        };
        // 把所有的操作添加到队列
        [queue addOperations:@[op0, op1, op2] waitUntilFinished:NO];
    

    NSOperation:
    1.如果只重写main方法 ,底层控制变更任务执行完成状态,以及任务退出。
    2.如果重写了star方法,自行控制任务状态

    系统是怎样移除—个isFinished=YES的NSOperation的?
    通过KVO来监听的.

    NSThread:封装C语言的pthread,流程如下:
    start()->创建pthread->main()->[target performSelector:selector]->exit()

    怎样用GCD实现多读单写?
    栅栏异步:
    //异步栅栏调用设置数据
    dispatch_barrier_async (concurrent_queue, ^{
    [userCenterDic setObiect:obj forKey:key];
    });

    ioS系统为我们提供的几种多线程技术各自的特点是怎样的?

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

    关于锁的了解:
    自旋锁: 等待锁的线程会处于忙等(busy-wait)状态,一直占用着CPU资源

    1. 目前已经不再安全,可能会出现优先级反转问题
    2. 如果等待锁的线程优先级较高,它会一直占用着CPU资源,优先级低的线程就无法释放锁
    3. 需要导入头文件#import <libkern/OSAtomic.h>

    互斥锁: 从底层调用看,等待os_unfair_lock锁的线程会处于休眠状态,并非忙等

    1. 需要导入头文件#import <os/lock.h>
    2. 用于取代不安全的OSSpinLock ,从iOS10开始才支持

    递归锁: 递归锁有一个特点,就是同一个线程可以加锁N次而不会引发死锁。但是需要成对出现

    Foundation系列:
    NSLock是对mutex普通锁的封装,NSRecursiveLock也是对mutex递归锁的封装,API跟NSLock基本一致,NSCondition是对mutex和cond的封装,NSConditionLock是对NSCondition的进一步封装,可以设置具体的条件值

    你都用过哪些锁(线程同步方案)?结合实际谈谈你是怎样使用的?
    性能从高到低:
    os_unfair_lock(互斥锁)
    OSSpinLock(自旋锁)
    dispatch_semaphore(信号量,设置为1代表只允许1条线程访问资源,保证线程同步)
    pthread_mutex(互斥锁,等待锁的线程会处于休眠状态.#import <pthread.h>)
    dispatch_queue(DISPATCH_QUEUE_SERIAL)(串行队列,实现线程同步)
    NSLock
    NSCondition
    pthread_mutex(recursive)
    NSRecursiveLock
    NSConditionLock
    @synchronized(是对mutex递归锁的封装)

    HTTP相关

    1.请求报文


    ①是请求方法,GET和POST是最常见的HTTP方法,除此以外还包括DELETE、HEAD、OPTIONS、PUT、TRACE。
    ②为请求对应的URL地址,它和报文头的Host属性组成完整的请求URL。
    ③是协议名称及版本号。
    请求头:
    ④是HTTP的报文头,报文头包含若干个属性,格式为“属性名:属性值”,服务端据此获取客户端的信息。
    与缓存相关的规则信息,均包含在header中
    请求体:
    ⑤是报文体,它将一个页面表单中的组件值通过param1=value1&param2=value2的键值对形式编码成一个格式化串,它承载多个请求参数的数据。不但报文体可以传递请求参数,请求URL也可以通过类似于“/chapter15/user.html? param1=value1&param2=value2”的方式传递请求参数。

    2.响应报文


    image.png

    响应报文:
    ①报文协议及版本;
    ②状态码及状态描述;
    响应头:
    ③响应报文头,也是由多个属性组成;
    响应体:
    ④响应报文体,即我们真正要的“干货”

    3.GET和POST区别
    参数:
    GET是拼接到URL后面的,POST参数是在Body里面
    GET参数限制2048个字符,POST一般没有该限制
    GET请求不安全,POST请求比较安全

    GET:获取资源
    安全的、幂等的、可缓存的
    POST:处理资源
    非安全的、非幂等的、不可缓存的

    安全性:不应该引起server端的任何状态变化。常见的安全性的方式:GET, HEAD, OPTIONS;
    幂等性:同一个请求方法执行多次和执行一次的效果完全相同 PUT,DELETE
    可缓存性:请求是否可以被缓存(GET, HEAD)

    TCP和UDP:
    TCP,传输控制协议,可靠传输,无差错(超时重传),不丢失(丢弃丢失的),不重复(丢弃重复),按序到达(序号增大)
    UDP,用户数报协议

    UDP面向报文,即不拆分,也不合并

    下载:http速度的增长.
    慢开始->指数规律增长->拥塞避免,加法增大->网络拥塞->乘法减小->慢开始

    DNS解析:
    域名到IP地址的映射,DNS解析请求采用UDP数据报,且明文
    两种方法:递归查询和迭代查询(知道哪个DNS服务器可能知道)

    DNS劫持与HTTP的关系是怎样的?
    没有关系,DNS解析发生在HTTP建立连接之前,DNS解析请求使用UDP数据报,端口号53

    避免DNS劫持:
    使用httpDNS:使用DNS协议向DNS服务器的53端口进行请求->使用HTTP协议向DNS服务器的80端口进行请求
    长连接

    Cookie:
    怎样删除Cookie?
    •新cookie覆盖1日cookie
    •覆盖规则:name、path、domain等需要与原cookie一致
    •设置cookie的expires=过去的一个时间点,或者maxAge=0

    保证Cookie安全:
    1.对Cookie进行加密
    2.只在https上携带Cookie
    3.设置Cookie为httpOnly,防止跨站脚本攻击

    Session也是用来记录用户状态,区分用户的;状态存放在服务器端。

    设计模式

    设计模式:

    1. 责任链
    2. 桥接
    3. 适配器
    4. 单例
    5. 命今(行为参数化,隆低代码重合度)

    六大设计原则:

    1. 单一职责原则(UIView和CAlayer)
    2. 开闭原则(修改关闭,扩展开放即增加方法)
    3. 接口隔离原则(使用多个专门的协议,而并非一个庞大臃肿的协议,协议的方法尽量少)
    4. 依赖倒置原则(抽象不依赖具体实现,具体实现可以依赖抽象)
    5. 里氏替換原则(父类可以被子类无缝替换,且原有功能不受任何影响:如KVO)
    6. 迪米特法则(一个对象应对其他对象尽可能少了解,高聚合,低耦合)

    责任链:
    App点击处理的原则

    架构框架

    图片缓存器的封装需要考虑的问题:

    1. 存储的Size(各种图片尺寸存储多少张)
    2. 淘汰策略(先进先出或LRU算法多久未使用)
    3. 存储方式以及大小
    4. 图片请求的最大并发量
    5. 请求超时策略
    6. 优先级
    7. 在哪个阶段进行解码(磁盘读取后或网络请求返回后 )

    阅读时长框架: 页面式(push,pop),流式(朋友圈),自定义式(视频播放时间等)

    记录管理者对数据保存:
    管理统计记录数据,包含记录缓存,磁盘存储,上传器

    如何降低数据的丢失率?

    定期写入磁盘
    每当记录条数达到某个值的时候,写入到磁盘

    对于MVVM的理解:
    View包含两部分:View和ViewController
    ViewModel:对数据进行处理
    Model:数据

    RN数据流思想:打上标记,反向回到根节点.自顶向上遍历,更新需要更新的结点(等待下一次刷新时更新自己)

    算法略

    相关文章

      网友评论

          本文标题:iOS面试的题目总结

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