# iOS 原生面试题精选

覆盖语言基础、系统原理、UI、并发、架构、性能、安全、测试与发布。每题都有 30 秒速记、代码示例、详细解析和面试官追问,流程类题目配了时序图。

# 1 Objective-C 语言基础

# 1. [obj foo] 这一行在运行时到底发生了什么?找不到方法时还有哪几次补救机会?

⚡ 30 秒速记

  • [obj foo] 编译后是 objc_msgSend(obj, @selector(foo)),调用哪个函数到运行时才定 = 动态绑定
  • 查找顺序:isa 找到类 → 方法缓存 cache → 本类方法列表 → 沿 superclass 一路往上查,找到就写进缓存
  • 找不到有三次机会:+resolveInstanceMethod: 动态加方法 → -forwardingTargetForSelector: 换个接收者 → -methodSignatureForSelector: + -forwardInvocation: 完整转发
  • 三步都没接住 → -doesNotRecognizeSelector: → unrecognized selector sent to instance 崩溃
  • 给 nil 发消息不会崩,直接返回 0 / nil;这是特性,也是很多「怎么没反应」的 问题来源

[obj foo] 在编译期只是变成一次 objc_msgSend(obj, @selector(foo)) 调用,真正执行哪段代码,是运行时顺着 isa 去类里现查的。 先查方法缓存,命中就直接跳过去,这也是消息发送没想象中慢的原因;没命中再查本类方法列表、父类,一路查到 NSObject,找到后写进缓存。全链路都没找到,运行时还给三次机会:先问类要不要动态补一个方法,再问能不能把消息丢给别的对象,最后打包成 NSInvocation 让你自己处理。三次都不接,就是大家熟悉的 unrecognized selector 崩溃。

时序图 · 5 个参与者 / 10 步
alt 缓存或方法列表命中一直查到根类都没有调用方调用方objc_msgSendobjc_msgSend类与父类类与父类动态解析动态解析消息转发消息转发发送 foo 消息1按 isa 找类,先查方法缓存2返回 IMP 并写入缓存3跳到 IMP 执行4调 resolveInstanceMethod5没有动态添加方法6调 forwardingTargetForSelector7返回 nil,不换接收者8要方法签名并 forwardInvocation9仍未处理,doesNotRecognizeSelector 崩溃10

# 编译器把方法调用变成了什么

Objective-C 的方法调用不是 C++ 那种编译期就定好的虚表跳转,而是「发消息」。比如下面这行:

NSString *s = [user nameWithPrefix:@"Mr."];
// 编译后大致等价于:
NSString *s = ((NSString *(*)(id, SEL, NSString *))objc_msgSend)(user, @selector(nameWithPrefix:), @"Mr.");
webapp
公众号
开发者导航
切换夜间模式
点击侧边栏上一篇
点击侧边栏下一篇
折叠侧边栏
收起全部