iOS(OC)面试小整理

2,364 阅读7分钟

1.OOP 编程思想

Object Oriented Programming(面向对象编程) 这种思想把一切看为对象,把现实中的事物及关系抽象成对象,使我们将处理现实中的实际问题简化为处理各种对象之间的问题

面向对象的三大特性

  • 封装:如 属性对成员变量的封装,方法对功能的封装,类对属性及方法的封装,提高复用性和安全性
  • 继承:如 子类继承父类,提高代码复用性
  • 多态:父类指针指向子类对象,多种表现形态,提高程序的拓展性

2.SOLID 设计原则

  • S: Single responsibility(单一职责) 一个类只负责一件事情 如:苹果设计 UIView 与 CALayer 就体现了这一原则,UIView 负责参与响应链、处理事件;CALayer 负责内容显示、动画
  • O:Open Closed(开闭) 对扩展开放,对修改关闭
  • L:Liskov substitute(里氏替换) 只要父类出现的地方子类就可以将之替换
  • I:Interface Segregation(接口隔离) 使用多个专门的协议而不是一个庞大臃肿的协议且协议内声明的方法尽量少 如:UITableViewDataSource、UITableViewDelegate 设计了两个接口
  • D:Dependency Inversion(依赖倒置) 抽象不应该依赖于具体实现,具体实现应该依赖于抽象 如:抽象父类定义了增删改查接口,至于后面是本地的还是远程的增删改查,是 sqlite3 还是 coredata 的增删改查,抽象父类不关心

3.属性关键字

  • 读写属性 readonly readwrite(默认)
  • 原子性 atomic(默认) nonatomic
  • 内存管理语义 assign(默认) strong weak copy
  • nonnull 不能为空 nullable 可以为空 也可以修饰方法参数,方法返回值

4.深拷贝、浅拷贝

  • 深拷贝:内容相同的、新的 内存空间,新的指针
  • 浅拷贝:拷贝的是指针
  • 不可变对象的 copy 为浅拷贝,mutableCopy 为深拷贝
  • 可变对象的 copy 和 mutableCopy 均为深拷贝

Q:NSString 为什么用 copy 修饰?用 strong 会有问题么?

A:保证其安全性

因为如果用 strong 修饰,在进行 setter 时,传进来的是一个NSMutableString 也是可以赋值的(这里也有多态的体现),但是如果 NSMutableString 发生变化的话,由于是 strong 修饰, NSString 的那个对象也会发生变化且无感知,这种风险就存在了,而如果用 copy 修饰,在 setter 时,如果传进来的不可变对象 NSString,那么 copy 为浅拷贝,如果传进来的是可变对象 NSMutableString,那么 copy 为深拷贝且 copy 出来的这个新对象变为了不可变的对象

5.事件传递 & 响应链

事件传递:当一个事件产生比如说一个点击事件,首先就要寻找最佳响应者,配合下面这两个方法,找到最佳响应者之后,UIApplication发送事件给UIWindow,UIWindow发送事件给最佳响应者

- (nullable UIView *)hitTest:(CGPoint)point withEvent:(nullable UIEvent *)event;   // recursively calls -pointInside:withEvent:. point is in the receiver's coordinate system
- (BOOL)pointInside:(CGPoint)point withEvent:(nullable UIEvent *)event;   // default returns YES if point is in bounds

响应链:如果最佳响应者可以处理这个事件,那就响应结束,如果不能响应事件则通过 nextResponder 传递给下一个响应者,如果传递到UIApplication还是不能响应则丢弃该事件

6.UI 卡顿掉帧的原因

当 FPS 低于 60 的时候,肉眼就会感到卡顿

所以每一帧绘制的时间需要小于 1000ms / 60 ≈ 16.7ms 才不会感觉到卡顿

系统生成图像信号是靠 CPU + GPU 共同工作的,所以优化的话也是从这两方面入手考虑

7. block 相关

概念

  • 将 “函数” 及 “执行其上下文” 封装起来的 “对象”
  • block的调用就是函数的调用

block捕获变量特性

在block中使用外部变量时,block会捕获其变量,具体规则如下

  • 局部变量为基本数据类型:捕获其值
  • 局部变量为对象类型:连同所有权修饰符一起捕获
  • 局部变量为静态的:以指针的形式捕获
  • 全局变量:不捕获
  • 全局静态变量:不捕获

__block

使用场景:在blcok内部对捕获的值进行赋值

  • 局部变量为基本数据类型、对象类型,需要__block
  • 静态局部变量、全局变量、全局静态变量,不需要__block,因为静态局部变量不捕获其指针,全局变量、全局静态变量不捕获

8. OC 开发中碰到的设计模式

  • 观察者模式:KVO、NSNotification
  • 代理模式:如 tableView 的 delegate 与 dataSource
  • 责任链模式:事件响应链

9.Runtime

概念:

Objective-C 扩展了 C 语言,并加入了面向对象特性和消息传递机制,而这个扩展的核心是一个用 C 和 编译语言 写的 Runtime 库。它是 Objective-C 面向对象和动态机制的基石。

