好的,iOS面试中考察Objective-C基础知识的范围非常集中,主要围绕其动态特性和内存管理两大核心。下面我为你梳理了高频考点和回答要点,可以用这个清单来查漏补缺。
为了让你有个整体印象,先用一个表格汇总核心考察领域和关键问题:
| 核心考察领域 | 关键问题与知识点 |
|---|---|
| 🧠 内存管理 | ARC原理、属性修饰符、循环引用及破解方法 |
| 🔄 Runtime机制 | 消息传递与转发、Method Swizzling、KVC/KVO原理 |
| 📝 语法与特性 | 分类 vs 扩展、协议与代理、@property底层 |
| 🧵 多线程与块 | GCD、线程安全、Block的类型与内存管理 |
🧠 内存管理(ARC与引用计数)
这是OC面试的重中之重。
-
ARC原理
- 问题:谈谈你对ARC的理解,它是如何工作的?
-
回答要点:ARC是编译期特性,编译器在编译时自动在合适的位置插入
retain,release,autorelease等代码来管理引用计数,而不是垃圾回收。需要理解引用计数的增减规则(如创建、强引用、释放、超出作用域等)。
-
属性修饰符
-
问题:
strong,weak,copy,assign有什么区别?分别用于什么场景? -
回答要点:
-
strong:强引用,默认。引用计数+1,只要强引用存在,对象就不会被释放。 -
weak:弱引用。不增加引用计数,当对象被释放时,指针自动置为nil。用于解决循环引用(如Delegate、Block)。 -
copy:建立副本。常用于保护NSString,NSArray等可变对象的封装性,防止在不知情时被外部修改。对于遵守NSCopying协议的对象,会调用copy方法。 -
assign:简单赋值。不改变引用计数,用于基本数据类型(如NSInteger,CGFloat)和C结构体。用于对象时,对象释放后会产生野指针。
-
-
问题:
-
循环引用
- 问题:什么是循环引用?如何发现和解决?
-
回答要点:
- 定义:两个或多个对象相互强引用,导致都无法释放。
-
典型场景:
-
Delegate:应使用
weak声明。 - Block:Block会捕获外部变量,如果Block被对象强持有,而Block内部又强引用了该对象,就会形成循环。
-
Delegate:应使用
-
解决方案:在Block内部使用
__weak typeof(self) weakSelf = self;来打破循环。
🔄 Runtime机制
这是考察OC动态性的核心,也是区分候选人水平的关键。
-
消息传递与转发
-
问题:
[obj message]是如何被调用的? -
回答要点:这并非直接调用函数,而是编译器转成
objc_msgSend函数,进行动态绑定。过程如下:- 消息发送:通过对象的isa指针找到类,在类的方法缓存和方法列表中查找方法实现。
-
动态方法解析:如果没找到,Runtime会调用
+resolveInstanceMethod:等,给类一次机会动态添加方法。 -
消息转发:
-
备用接收者:Runtime调用
-forwardingTargetForSelector:,看看有没有其他对象能处理这个消息。 -
完整转发:如果以上都不行,最后会调用
-methodSignatureForSelector:和-forwardInvocation:,你可以在这里修改调用目标、参数等,或者直接吞掉消息。
-
备用接收者:Runtime调用
-
问题:
-
KVC与KVO
- 问题:KVO的底层实现原理是什么?
-
回答要点:基于Runtime。当你注册一个观察者时,Runtime会动态创建一个被观察对象类的子类,并重写被观察属性的setter方法。在重写的setter方法中,会自动在改变前后调用
-willChangeValueForKey:和-didChangeValueForKey:,从而触发观察者的回调。
-
Category
- 问题:Category的实现原理是什么?它和Extension有什么区别?
-
回答要点:
- 原理:Category在编译时,其方法、属性、协议等信息会被添加到类对象和元类对象中,运行时合并到类中,看起来就像是原类的一部分。
- 与Extension的区别:Extension在编译期决定,通常用于隐藏私有信息,可以添加实例变量;而Category在运行期决议,无法直接添加实例变量(但可以通过关联对象间接实现)。
📝 语法与核心特性
-
@property-
问题:
@property做了什么? -
回答要点:这是一个编译器指令,它会自动合成(synthesize)一个带下划线的实例变量、getter和setter方法。使用
@dynamic则是告诉编译器不自动合成,由开发者自己提供。
-
问题:
-
Protocol与Delegate
-
问题:代理为什么常用
weak修饰? -
回答要点:为了避免循环引用。通常,一个View会强引用它的Delegate(例如,一个ViewController),如果Delegate属性是
strong,那么ViewController强引用View,View又强引用ViewController,导致两者都无法释放。
-
问题:代理为什么常用
🧵 多线程与Block
-
Block
- 问题:Block有哪几种类型?在MRC和ARC下有什么区别?
-
回答要点:
-
类型:
__NSGlobalBlock__(全局块,不捕获变量)、__NSStackBlock__(栈块,MRC下捕获自动变量)、__NSMallocBlock__(堆块,被拷贝到堆上)。 -
内存管理:在ARC环境下,编译器会自动将捕获了外部变量的Block从栈拷贝到堆,以延长其生命周期。在MRC下需要手动调用
copy。
-
类型:
-
多线程与锁
- 问题:如何保证线程安全?
-
回答要点:当多个线程可能同时访问/修改同一块数据时,需要同步。常用方式有:
-
@synchronized:使用简单,但性能较低。 -
NSLock/NSRecursiveLock:递归锁,允许同一线程多次加锁。 -
dispatch_semaphore:信号量,性能高,更轻量。
-
💡 面试准备终极建议
- 理解原理而非背诵:面试官更希望听到你用自己的话解释机制背后的“为什么”,而不是死记硬背概念。
- 结合实战经验:准备1-2个具体案例,说明你是如何用这些知识解决实际问题的(例如,如何用Instrument排查循环引用,如何用Method Swizzling做无侵入埋点)。
- 展现技术视野:虽然考察的是OC,但可以适时提及Swift如何对应或改进了OC的某些特性(如Swift的闭包捕获列表、自动引用计数等),这会让你显得更有技术前瞻性。