2020年大厂面试题

33次阅读

共计 4878 个字符,预计需要花费 13 分钟才能阅读完成。

1、NSArray 与 NSSet 的区别?

  • NSArray 内存中存储地址连续,而 NSSet 不连续
  • NSSet 效率高,内部使用 hash 查找;NSArray 查找需要遍历
  • NSSet 通过 anyObject 访问元素,NSArray 通过下标访问

2、NSHashTable 与 NSMapTable?

  • NSHashTable 是 NSSet 的通用版本,对元素弱引用,可变类型;可以在访问成员时 copy
  • NSMapTable 是 NSDictionary 的通用版本,对元素弱引用,可变类型;可以在访问成员时 copy

(注:NSHashTable 与 NSSet 的区别:NSHashTable 可以通过 option 设置元素弱引用 /copyin,只有可变类型。但是添加对象的时候 NSHashTable 耗费时间是 NSSet 的两倍。
NSMapTable 与 NSDictionary 的区别:同上)

3、属性关键字 assign、retain、weak、copy

  • assign:用于基本数据类型和结构体。如果修饰对象的话,当销毁时,属性值不会自动置 nil,可能造成野指针。
  • weak:对象引用计数为 0 时,属性值也会自动置 nil
  • retain:强引用类型,ARC 下相当于 strong,但 block 不能用 retain 修饰,因为等同于 assign 不安全。
  • strong:强引用类型,修饰 block 时相当于 copy。

4、weak 属性如何自动置 nil 的?

  • Runtime 会对 weak 属性进行内存布局,构建 hash 表:以 weak 属性对象内存地址为 key,weak 属性值 (weak 自身地址) 为 value。当对象引用计数为 0 dealloc 时,会将 weak 属性值自动置 nil。

5、Block 的循环引用、内部修改外部变量、三种 block

  • block 强引用 self,self 强引用 block
  • 内部修改外部变量:block 不允许修改外部变量的值,这里的外部变量指的是栈中指针的内存地址。__block 的作用是只要观察到变量被 block 使用,就将外部变量在栈中的内存地址放到堆中。
  • 三种 block:NSGlobalBlack(全局)、NSStackBlock(栈 block)、NSMallocBlock(堆 block)

6、KVO 底层实现原理?手动触发 KVO?swift 如何实现 KVO?

  • KVO 原理:当观察一个对象时,runtime 会动态创建继承自该对象的类,并重写被观察对象的 setter 方法,重写的 setter 方法会负责在调用原 setter 方法前后通知所有观察对象值得更改,最后会把该对象的 isa 指针指向这个创建的子类,对象就变成子类的实例。
  • 如何手动触发 KVO:在 setter 方法里,手动实现 NSObject 两个方法:willChangeValueForKey、didChangeValueForKey
  • swift 的 kvo:继承自 NSObject 的类,或者直接 willset/didset 实现。

7、categroy 为什么不能添加属性?怎么实现添加?与 Extension 的区别?category 覆盖原类方法?多个 category 调用顺序

  • Runtime 初始化时 categroy 的内存布局已经确定,没有 ivar,所以默认不能添加属性。
  • 使用 runtime 的关联对象,并重写 setter 和 getter 方法。
  • Extenstion 编译期创建,可以添加成员变量 ivar,一般用作隐藏类的信息。必须要有类的源码才可以添加,如 NSString 就不能创建 Extension。
  • category 方法会在 runtime 初始化的时候 copy 到原来前面,调用分类方法的时候直接返回,不再调用原类。如何保持原类也调用(https://www.jianshu.com/p/40e28c9f9da5)。
  • 多个 category 的调用顺序按照:Build Phases ->Complie Source 中的编译顺序。

8、load 方法和 initialize 方法的异同。——主要说一下执行时间,各自用途,没实现子类的方法会不会调用父类的?
load initialize 调用时机 app 启动后,runtime 初始化的时候 第一个方法调用前调用 调用顺序 父类 -> 本类 -> 分类 父类 -> 本类(如果有分类直接调用分类,本类不会调用) 没实现子类的方法会不会调用父类的 否 是 是否沿用父类实现 否 是

9、对 runtime 的理解。——主要是方法调用时如何查找缓存,如何找到方法,找不到方法时怎么转发,对象的内存布局

OC 中向对象发送消息时,runtime 会根据对象的 isa 指针找到对象所属的类,然后在该类的方法列表和父类的方法列表中寻找方法执行。如果在最顶层父类中没找到方法执行,就会进行消息转发:Method resoution(实现方法)、fast forwarding(转发给其他对象)、normal forwarding(完整消息转发。可以转发给多个对象)

10、runtime 中,SEL 和 IMP 的区别?

每个类对象都有一个方法列表,方法列表存储方法名、方法实现、参数类型,SEL 是方法名(编号),IMP 指向方法实现的首地址

11、autoreleasepool 的原理和使用场景?

  • 若干个 autoreleasepoolpage 组成的双向链表的栈结构,objc_autoreleasepoolpush、objc_autoreleasepoolpop、objc_autorelease
  • 使用场景:多次创建临时变量导致内存上涨时,需要延迟释放
  • autoreleasepoolpage 的内存结构:4k 存储大小

12、Autorelase 对象什么时候释放

在没有手加 Autorelease Pool 的情况下,Autorelease 对象是在当前的 runloop 迭代结束时释放的,而它能够释放的原因是系统在每个 runloop 迭代中都加入了自动释放池 Push 和 Pop。

13、Runloop 与线程的关系?Runloop 的 mode? Runloop 的作用?内部机制?

  • 每一个线程都有一个 runloop,主线程的 runloop 默认启动。
  • mode:主要用来指定事件在运行时循环的优先级
  • 作用:保持程序的持续运行、随时处理各种事件、节省 cpu 资源(没事件休息释放资源)、渲染屏幕 UI

14、iOS 中使用的锁、死锁的发生与避免

  • @synchronized、信号量、NSLock 等
  • 死锁:多个线程同时访问同一资源,造成循环等待。GCD 使用异步线程、并行队列

15、NSOperation 和 GCD 的区别

  • GCD 底层使用 C 语言编写高效、NSOperation 是对 GCD 的面向对象的封装。对于特殊需求,如取消任务、设置任务优先级、任务状态监听,NSOperation 使用起来更加方便。
  • NSOperation 可以设置依赖关系,而 GCD 只能通过 dispatch_barrier_async 实现
  • NSOperation 可以通过 KVO 观察当前 operation 执行状态(执行 / 取消)
  • NSOperation 可以设置自身优先级(queuePriority)。GCD 只能设置队列优先级(DISPATCH_QUEUE_PRIORITY_DEFAULT),无法在执行的 block 中设置优先级
  • NSOperation 可以自定义 operation 如 NSInvationOperation/NSBlockOperation,而 GCD 执行任务可以自定义封装但没有那么高的代码复用度
  • GCD 高效,NSOperation 开销相对高

16、oc 与 js 交互

  • 拦截 url
  • JavaScriptCore(只适用于 UIWebView)
  • WKScriptMessageHandler(只适用于 WKWebView)
  • WebViewJavaScriptBridge(第三方框架)

17、swift 相比 OC 有什么优势?

18、struct、Class 的区别

  • class 可以继承,struct 不可以
  • class 是引用类型,struct 是值类型
  • struct 在 function 里修改 property 时需要 mutating 关键字修饰

19、访问控制关键字(public、open、private、filePrivate、internal)

  • public 与 open:public 在 module 内部中,class 和 func 都可以被访问 / 重载 / 继承,外部只能访问;而 open 都可以
  • private 与 filePrivate:private 修饰 class/func,表示只能在当前 class 源文件 /func 内部使用,外部不可以被继承和访问;而 filePrivate 表示只能在当前 swift 源文件内访问
  • internal:在整个模块或者 app 内都可以访问,默认访问级别,可写可不写

20、OC 与 Swift 混编

  • OC 调用 swift:import “ 工程名 -swift.h”@objc
  • swift 调用 oc:桥接文件

21、map、filter、reduce?map 与 flapmap 的区别?

  • map:数组中每个元素都经过某个方法转换,最后返回新的数组(xx.map({$0 \* $0}))
  • flatmap:同 map 类似,区别在 flatmap 返回的数组不存在 nil,并且会把 optional 解包;而且还可以把嵌套的数组打开变成一 个([[1,2],[2,3,4],[5,6]] ->[1,2,2,3,4,5,6])
  • filter:用户筛选元素(xxx.filter({$0 > 25}),筛选出大于 25 的元素组成新数组)
  • reduce:把数组元素组合计算为一个值,并接收初始值()

22、guard 与 defer

  • guard 用于提前处理错误数据,else 退出程序,提高代码可读性
  • defer 延迟执行,回收资源。多个 defer 反序执行,嵌套 defer 先执行外层,后执行内层

23、try、try? 与 try!

  • try:手动捕捉异常
  • try?:系统帮我们处理,出现异常返回 nil;没有异常返回对应的对象
  • try!:直接告诉系统,该方法没有异常。如果出现异常程序会 crash

24、@autoclosure:把一个表达式自动封装成闭包

25、throws 与 rethrows:throws 另一个 throws 时,将前者改为 rethrows

26、App 启动优化策略?main 函数执行前后怎么优化

  • 启动时间 = pre-main 耗时 +main 耗时
  • pre-main 阶段优化:
  • 删除无用代码
  • 抽象重复代码
  • +load 方法做的事情延迟到 initialize 中,或者 +load 的事情不宜花费太多时间
  • 减少不必要的 framework,或者优化已有 framework
  • Main 阶段优化
  • didFinishLauchingwithOptions 里代码延后执行
  • 首次启动渲染的页面优化

27、crash 防护?

  • unrecognized selector crash
  • KVO crash
  • NSNotification crash
  • NSTimer crash
  • Container crash(数组越界,插 nil 等)
  • NSString crash(字符串操作的 crash)
  • Bad Access crash(野指针)
  • UI not on Main Thread Crash (非主线程刷 UI (机制待改善))

28、内存泄露问题?

主要集中在循环引用问题中,如 block、NSTime、perform selector 引用计数问题。

29、UI 卡顿优化?

30、架构 & 设计模式

  • MVC 设计模式介绍
  • MVVM 介绍、MVC 与 MVVM 的区别?
  • ReactiveCocoa 的热信号与冷信号
  • 缓存架构设计 LRU 方案
  • SDWebImage 源码,如何实现解码
  • AFNetWorking 源码分析
  • 组件化的实施,中间件的设计
  • 哈希表的实现原理?如何解决冲突

31、数据结构 & 算法

  • 快速排序、归并排序
  • 二维数组查找(每一行都按照从左到右递增的顺序排序,每一列都按照从上到下递增的顺序排序。请完成一个函数,输入这样的一个二维数组和一个整数,判断数组中是否含有该整数)
  • 二叉树的遍历:判断二叉树的层数
  • 单链表判断环

32、计算机基础

  1. http 与 https?socket 编程?tcp、udp?get 与 post?
  2. tcp 三次握手与四次握手
  3. 进程与线程的区别
正文完
 0