深入理解Objective-C的核心特性(二)

好的,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结构体。用于对象时,对象释放后会产生野指针。
  • 循环引用

    • 问题:什么是循环引用?如何发现和解决?
    • 回答要点
      • 定义:两个或多个对象相互强引用,导致都无法释放。
      • 典型场景
        1. Delegate:应使用weak声明。
        2. Block:Block会捕获外部变量,如果Block被对象强持有,而Block内部又强引用了该对象,就会形成循环。
      • 解决方案:在Block内部使用 __weak typeof(self) weakSelf = self; 来打破循环。

🔄 Runtime机制

这是考察OC动态性的核心,也是区分候选人水平的关键。

  • 消息传递与转发

    • 问题[obj message]是如何被调用的?
    • 回答要点:这并非直接调用函数,而是编译器转成 objc_msgSend 函数,进行动态绑定。过程如下:
      1. 消息发送:通过对象的isa指针找到类,在类的方法缓存方法列表中查找方法实现。
      2. 动态方法解析:如果没找到,Runtime会调用 +resolveInstanceMethod: 等,给类一次机会动态添加方法。
      3. 消息转发
        • 备用接收者:Runtime调用 -forwardingTargetForSelector:,看看有没有其他对象能处理这个消息。
        • 完整转发:如果以上都不行,最后会调用 -methodSignatureForSelector:-forwardInvocation:,你可以在这里修改调用目标、参数等,或者直接吞掉消息。
  • 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. 结合实战经验:准备1-2个具体案例,说明你是如何用这些知识解决实际问题的(例如,如何用Instrument排查循环引用,如何用Method Swizzling做无侵入埋点)。
  3. 展现技术视野:虽然考察的是OC,但可以适时提及Swift如何对应或改进了OC的某些特性(如Swift的闭包捕获列表、自动引用计数等),这会让你显得更有技术前瞻性。
©著作权归作者所有,转载或内容合作请联系作者
【社区内容提示】社区部分内容疑似由AI辅助生成,浏览时请结合常识与多方信息审慎甄别。
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。

相关阅读更多精彩内容

友情链接更多精彩内容