深入 Objective-C 类的 runtime:动态添加属性与方法的实战案例

深入 Objective-C Runtime:动态添加属性与方法的实战案例

一、动态添加属性

在 Objective-C 中,通过关联对象(Associated Objects)实现属性的动态添加。核心原理是将键值对与对象关联,突破类扩展的限制。

实现步骤:

  1. 定义静态键值(确保唯一性)
  2. 实现 gettersetter 方法
  3. 使用 objc_setAssociatedObjectobjc_getAssociatedObject

示例代码:

#import <objc/runtime.h>

// 动态添加字符串属性
@interface NSObject (DynamicProperty)
@property (nonatomic, copy) NSString *dynamicIdentifier;
@end

@implementation NSObject (DynamicProperty)

static char kDynamicIdentifierKey;

- (void)setDynamicIdentifier:(NSString *)dynamicIdentifier {
    objc_setAssociatedObject(self, 
                             &kDynamicIdentifierKey, 
                             dynamicIdentifier, 
                             OBJC_ASSOCIATION_COPY_NONATOMIC);
}

- (NSString *)dynamicIdentifier {
    return objc_getAssociatedObject(self, &kDynamicIdentifierKey);
}

@end

// 使用案例
NSObject *obj = [NSObject new];
obj.dynamicIdentifier = @"ID_123";
NSLog(@"%@", obj.dynamicIdentifier); // 输出:ID_123

关联策略说明:

策略类型 等效属性声明
OBJC_ASSOCIATION_ASSIGN @property (assign)
OBJC_ASSOCIATION_RETAIN @property (strong)
OBJC_ASSOCIATION_COPY @property (copy)

二、动态添加方法

通过 class_addMethod 实现方法注入,需处理选择器(SEL)与函数指针(IMP)的映射关系。

实现步骤:

  1. 定义函数实现(需符合 id self, SEL _cmd 签名)
  2. 创建方法选择器
  3. 调用 class_addMethod 注入方法
  4. 处理消息转发(可选)

示例代码:

// 动态添加无参数方法
void dynamicMethodIMP(id self, SEL _cmd) {
    NSLog(@"动态方法执行成功!");
}

@interface MyClass : NSObject
@end

@implementation MyClass

// 处理未实现的方法
+ (BOOL)resolveInstanceMethod:(SEL)sel {
    if (sel == @selector(dynamicMethod)) {
        class_addMethod([self class], 
                        sel, 
                        (IMP)dynamicMethodIMP, 
                        "v@:");
        return YES;
    }
    return [super resolveInstanceMethod:sel];
}

@end

// 使用案例
MyClass *instance = [MyClass new];
[instance performSelector:@selector(dynamicMethod)]; // 输出:动态方法执行成功!

类型编码规则:

  • v@: 表示返回 void (v),参数为 id (@) 和 SEL (:)
  • i@: 表示返回 int (i),参数同上
  • 复杂类型需使用 @encode() 宏,如 @encode(CGPoint) 返回 "{CGPoint=dd}"

三、实战应用场景
  1. 分类扩展存储属性
    解决分类无法添加存储属性的限制(如为 UIImageView 添加缓存标识)

  2. 热修复与 AOP
    运行时替换方法实现(Method Swizzling)实现:

    Method original = class_getInstanceMethod([self class], @selector(viewDidLoad));
    Method swizzled = class_getInstanceMethod([self class], @selector(swizzled_viewDidLoad));
    method_exchangeImplementations(original, swizzled);
    

  3. 动态响应未实现方法
    forwardInvocation: 中动态创建方法,实现灵活的消息转发

  4. JSON 模型转换
    运行时遍历属性列表实现字典转模型:

    unsigned int count;
    objc_property_t *properties = class_copyPropertyList([self class], &count);
    for (int i = 0; i < count; i++) {
        const char *name = property_getName(properties[i]);
        NSString *key = [NSString stringWithUTF8String:name];
    }
    


四、注意事项
  1. 键值冲突
    使用 static char 地址作为键值,避免字符串重复

  2. 内存管理
    关联对象根据策略自动管理内存,但需注意循环引用:

    // 错误示例(导致循环引用)
    objc_setAssociatedObject(objA, &key, objB, OBJC_ASSOCIATION_RETAIN);
    objc_setAssociatedObject(objB, &key, objA, OBJC_ASSOCIATION_RETAIN);
    

  3. 性能影响
    频繁的动态方法解析会降低消息发送效率,建议在 +load 中预注册方法

  4. 类型安全
    动态方法需手动确保参数/返回值类型匹配,错误类型编码会导致崩溃

通过灵活运用 Runtime,可在保持类型安全的前提下实现高度动态化编程,但需严格遵循内存管理规则和线程安全原则。

转载请说明出处内容投诉
CSS教程网 » 深入 Objective-C 类的 runtime:动态添加属性与方法的实战案例

发表评论

欢迎 访客 发表评论

一个令你着迷的主题!

查看演示 官网购买