MST

星途 面试题库

面试题:Objective-C属性的实现原理是什么,如何自定义属性的存取方法?

深入剖析Objective-C属性在底层是如何实现的,包括runtime相关知识。并且阐述如何手动自定义属性的setter和getter方法,自定义时需要注意哪些与默认实现不同的地方,以及可能带来的影响。
21.8万 热度难度
编程语言Objective-C

知识考点

AI 面试

面试题答案

一键面试

1. Objective - C属性在底层的实现

在Objective - C中,属性(property)本质上是一种编译器特性。编译器会根据属性的声明自动生成存取方法(accessor methods,即getter和setter方法)、实例变量(ivar)以及相关的内存管理代码。

  • 实例变量(ivar):属性会对应一个实例变量来存储值。实例变量的命名默认是下划线 + 属性名,例如声明 @property (nonatomic, strong) NSString *name;,会有一个 _name 的实例变量。在类的结构体 objc_class 中有一个 ivar_list_t 用于存储所有的实例变量信息,实例变量的布局在对象的内存布局中占有一定的位置。
  • 存取方法(accessor methods):编译器会自动生成getter和setter方法。例如对于 @property (nonatomic, strong) NSString *name;,会生成 - (NSString *)name 作为getter方法,- (void)setName:(NSString *)name 作为setter方法。这些方法的实现通过runtime来操作实例变量。在runtime中,方法是以 objc_method 结构体的形式存储在类的 method_list_t 中。当调用对象的存取方法时,runtime通过消息发送机制(objc_msgSend)来查找并执行对应的方法。

2. 手动自定义属性的setter和getter方法

  • 自定义setter方法:例如对于 @property (nonatomic, strong) NSString *name;,自定义setter方法如下:
- (void)setName:(NSString *)name {
    if (_name != name) {
        [_name release];
        _name = [name retain];
    }
}
  • 自定义getter方法
- (NSString *)name {
    return _name;
}

3. 自定义时与默认实现不同的地方及可能带来的影响

  • 内存管理:默认的setter方法对于 strong 属性会自动进行引用计数的管理(retain 旧值,release 新值)。在自定义setter中,需要手动处理内存管理,如果处理不当,可能会导致内存泄漏或悬空指针。例如,忘记 release 旧值会导致内存泄漏,而错误地 release 新值后又使用它会导致悬空指针。
  • 线程安全:默认的 nonatomic 属性不保证线程安全,而 atomic 属性会使用锁来保证线程安全。如果自定义存取方法,需要自行考虑线程安全问题。例如,在多线程环境下访问共享资源(实例变量)时,如果不做同步处理,可能会出现数据竞争问题。
  • KVO(Key - Value Observing):默认的存取方法会自动支持KVO。如果自定义存取方法,需要手动调用 willChangeValueForKey:didChangeValueForKey: 方法来通知观察者属性值的变化,否则KVO机制将无法正常工作。
  • 性能:自定义存取方法可能会影响性能。例如,过度复杂的自定义存取方法逻辑可能会增加方法调用的开销,影响程序的执行效率。在优化性能时,需要仔细考虑自定义存取方法的实现。