探索Objective-C底层 - Runtime的相关应用
引言
Objective - C 的 Runtime 是一个强大且灵活的运行时系统,它为开发者提供了许多在编译时无法实现的动态特性。在之前的文章中,我们已经深入探讨了 Runtime 的底层原理,包括 isa
指针、class
结构、objc_msgSend
机制等。本文将重点介绍 Runtime 在实际开发中的一些常见应用,并通过具体的例子详细说明。
一、核心Runtime API详解
1. 类与对象操作
1 2 3 4 5 6 7 8 9 10 11
| // 获取类的父类 Class class_getSuperclass(Class cls);
// 获取类的实例方法 Method class_getInstanceMethod(Class cls, SEL name);
// 获取类的所有方法 Method *class_copyMethodList(Class cls, unsigned int *outCount);
// 创建类的实例 id class_createInstance(Class cls, size_t extraBytes);
|
2. 方法操作
1 2 3 4 5 6 7 8
| // 交换两个方法的实现 void method_exchangeImplementations(Method m1, Method m2);
// 添加新方法 BOOL class_addMethod(Class cls, SEL name, IMP imp, const char *types);
// 获取方法实现 IMP class_getMethodImplementation(Class cls, SEL name);
|
3. 关联对象
1 2 3 4 5
| // 设置关联对象 void objc_setAssociatedObject(id object, const void *key, id value, objc_AssociationPolicy policy);
// 获取关联对象 id objc_getAssociatedObject(id object, const void *key);
|
4. 属性与协议
1 2 3 4 5
| // 获取属性列表 objc_property_t *class_copyPropertyList(Class cls, unsigned int *outCount);
// 获取协议列表 Protocol *class_copyProtocolList(Class cls, unsigned int *outCount);
|
二、15种典型应用场景
1. 方法交换(Method Swizzling)
应用场景:统一日志记录、埋点统计
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
| @implementation UIViewController (Logging) + (void)load { static dispatch_once_t onceToken; dispatch_once(&onceToken, ^{ Method original = class_getInstanceMethod(self, @selector(viewDidLoad)); Method swizzled = class_getInstanceMethod(self, @selector(swizzled_viewDidLoad)); method_exchangeImplementations(original, swizzled); }); }
- (void)swizzled_viewDidLoad { [self swizzled_viewDidLoad]; NSLog(@"ViewController %@ loaded", NSStringFromClass([self class])); } @end
|
2. 动态添加方法
应用场景:懒加载功能模块
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
| @interface DynamicLoader : NSObject @end
@implementation DynamicLoader void dynamicMethod(id self, SEL _cmd) { NSLog(@"Dynamic method called"); }
+ (BOOL)resolveInstanceMethod:(SEL)sel { if (sel == @selector(lazyMethod)) { class_addMethod(self, sel, (IMP)dynamicMethod, "v@:"); return YES; } return NO; } @end
|
3. 关联对象扩展类
应用场景:为系统类添加属性
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
| @interface UIImage (Watermark) @property (nonatomic, strong) UIImage *watermarkedImage; @end
@implementation UIImage (Watermark) static char kWatermarkKey;
- (void)setWatermarkedImage:(UIImage *)image { objc_setAssociatedObject(self, &kWatermarkKey, image, OBJC_ASSOCIATION_RETAIN_NONATOMIC); }
- (UIImage *)watermarkedImage { return objc_getAssociatedObject(self, &kWatermarkKey); } @end
|
4. 字典转模型
应用场景:JSON数据解析
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20
| @interface Person : NSObject @property (nonatomic, copy) NSString *name; @property (nonatomic, assign) NSInteger age; @end
@implementation Person - (instancetype)initWithDictionary:(NSDictionary *)dict { self = [super init]; unsigned int count; objc_property_t *props = class_copyPropertyList([self class], &count); for (int i = 0; i < count; i++) { NSString *key = [NSString stringWithUTF8String:property_getName(props[i])]; if (dict[key]) { [self setValue:dict[key] forKey:key]; } } free(props); return self; } @end
|
5. AOP编程(面向切面编程)
应用场景:统一异常处理
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
| @implementation NSObject (ExceptionHandler) + (void)load { Method original = class_getInstanceMethod(self, @selector(forwardInvocation:)); Method swizzled = class_getInstanceMethod(self, @selector(swizzled_forwardInvocation:)); method_exchangeImplementations(original, swizzled); }
- (void)swizzled_forwardInvocation:(NSInvocation *)invocation { @try { [self swizzled_forwardInvocation:invocation]; } @catch (NSException *exception) { NSLog(@"Caught exception: %@", exception); } } @end
|
6. KVO实现
应用场景:属性变更监听
1 2 3 4 5 6 7 8 9 10 11
| @interface Observable : NSObject @property (nonatomic, strong) NSString *value; @end
@implementation Observable - (void)setValue:(NSString *)value { [self willChangeValueForKey:@"value"]; _value = value; [self didChangeValueForKey:@"value"]; } @end
|
7. 动态代理
应用场景:实现轻量级代理模式
1 2 3 4 5 6 7 8 9
| @interface DynamicProxy : NSObject @property (nonatomic, weak) id target; @end
@implementation DynamicProxy - (id)forwardingTargetForSelector:(SEL)aSelector { return self.target; } @end
|
8. 方法拦截
应用场景:实现方法调用监控
1 2 3 4 5 6 7 8 9 10 11 12
| @implementation NSObject (MethodInterceptor) + (void)load { Method original = class_getInstanceMethod(self, @selector(performSelector:)); Method swizzled = class_getInstanceMethod(self, @selector(intercepted_performSelector:)); method_exchangeImplementations(original, swizzled); }
- (id)intercepted_performSelector:(SEL)aSelector { NSLog(@"Intercepted selector: %@", NSStringFromSelector(aSelector)); return [self intercepted_performSelector:aSelector]; } @end
|
9. 实现单例
应用场景:线程安全的单例模式
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19
| @interface Singleton : NSObject + (instancetype)sharedInstance; @end
@implementation Singleton static id _instance;
+ (instancetype)allocWithZone:(struct _NSZone *)zone { static dispatch_once_t onceToken; dispatch_once(&onceToken, ^{ _instance = [super allocWithZone:zone]; }); return _instance; }
+ (instancetype)sharedInstance { return [[self alloc] init]; } @end
|
10. 热修复
应用场景:紧急修复线上bug
1 2 3 4 5 6 7 8 9 10 11 12 13 14
| @interface BuggyClass : NSObject - (void)buggyMethod; @end
@implementation BuggyClass void fixedMethod(id self, SEL _cmd) { NSLog(@"Fixed implementation"); }
+ (void)load { Method buggy = class_getInstanceMethod(self, @selector(buggyMethod)); class_replaceMethod(self, buggy, (IMP)fixedMethod, "v@:"); } @end
|
11. 统计方法调用次数
应用场景:性能分析
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19
| @implementation NSObject (MethodCounter) static char kInvocationCountKey;
- (void)incrementInvocationCount { NSNumber *count = objc_getAssociatedObject(self, &kInvocationCountKey); objc_setAssociatedObject(self, &kInvocationCountKey, @(count.integerValue + 1), OBJC_ASSOCIATION_RETAIN_NONATOMIC); }
+ (void)load { Method original = class_getInstanceMethod(self, @selector(performSelector:)); Method swizzled = class_getInstanceMethod(self, @selector(counted_performSelector:)); method_exchangeImplementations(original, swizzled); }
- (id)counted_performSelector:(SEL)aSelector { [self incrementInvocationCount]; return [self counted_performSelector:aSelector]; } @end
|
12. 实现协议
应用场景:动态遵循协议
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
| @interface DynamicAdopter : NSObject @end
@implementation DynamicAdopter + (BOOL)conformsToProtocol:(Protocol *)aProtocol { return YES; }
- (id)forwardingTargetForSelector:(SEL)aSelector { if (Protocol_getMethodDescription(aProtocol, aSelector, YES, YES)) { return self; } return [super forwardingTargetForSelector:aSelector]; } @end
|
13. 实现链式调用
应用场景:构建DSL
1 2 3 4 5 6 7 8 9 10 11 12
| @interface Chainable : NSObject - (Chainable *(^)(NSString *))setName; @end
@implementation Chainable - (Chainable *(^)(NSString *))setName { return ^(NSString *name) { [self setValue:name forKey:@"name"]; return self; }; } @end
|
14. 实现动态容器
应用场景:动态存储键值对
1 2 3 4 5 6 7 8 9 10 11 12 13 14
| @interface DynamicContainer : NSObject - (void)setValue:(id)value forKey:(NSString *)key; - (id)valueForKey:(NSString *)key; @end
@implementation DynamicContainer - (void)setValue:(id)value forKey:(NSString *)key { objc_setAssociatedObject(self, (__bridge const void *)(key), value, OBJC_ASSOCIATION_RETAIN_NONATOMIC); }
- (id)valueForKey:(NSString *)key { return objc_getAssociatedObject(self, (__bridge const void *)(key)); } @end
|
15. 实现自定义KVC
应用场景:扩展KVC功能
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19
| @interface CustomKVC : NSObject - (void)customSetValue:(id)value forKey:(NSString *)key; @end
@implementation CustomKVC - (void)customSetValue:(id)value forKey:(NSString *)key { unsigned int count; objc_property_t *props = class_copyPropertyList([self class], &count); for (int i = 0; i < count; i++) { if ([NSStringFromCString(property_getName(props[i])) isEqualToString:key]) { [self setValue:value forKey:key]; free(props); return; } } free(props); [self doesNotRecognizeSelector:_cmd]; } @end
|
三、注意事项
- 性能开销:频繁使用Runtime API可能影响性能
- 线程安全:方法交换等操作需加锁保护
- 版本兼容:不同iOS版本的Runtime实现可能有差异
- 内存管理:关联对象需合理选择内存策略
- 方法冲突:避免方法名冲突导致不可预期行为
四、总结
Objective - C Runtime通过开放底层API,赋予开发者强大的动态编程能力。掌握这些API的使用方法和应用场景,能够显著提升代码的灵活性和扩展性。建议在实际开发中结合具体需求,合理运用Runtime技术,并注意性能与维护性的平衡。
参考资料: