面试题答案
一键面试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机制将无法正常工作。 - 性能:自定义存取方法可能会影响性能。例如,过度复杂的自定义存取方法逻辑可能会增加方法调用的开销,影响程序的执行效率。在优化性能时,需要仔细考虑自定义存取方法的实现。