MST

星途 面试题库

面试题:Objective-C中属性是如何通过运行时机制实现的?

请简要阐述Objective-C运行时机制下,属性的存储、访问方法的生成等具体实现过程。
16.9万 热度难度
编程语言Objective-C

知识考点

AI 面试

面试题答案

一键面试

属性的存储

  1. 关联对象(Associated Objects)
    • 在Objective - C运行时,属性可以通过关联对象机制来存储值。这种方式允许为对象动态添加属性,即使类在编译时没有定义这些属性。
    • 运行时使用objc_setAssociatedObject函数来设置关联对象,objc_getAssociatedObject函数来获取关联对象。例如:
#import <objc/runtime.h>

@interface MyClass : NSObject
@end

@implementation MyClass
static char myPropertyKey;
- (void)setMyProperty:(id)value {
    objc_setAssociatedObject(self, &myPropertyKey, value, OBJC_ASSOCIATION_RETAIN_NONATOMIC);
}
- (id)myProperty {
    return objc_getAssociatedObject(self, &myPropertyKey);
}
@end
  1. 实例变量(Ivar)
    • 类定义中的属性通常会对应一个实例变量(Ivar),它是对象在内存中的一部分。编译器会自动为属性生成对应的实例变量,其命名规则通常是以_开头加上属性名。例如,对于属性name,会生成实例变量_name
    • 实例变量存储在对象的内存结构中,其内存布局由运行时根据类的定义确定。对象在创建时,会为实例变量分配内存空间。

访问方法的生成

  1. 自动合成(Auto - Synthesis)
    • 从Xcode 4.4开始,编译器默认会自动为属性合成访问方法(getter和setter)。当在类中声明一个属性,如@property (nonatomic, strong) NSString *name;,编译器会在后台自动生成name的getter方法- (NSString *)name和setter方法- (void)setName:(NSString *)name
    • 对于只读属性(@property (readonly)),只会生成getter方法。
  2. 自定义访问方法
    • 如果开发者手动实现了访问方法,编译器就不会自动合成。例如,如果手动实现了- (NSString *)name- (void)setName:(NSString *)name方法,那么运行时会使用开发者定义的这些方法来访问属性。
    • 在自定义访问方法中,可以实现更复杂的逻辑,比如在setter方法中进行数据验证,在getter方法中进行数据的延迟加载等。例如:
- (void)setName:(NSString *)name {
    if (name.length > 0) {
        _name = name;
    }
}
- (NSString *)name {
    if (!_name) {
        _name = @"Default Name";
    }
    return _name;
}
  1. 方法查找与消息转发
    • 当通过点语法或消息发送方式访问属性(如object.name[object name])时,运行时首先会在类的方法列表中查找对应的访问方法。
    • 如果没有找到,会进行动态方法解析,尝试动态添加方法。若动态方法解析失败,会进入备用接收者阶段,看是否有其他对象可以处理该消息。若仍失败,最终会进行完整的消息转发流程,包括forwardInvocation:methodSignatureForSelector:等方法的调用,以处理未识别的方法调用。