isa 指针(一图胜千言)

消息传递机制:(一图胜千言)

消息转发机制:(一图胜千言)

使用场景:

  • 关联对象(Objective-C Associated Objects)给分类增加属性
  • Method Swizzling 各种 HOOK 操作
  • 实现 NSCoding 的自动归档和自动解档
  • 实现字典和模型的自动转换( MJExtension、YYModel )

10.RunLoop

概念:

通过内部维护的事件循环来对事件、消息进行管理的一个对象 没有消息的时候休眠以避免资源占用(用户态转为内核态),有消息时立即被唤醒(内核态转为用户态)

作用

  • 保持 App 的持续运行
  • 处理 App 中的各种事件(触摸、timer...)
  • 节省 CPU 资源以提高程序性能

其他

  • RunLoop 在同一段时间只能且必须在一种特定的 Mode 下运行
  • 更换 Mode 时需要退出当前 Loop 然后重启新的 Loop
  • RunLoop 与线程是一一对应的
  • 自己创建的线程默认是没有 RunLoop 的

RunLoop in cocoa

  • NSTimer
  • UIEvent
  • @autoreleasepool
  • CADisplayLink
  • GCD 中 dispatch 到 main queue 的 block 被分发到 Main RunLoop 执行

11.多线程相关

相关概念

队列:负责任务的调度 线程:负责任务的执行

// OC 中涉及的队列
dispatch_queue_t _cocurrentQueue;
dispatch_queue_t _globalCocurrentQueue;
dispatch_queue_t _serialQueue;
dispatch_queue_t _mainQueue;  

串行队列

_serialQueue = dispatch_queue_create("serialQueue", DISPATCH_QUEUE_SERIAL);
_mainQueue = dispatch_get_main_queue();

并行队列

_cocurrentQueue = dispatch_queue_create("concurrentQueue", DISPATCH_QUEUE_CONCURRENT);
_globalCocurrentQueue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);

系统提供的并行队列优先级

#define DISPATCH_QUEUE_PRIORITY_HIGH 2
#define DISPATCH_QUEUE_PRIORITY_DEFAULT 0
#define DISPATCH_QUEUE_PRIORITY_LOW (-2)
#define DISPATCH_QUEUE_PRIORITY_BACKGROUND INT16_MIN
  • 同步提交任务到串行队列 按顺序执行
  • 异步提交任务到串行队列 按顺序执行,虽然是异步,但是有串行队列的特点,前一个任务没有结束,队列不会调度
  • 同步提交任务到并行队列 按顺序执行,虽然并行队列不用等待前一个任务结束,但是是同步执行,所以不会开新线程,还是会顺序执行
  • 异步提交任务到并行队列 不按顺序执行,异步开启新线程 + 并行队列的特点,可以实现任务的并发,不是立刻执行

小红书面试题:有5个任务,要求前三个任务顺序执行,4、5两个任务并行执行,并在5个任务都执行结束后执行其他

{
    dispatch_queue_t global_queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
    
    dispatch_group_t group = dispatch_group_create();
    
    dispatch_group_async(group, global_queue, ^{
        dispatch_sync(global_queue, ^{
            sleep(5);
            NSLog(@"1 %@",[NSThread currentThread]);
        });
        dispatch_sync(global_queue, ^{
            sleep(3);
            NSLog(@"2 %@",[NSThread currentThread]);
        });
        dispatch_sync(global_queue, ^{
            sleep(1);
            NSLog(@"3 %@",[NSThread currentThread]);
        });
    });
    
    dispatch_group_async(group, global_queue, ^{
        NSLog(@"4 %@",[NSThread currentThread]);
    });
    
    dispatch_group_async(group, global_queue, ^{
        NSLog(@"5 %@",[NSThread currentThread]);
    });
    
    dispatch_group_notify(group, dispatch_get_main_queue(), ^{
        NSLog(@"三个任务都执行完毕后执行");
    });
}

执行结果:

携程面试题:在用户登录结束后,并行执行用户的操作

{
    dispatch_sync(_globalCocurrentQueue, ^{
        NSLog(@"登录");
    });
    
    dispatch_async(_globalCocurrentQueue, ^{
        NSLog(@"下载攻略");
    });
    
    dispatch_async(_globalCocurrentQueue, ^{
        NSLog(@"下载音乐");
    });
}

12.HTTP 请求 get与post 区别

  • 形式上的区别: get 请求参数拼接在 url 之后以 ?分割,post 请求参数在报文体中 所以说 post 请求方式参数体积要大于 get
  • 语义上的区别: get :获取资源,安全的、幂等的、可缓存的 post:处理资源,不安全的、不幂等的,不可缓存的

安全的:不引起 server 端任何状态变化 幂等的:同一请求方法执行多次与执行一次效果相同

13.HTTPS

空了继续更

番外

在简书准备发布呢,系统提示需要到 9.28 才能发,突然想到还有个好平台--掘金,就直接粘过来了😆