iOS Runtime 详解 - 面试必备
一、Runtime 基本概念
Objective-C Runtime 是Objective-C语言的运行时系统,它让Objective-C成为一门真正动态的语言。
核心特性:
- 动态特性:运行时决定方法调用、类创建
- 消息机制:所有方法调用都是消息发送
- 反射能力:可以检查和修改类的结构
- 动态绑定:方法实现在运行时确定
二、Runtime 核心机制
1. 消息发送机制详解
核心原理: Objective-C中所有方法调用都是消息发送,不是函数调用。
// 编译时转换
[receiver message] → objc_msgSend(receiver, @selector(message))
[receiver message:arg] → objc_msgSend(receiver, @selector(message:), arg)
消息查找流程:
- 方法缓存查找 - 先在缓存中查找方法实现
- 类的方法列表查找 - 在当前类的方法列表中查找
- 继承链查找 - 沿着superclass指针向上查找父类
- 消息转发 - 如果找不到,进入消息转发流程
2. 消息转发机制 (Message Forwarding)
消息转发是Runtime最强大的特性之一,当对象无法响应某个消息时,Runtime不会直接crash,而是给对象一次"补救"的机会。
完整的消息转发流程分为三个阶段:
第一阶段:动态方法解析 (Dynamic Method Resolution)
// 当对象无法找到方法时,首先调用
+ (BOOL)resolveInstanceMethod:(SEL)sel {
// 可以在这里动态添加方法
if (sel == @selector(dynamicMethod)) {
class_addMethod(self, sel, (IMP)dynamicMethodIMP, "v@:");
return YES;
}
return [super resolveInstanceMethod:sel];
}
第二阶段:快速消息转发 (Fast Forwarding)
// 尝试将消息转发给其他对象处理
- (id)forwardingTargetForSelector:(SEL)aSelector {
if (aSelector == @selector(someMethod)) {
// 将消息转发给helper对象处理
return self.helper;
}
return [super forwardingTargetForSelector:aSelector];
}
第三阶段:完整消息转发 (Full Forwarding)
// 如果前两个阶段都无法处理,则进行完整转发
- (NSMethodSignature *)methodSignatureForSelector:(SEL)aSelector {
// 返回方法签名,让Runtime知道如何构造NSInvocation
return [NSMethodSignature signatureWithObjcTypes:"v@:"];
}
- (void)forwardInvocation:(NSInvocation *)anInvocation {
// 可以在此阶段做任何处理:日志、修改参数、转发给其他对象等
SEL selector = anInvocation.selector;
NSLog(@"对象 %@ 无法响应方法 %@", self, NSStringFromSelector(selector));
// 可以转发给其他对象
if ([self.backupObject respondsToSelector:selector]) {
[anInvocation invokeWithTarget:self.backupObject];
}
}
3. 类结构详解
struct objc_class {
Class isa; // 指向元类(meta-class),存储类方法
Class superclass; // 父类
const char *name; // 类名
long version; // 版本信息
long info; // 类信息标志位
long instance_size; // 实例对象大小
struct objc_ivar_list *ivars; // 成员变量列表
struct objc_method_list **methodLists; // 方法列表
struct objc_cache *cache; // 方法缓存
// ... 其他字段
};
关键点:
- isa指针:连接对象和类的桥梁
- superclass:形成继承链的基础
- cache:提升方法查找性能的重要缓存机制
三、常用 Runtime API
1. 类操作
// 获取类
Class cls = NSClassFromString(@"UIViewController");
Class cls = [UIViewController class];
// 创建类
Class newClass = objc_allocateClassPair(superClass, "MyClass", 0);
objc_registerClassPair(newClass);
// 类信息查询
const char *className = class_getName(cls);
Class superClass = class_getSuperclass(cls);
BOOL isMeta = class_isMetaClass(cls);
2. 方法操作
// 获取方法
Method method = class_getInstanceMethod(cls, @selector(viewDidLoad));
// 方法信息
SEL selector = method_getName(method);
const char *typeEncoding = method_getTypeEncoding(method);
IMP imp = method_getImplementation(method);
// 替换实现
IMP originalImp = class_replaceMethod(cls, selector, newImp, typeEncoding);
// 方法交换
method_exchangeImplementations(method1, method2);
3. 成员变量操作
// 获取成员变量
Ivar ivar = class_getInstanceVariable(cls, "_name");
// 变量信息
const char *name = ivar_getName(ivar);
const char *type = ivar_getTypeEncoding(ivar);
// 运行时访问私有变量
id value = object_getIvar(obj, ivar);
object_setIvar(obj, ivar, value);
4. 属性操作
// 获取属性
objc_property_t property = class_getProperty(cls, "name");
// 属性信息
const char *name = property_getName(property);
const char *attributes = property_getAttributes(property);
四、项目中常用应用举例
1. 方法交换 (Method Swizzling)
场景: 全局修改系统方法行为,如埋点统计、日志记录
@implementation UIViewController (Tracking)
+ (void)load {
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
Class class = [self class];
SEL originalSelector = @selector(viewDidLoad);
SEL swizzledSelector = @selector(tracking_viewDidLoad);
Method originalMethod = class_getInstanceMethod(class, originalSelector);
Method swizzledMethod = class_getInstanceMethod(class, swizzledSelector);
BOOL didAddMethod = class_addMethod(class, originalSelector,
method_getImplementation(swizzledMethod),
method_getTypeEncoding(swizzledMethod));
if (didAddMethod) {
class_replaceMethod(class, swizzledSelector,
method_getImplementation(originalMethod),
method_getTypeEncoding(originalMethod));
} else {
method_exchangeImplementations(originalMethod, swizzledMethod);
}
});
}
- (void)tracking_viewDidLoad {
[self tracking_viewDidLoad]; // 调用原始方法
// 添加埋点代码
[TrackingManager trackPageView:NSStringFromClass([self class])];
}
@end
2. 动态添加属性
场景: 为现有类添加属性存储,如Category中添加属性
#import <objc/runtime.h>
static void *kPropertyKey = &kPropertyKey;
@implementation NSObject (AssociatedObject)
- (void)setAssociatedObject:(id)object {
objc_setAssociatedObject(self, kPropertyKey, object, OBJC_ASSOCIATION_RETAIN_NONATOMIC);
}
- (id)associatedObject {
return objc_getAssociatedObject(self, kPropertyKey);
}
@end
3. 字典转模型 (Runtime实现)
场景: JSON数据自动转换为Model对象
@implementation NSObject (Model)
+ (instancetype)modelWithDict:(NSDictionary *)dict {
id model = [[self alloc] init];
unsigned int count;
Ivar *ivars = class_copyIvarList(self, &count);
for (int i = 0; i < count; i++) {
Ivar ivar = ivars[i];
NSString *ivarName = [NSString stringWithUTF8String:ivar_getName(ivar)];
NSString *key = [ivarName substringFromIndex:1]; // 去掉下划线
id value = dict[key];
if (value) {
[model setValue:value forKey:key];
}
}
free(ivars);
return model;
}
@end
// 使用
UserModel *user = [UserModel modelWithDict:@{@"name": @"张三", @"age": @25}];
4. 组件化中间件 (CTMediator原理)
场景: 模块间解耦调用
@implementation CTMediator
- (id)performTarget:(NSString *)targetName
action:(NSString *)actionName
params:(NSDictionary *)params {
// 构造Target类名
NSString *targetClassString = [NSString stringWithFormat:@"Target_%@", targetName];
NSString *actionString = [NSString stringWithFormat:@"Action_%@:", actionName];
Class targetClass = NSClassFromString(targetClassString);
id target = [[targetClass alloc] init];
SEL action = NSSelectorFromString(actionString);
if (target && [target respondsToSelector:action]) {
return [self safePerformAction:action target:target params:params];
}
return nil;
}
@end
// Target实现
@implementation Target_User
- (UIViewController *)Action_detail:(NSDictionary *)params {
UserDetailViewController *vc = [[UserDetailViewController alloc] init];
vc.userId = params[@"userId"];
return vc;
}
@end
// 调用
UIViewController *vc = [[CTMediator sharedInstance] performTarget:@"User"
action:@"detail"
params:@{@"userId": @"123"}];
5. 消息转发实现多重继承效果
场景: 一个类"继承"多个类的行为,实现组合优于继承的设计模式
@interface MultiInheritanceObject : NSObject
@property (nonatomic, strong) id<ProtocolA> helperA; // 支付能力
@property (nonatomic, strong) id<ProtocolB> helperB; // 物流能力
@end
@implementation MultiInheritanceObject
// 快速转发:优先选择转发目标
- (id)forwardingTargetForSelector:(SEL)aSelector {
if ([self.helperA respondsToSelector:aSelector]) {
return self.helperA;
}
if ([self.helperB respondsToSelector:aSelector]) {
return self.helperB;
}
return [super forwardingTargetForSelector:aSelector];
}
// 如果快速转发无法处理,返回方法签名
- (NSMethodSignature *)methodSignatureForSelector:(SEL)aSelector {
NSMethodSignature *signature = [self.helperA methodSignatureForSelector:aSelector];
if (!signature) {
signature = [self.helperB methodSignatureForSelector:aSelector];
}
if (!signature) {
// 如果都找不到,返回一个默认签名避免crash
signature = [NSMethodSignature signatureWithObjcTypes:"v@:"];
}
return signature;
}
// 完整转发:可以做更复杂的处理
- (void)forwardInvocation:(NSInvocation *)anInvocation {
SEL selector = anInvocation.selector;
if ([self.helperA respondsToSelector:selector]) {
// 记录调用日志
NSLog(@"调用支付相关方法: %@", NSStringFromSelector(selector));
[anInvocation invokeWithTarget:self.helperA];
}
else if ([self.helperB respondsToSelector:selector]) {
// 记录调用日志
NSLog(@"调用物流相关方法: %@", NSStringFromSelector(selector));
[anInvocation invokeWithTarget:self.helperB];
}
else {
// 可以在这里实现容错处理
NSLog(@"未找到方法实现: %@", NSStringFromSelector(selector));
[self handleUnimplementedMethod:anInvocation];
}
}
- (void)handleUnimplementedMethod:(NSInvocation *)invocation {
// 容错处理:比如发送通知、记录错误等
[[NSNotificationCenter defaultCenter] postNotificationName:@"MethodNotFound"
object:self
userInfo:@{@"selector": NSStringFromSelector(invocation.selector)}];
}
@end
七、Runtime 在电商项目中的实际应用
1. 电商App架构设计中的Runtime应用
案例1:支付模块的动态扩展
// 支付策略管理器
@interface PaymentManager : NSObject
@property (nonatomic, strong) NSMutableDictionary<NSString *, Class> *paymentStrategies;
@end
@implementation PaymentManager
+ (instancetype)sharedManager {
static PaymentManager *manager = nil;
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
manager = [[self alloc] init];
manager.paymentStrategies = [NSMutableDictionary dictionary];
});
return manager;
}
// 动态注册支付方式
- (void)registerPaymentStrategy:(Class)strategyClass forType:(NSString *)type {
self.paymentStrategies[type] = strategyClass;
}
// 动态创建支付对象
- (id<PaymentProtocol>)createPaymentWithType:(NSString *)type {
Class strategyClass = self.paymentStrategies[type];
if (strategyClass) {
return [[strategyClass alloc] init];
}
return nil;
}
@end
// 使用消息转发实现支付方式的动态调用
@implementation OrderService
- (void)processPayment:(NSString *)paymentType order:(Order *)order {
id<PaymentProtocol> payment = [[PaymentManager sharedManager] createPaymentWithType:paymentType];
if (payment) {
// 直接调用支付方法,如果支付对象不支持,会通过消息转发处理
[payment payWithOrder:order completion:^(BOOL success, NSError *error) { if (success) { [self updateOrderStatus:order status:OrderStatusPaid];
} else {
[self handlePaymentFailure:error];
}
}];
} else {
[self handleUnsupportedPaymentType:paymentType];
}
}
@end
案例2:商品详情页的组件化架构
// 商品组件管理器 - 使用Runtime实现组件的动态加载
@interface ProductComponentManager : NSObject
- (UIView *)createComponentWithType:(NSString *)type config:(NSDictionary *)config;
@end
@implementation ProductComponentManager
- (UIView *)createComponentWithType:(NSString *)type config:(NSDictionary *)config {
// 动态构造组件类名
NSString *className = [NSString stringWithFormat:@"Product%@Component", type];
Class componentClass = NSClassFromString(className);
if (!componentClass) {
// 如果类不存在,使用Runtime动态创建
componentClass = [self createDynamicComponentClassWithType:type];
}
id component = [[componentClass alloc] init];
[component configureWithDictionary:config];
return component;
}
- (Class)createDynamicComponentClassWithType:(NSString *)type {
NSString *className = [NSString stringWithFormat:@"Product%@Component", type];
Class superClass = [UIView class];
// 动态创建类
Class dynamicClass = objc_allocateClassPair(superClass, className.UTF8String, 0);
// 添加初始化方法
SEL initSelector = @selector(initWithFrame:);
Method initMethod = class_getInstanceMethod(superClass, initSelector);
const char *initTypes = method_getTypeEncoding(initMethod);
class_addMethod(dynamicClass, initSelector, (IMP)dynamicInitWithFrame, initTypes);
// 添加配置方法
class_addMethod(dynamicClass, @selector(configureWithDictionary:),
(IMP)configureWithDictionaryIMP, "v@:@");
objc_registerClassPair(dynamicClass);
return dynamicClass;
}
@end
案例3:电商数据埋点系统的实现
// 全局埋点管理器
@interface TrackingManager : NSObject
+ (void)trackEvent:(NSString *)eventId parameters:(NSDictionary *)params;
@end
@implementation TrackingManager
+ (void)trackEvent:(NSString *)eventId parameters:(NSDictionary *)params {
// 获取所有注册的埋点服务
NSArray *trackingServices = [self registeredTrackingServices];
for (id<TrackingServiceProtocol> service in trackingServices) {
// 使用消息转发实现服务的动态调用
// 如果某个服务不支持某个埋点方法,会通过forwardInvocation处理
[service trackEvent:eventId withParameters:params];
}
}
@end
// UIViewController的埋点扩展
@implementation UIViewController (AutoTracking)
+ (void)load {
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
// 方法交换实现自动埋点
Method originalMethod = class_getInstanceMethod(self, @selector(viewDidAppear:));
Method swizzledMethod = class_getInstanceMethod(self, @selector(tracking_viewDidAppear:));
method_exchangeImplementations(originalMethod, swizzledMethod);
});
}
- (void)tracking_viewDidAppear:(BOOL)animated {
[self tracking_viewDidAppear:animated];
// 自动上报页面曝光埋点
NSString *pageName = NSStringFromClass([self class]);
[[TrackingManager sharedManager] trackPageView:pageName];
}
@end
八、Runtime 在即时通讯项目中的实际应用
1. IM消息处理系统的设计
案例1:消息类型的动态扩展
// 消息类型注册中心
@interface MessageTypeRegistry : NSObject
+ (void)registerMessageClass:(Class)messageClass forType:(NSString *)type;
+ (Class)messageClassForType:(NSString *)type;
@end
@implementation MessageTypeRegistry
static NSMutableDictionary *messageTypeMap = nil;
+ (void)initialize {
messageTypeMap = [NSMutableDictionary dictionary];
}
+ (void)registerMessageClass:(Class)messageClass forType:(NSString *)type {
messageTypeMap[type] = messageClass;
}
+ (Class)messageClassForType:(NSString *)type {
return messageTypeMap[type];
}
@end
// 消息工厂
@implementation MessageFactory
+ (id<MessageProtocol>)createMessageWithType:(NSString *)type data:(NSDictionary *)data {
Class messageClass = [MessageTypeRegistry messageClassForType:type];
if (!messageClass) {
// 如果找不到对应的消息类,使用Runtime动态创建
messageClass = [self createDynamicMessageClassForType:type];
}
id<MessageProtocol> message = [[messageClass alloc] init];
[message parseFromDictionary:data];
return message;
}
+ (Class)createDynamicMessageClassForType:(NSString *)type {
NSString *className = [NSString stringWithFormat:@"%@Message", type];
Class superClass = [NSObject class];
Class dynamicClass = objc_allocateClassPair(superClass, className.UTF8String, 0);
// 添加协议遵循
class_addProtocol(dynamicClass, @protocol(MessageProtocol));
// 添加基础方法
class_addMethod(dynamicClass, @selector(parseFromDictionary:),
(IMP)parseFromDictionaryIMP, "v@:@");
class_addMethod(dynamicClass, @selector(toDictionary),
(IMP)toDictionaryIMP, "@@:");
objc_registerClassPair(dynamicClass);
return dynamicClass;
}
@end
案例2:消息转发机制在IM中的应用
// 聊天消息处理器
@interface MessageHandler : NSObject
@property (nonatomic, strong) NSMutableDictionary<NSString *, id<MessageHandlerProtocol>> *handlers;
@end
@implementation MessageHandler
- (instancetype)init {
self = [super init];
if (self) {
self.handlers = [NSMutableDictionary dictionary];
}
return self;
}
// 注册消息处理器
- (void)registerHandler:(id<MessageHandlerProtocol>)handler forMessageType:(NSString *)type {
self.handlers[type] = handler;
}
// 处理消息 - 使用消息转发实现
- (void)handleMessage:(Message *)message {
id<MessageHandlerProtocol> handler = self.handlers[message.type];
if (handler) {
// 直接调用处理器方法
[handler processMessage:message];
} else {
// 如果没有注册处理器,通过消息转发处理
[self forwardMessage:message];
}
}
// 消息转发实现
- (id)forwardingTargetForSelector:(SEL)aSelector {
// 可以转发给默认处理器
return self.defaultHandler;
}
- (NSMethodSignature *)methodSignatureForSelector:(SEL)aSelector {
// 返回一个通用的方法签名
return [NSMethodSignature signatureWithObjcTypes:"v@:@"];
}
- (void)forwardInvocation:(NSInvocation *)anInvocation {
Message *message = nil;
[anInvocation getArgument:&message atIndex:2];
// 记录未处理的消息类型
NSLog(@"未找到消息处理器: %@", message.type);
// 可以存储到待处理队列,或者发送给服务器处理
[self queueUnhandledMessage:message];
}
@end
案例3:IM协议的动态适配
// 协议适配器 - 使用Runtime实现不同版本协议的兼容
@interface ProtocolAdapter : NSObject
- (id)adaptObject:(id)object toProtocol:(Protocol *)protocol;
@end
@implementation ProtocolAdapter
- (id)adaptObject:(id)object toProtocol:(Protocol *)protocol {
// 检查对象是否已经遵循协议
if ([object conformsToProtocol:protocol]) {
return object;
}
// 如果不符合,使用Runtime创建适配器
return [self createAdapterForObject:object protocol:protocol];
}
- (id)createAdapterForObject:(id)object protocol:(Protocol *)protocol {
NSString *adapterClassName = [NSString stringWithFormat:@"%@Adapter", NSStringFromProtocol(protocol)];
Class adapterClass = NSClassFromString(adapterClassName);
if (!adapterClass) {
adapterClass = [self createDynamicAdapterClassForProtocol:protocol];
}
id adapter = [[adapterClass alloc] init];
[adapter setOriginalObject:object];
return adapter;
}
- (Class)createDynamicAdapterClassForProtocol:(Protocol *)protocol {
NSString *className = [NSString stringWithFormat:@"%@Adapter", NSStringFromProtocol(protocol)];
Class superClass = [NSObject class];
Class adapterClass = objc_allocateClassPair(superClass, className.UTF8String, 0);
// 添加协议遵循
class_addProtocol(adapterClass, protocol);
// 获取协议中的所有方法
unsigned int methodCount;
struct objc_method_description *methods = protocol_copyMethodDescriptionList(protocol, NO, YES, &methodCount);
for (unsigned int i = 0; i < methodCount; i++) {
struct objc_method_description methodDesc = methods[i];
class_addMethod(adapterClass, methodDesc.name, (IMP)genericForwardingIMP, methodDesc.types);
}
free(methods);
objc_registerClassPair(adapterClass);
return adapterClass;
}
@end
案例4:IM连接状态管理
// 连接状态管理器
@interface ConnectionManager : NSObject
@property (nonatomic, assign) ConnectionState state;
@property (nonatomic, strong) NSMutableArray<id<ConnectionDelegate>> *delegates;
@end
@implementation ConnectionManager
- (void)addDelegate:(id<ConnectionDelegate>)delegate {
[self.delegates addObject:delegate];
}
- (void)notifyConnectionStateChanged:(ConnectionState)newState {
self.state = newState;
// 通知所有代理 - 使用消息转发实现安全的调用
for (id<ConnectionDelegate> delegate in self.delegates) {
if ([delegate respondsToSelector:@selector(connectionStateDidChange:)]) {
[delegate connectionStateDidChange:newState];
} else {
// 如果代理不响应,通过消息转发处理
[self safelyNotifyDelegate:delegate selector:@selector(connectionStateDidChange:) withObject:@(newState)];
}
}
}
- (void)safelyNotifyDelegate:(id)delegate selector:(SEL)selector withObject:(id)object {
NSMethodSignature *signature = [delegate methodSignatureForSelector:selector];
if (signature) {
NSInvocation *invocation = [NSInvocation invocationWithMethodSignature:signature];
invocation.selector = selector;
invocation.target = delegate;
if (signature.numberOfArguments > 2) {
[invocation setArgument:&object atIndex:2];
}
[invocation invoke];
}
}
@end
6. 动态创建类
场景: 根据配置动态创建类,如UI组件工厂
Class ComponentFactory::createComponentClass(NSString *componentType, NSDictionary *config) {
NSString *className = [NSString stringWithFormat:@"Dynamic%@", componentType];
Class superClass = [UIView class];
Class dynamicClass = objc_allocateClassPair(superClass, className.UTF8String, 0);
// 添加属性
class_addIvar(dynamicClass, "_config", sizeof(id), log2(sizeof(id)), @encode(id));
// 添加方法
class_addMethod(dynamicClass, @selector(initWithConfig:),
(IMP)initWithConfigIMP, "@@:@");
objc_registerClassPair(dynamicClass);
return dynamicClass;
}
九、Runtime 性能与注意事项
1. 性能优化策略
消息发送性能:
- 方法缓存:Runtime维护方法缓存,大幅提升重复调用的性能
- 内联缓存:现代Runtime使用更高效的缓存机制
- 消息转发开销:转发链越长,性能影响越大,应尽量避免
最佳实践:
// ✅ 推荐:缓存方法签名
- (NSMethodSignature *)methodSignatureForSelector:(SEL)aSelector {
if (aSelector == @selector(someMethod)) {
return [NSMethodSignature signatureWithObjcTypes:"v@:"];
}
return [super methodSignatureForSelector:aSelector];
}
// ✅ 推荐:在+load中进行方法交换
+ (void)load {
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
// 方法交换逻辑
});
}
// ❌ 避免:频繁的类查询
// 不要在循环中频繁调用class_copyMethodList等
2. 线程安全考虑
Runtime API线程安全:
- 大部分Runtime API都是线程安全的
objc_msgSend是线程安全的- 方法交换在
+load中进行可保证只执行一次
多线程注意事项:
// 动态类创建需要考虑线程安全
+ (Class)createDynamicClass {
static Class dynamicClass = nil;
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
dynamicClass = objc_allocateClassPair([NSObject class], "DynamicClass", 0);
objc_registerClassPair(dynamicClass);
});
return dynamicClass;
}
3. 内存管理最佳实践
动态类管理:
// 动态创建的类会在程序结束时自动释放
// 但在ARC环境下,需要注意引用计数
Class dynamicClass = objc_allocateClassPair(superClass, className, 0);
// 使用后必须注册
objc_registerClassPair(dynamicClass);
// 动态类会跟随程序生命周期,不需要手动释放
关联对象内存策略:
// 选择正确的关联策略
objc_setAssociatedObject(obj, key, value, OBJC_ASSOCIATION_ASSIGN); // 弱引用
objc_setAssociatedObject(obj, key, value, OBJC_ASSOCIATION_RETAIN); // 强引用
objc_setAssociatedObject(obj, key, value, OBJC_ASSOCIATION_COPY); // 拷贝
objc_setAssociatedObject(obj, key, value, OBJC_ASSOCIATION_RETAIN_NONATOMIC); // 非原子强引用
十、面试常问问题详解
1. Runtime 消息发送机制是什么?
核心原理:
- Objective-C中所有方法调用都是消息发送,不是直接的函数调用
[obj method]编译时会被转换为objc_msgSend(obj, @selector(method))- 消息发送比函数调用多了一层方法查找的开销,但提供了动态性
消息查找流程:
- 缓存查找:优先在方法缓存中查找,提高性能
- 类方法列表查找:在当前类的方法列表中查找
- 继承链查找:沿着superclass指针向上查找父类
- 消息转发:如果找不到,进入三阶段转发流程
2. 消息转发机制详解?
三阶段转发流程:
第一阶段 - 动态方法解析:
+ (BOOL)resolveInstanceMethod:(SEL)sel {
// 可以动态添加方法实现
class_addMethod(self, sel, (IMP)dynamicIMP, "v@:");
return YES;
}
第二阶段 - 快速转发:
- (id)forwardingTargetForSelector:(SEL)aSelector {
// 将消息转发给其他对象处理,返回速度快
return self.helperObject;
}
第三阶段 - 完整转发:
- (void)forwardInvocation:(NSInvocation *)invocation {
// 可以修改参数、返回值,实现复杂的转发逻辑
// 但性能开销最大
}
3. Method Swizzling 是什么?有什么风险?
概念: 在运行时交换两个方法的实现
应用场景:
- AOP编程(埋点统计、日志记录)
- 热修复
- 系统方法增强
风险:
- 方法污染:可能与其他库产生冲突
- 调试困难:方法调用栈混乱
- 线程安全:需要在
+load中进行,保证只执行一次 - 继承问题:子类可能继承到交换后的方法
4. 关联对象 (Associated Objects) 的实现原理?
底层实现:
- 关联对象存储在全局的
AssociationsManager中 - 使用哈希表存储,key为对象地址,value为关联对象的字典
- 当对象释放时,会自动清理关联对象
内存策略:
OBJC_ASSOCIATION_ASSIGN- 弱引用,不增加引用计数OBJC_ASSOCIATION_RETAIN- 强引用,增加引用计数OBJC_ASSOCIATION_COPY- 拷贝对象OBJC_ASSOCIATION_RETAIN_NONATOMIC- 非原子强引用
5. Runtime 如何实现 Category?
实现机制:
- Category在编译时生成独立的结构体
- 运行时将Category的方法列表合并到类的主方法列表中
- 后编译的Category方法会覆盖先编译的同名方法
存储结构:
struct category_t {
const char *name; // 目标类名
struct category_t *next; // 链表结构
struct method_list_t *instanceMethods; // 实例方法
struct method_list_t *classMethods; // 类方法
struct protocol_list_t *protocols; // 协议
struct property_list_t *instanceProperties; // 属性
};
6. isa 指针指向什么?
对象内存结构:
- 实例对象:isa指向类对象
- 类对象:isa指向元类对象,存储类方法
- 元类对象:isa指向基类的元类对象
- 基类元类:isa指向自己,形成闭环
isa 作用:
- 确定对象的类型和类层次
- 支持消息发送机制
- 实现反射功能
7. Runtime 在项目中如何应用?
电商项目应用:
- 支付方式的动态扩展
- 商品组件的动态加载
- 数据埋点系统的实现
- 网络请求的统一处理
即时通讯应用:
- 消息类型的动态注册
- 协议适配器的实现
- 连接状态的统一管理
- 消息处理器的扩展机制
8. 动态创建类的应用场景?
适用场景:
- 插件化架构
- 动态UI组件生成
- 数据库ORM映射
- 网络协议的动态适配
- A/B测试的动态配置
十一、Runtime 实际价值与好处详解
🎯 为什么要在项目中使用Runtime?
Runtime不是"炫技"工具,而是解决实际问题的利器。让我用具体的对比案例来说明好处。
案例1:电商App支付模块 - 传统vs Runtime方案
❌ 传统方案的问题:
// 每次添加新支付方式都要修改这堆代码
- (void)processPayment:(NSString *)paymentType amount:(CGFloat)amount {
if ([paymentType isEqualToString:@"alipay"]) {
[self processAlipayPayment:amount];
} else if ([paymentType isEqualToString:@"wechat"]) {
[self processWechatPayment:amount];
} else if ([paymentType isEqualToString:@"applepay"]) {
[self processApplePayPayment:amount];
} else if ([paymentType isEqualToString:@"paypal"]) {
[self processPaypalPayment:amount];
}
// 添加新支付方式?修改这里 + 新增方法 + 重新编译...
}
痛点:
- 每次接入新支付方式都要修改核心代码
- 违反开闭原则(对扩展开放,对修改封闭)
- 产品经理说"明天上架新支付方式",开发要加班
✅ Runtime动态方案的好处:
// 运行时动态注册支付方式
[[PaymentManager sharedManager] registerPaymentStrategy:[AlipayStrategy class] forType:@"alipay"];
[[PaymentManager sharedManager] registerPaymentStrategy:[WechatStrategy class] forType:@"wechat"];
// 新支付方式?只需要注册一行代码!
- (void)processPayment:(NSString *)paymentType amount:(CGFloat)amount {
// 统一的处理逻辑,永远不需要修改
id<PaymentProtocol> payment = [[PaymentManager sharedManager] createPaymentWithType:paymentType];
[payment payWithAmount:amount completion:^(BOOL success) {
// 处理结果
}];
}
实际好处:
- 🚀 发布速度:新支付方式上线从"1周"缩短到"1小时"
- 🛡️ 稳定性:核心支付逻辑永不修改,bug率降低80%
- 👥 团队协作:支付团队独立开发,互不影响
- 📈 业务价值:快速响应市场,支持更多支付方式,增加转化率
案例2:消息转发在IM中的实际价值
❌ 传统容错处理:
// 收到未知消息类型,App直接崩溃
- (void)handleMessage:(Message *)message {
switch (message.type) {
case MessageTypeText:
[self handleTextMessage:message];
break;
case MessageTypeImage:
[self handleImageMessage:message];
break;
// 忘记处理新消息类型?Crash!
default:
NSAssert(NO, @"Unknown message type: %ld", message.type);
break;
}
}
痛点:
- 新消息类型上线,老版本App崩溃
- 服务器推送新功能,客户端没更新就挂了
- 用户体验极差,可能流失用户
✅ Runtime消息转发容错:
- (void)handleMessage:(Message *)message {
// 先尝试正常处理
if ([self respondsToSelector:@selector(handleTextMessage:)] && message.type == MessageTypeText) {
[self handleTextMessage:message];
} else {
// 未知消息类型?通过消息转发优雅处理
[self forwardMessage:message]; // 不会崩溃!
}
}
- (void)forwardInvocation:(NSInvocation *)anInvocation {
// 记录未知消息类型,用于后续分析
Message *message = nil;
[anInvocation getArgument:&message atIndex:2];
// 存储到"未处理消息队列",等App更新后再处理
[self queueUnhandledMessage:message];
// 发送通知给服务器,告知客户端版本过低
[self reportUnsupportedMessageType:message.type];
}
实际好处:
- 🛡️ Crash防护:未知消息类型不会导致App崩溃
- 📊 数据完整性:重要消息不会丢失,可在App更新后重新处理
- 🔄 向下兼容:新版本服务器可以给老版本客户端发消息
- 📈 用户体验:App稳定运行,用户不会因为版本问题流失
案例3:Category + Runtime的实际威力
❌ 传统Category的局限:
// Category不能添加属性(实际上可以,但需要Runtime)
@interface UIViewController (Tracking)
@property (nonatomic, copy) NSString *pageName; // 编译错误!
@end
// 只能用全局字典存储,代码丑陋
static NSMutableDictionary *pageNames = nil;
@implementation UIViewController (Tracking)
+ (void)initialize {
pageNames = [NSMutableDictionary dictionary];
}
- (void)setPageName:(NSString *)pageName {
pageNames[NSStringFromPointer(self)] = pageName; // 内存泄漏风险!
}
- (NSString *)pageName {
return pageNames[NSStringFromPointer(self)];
}
@end
✅ Runtime关联对象的优雅方案:
#import <objc/runtime.h>
static void *kPageNameKey = &kPageNameKey;
@implementation UIViewController (Tracking)
- (void)setPageName:(NSString *)pageName {
objc_setAssociatedObject(self, kPageNameKey, pageName, OBJC_ASSOCIATION_COPY_NONATOMIC);
}
- (NSString *)pageName {
return objc_getAssociatedObject(self, kPageNameKey);
}
// 自动埋点
+ (void)load {
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
Method original = class_getInstanceMethod(self, @selector(viewDidAppear:));
Method swizzled = class_getInstanceMethod(self, @selector(tracking_viewDidAppear:));
method_exchangeImplementations(original, swizzled);
});
}
- (void)tracking_viewDidAppear:(BOOL)animated {
[self tracking_viewDidAppear:animated];
// 自动上报埋点
if (self.pageName) {
[TrackingManager trackPageView:self.pageName];
}
}
@end
// 使用超级简单
- (void)viewDidLoad {
[super viewDidLoad];
self.pageName = @"商品详情页"; // 直接赋值,就像普通属性!
}
实际好处:
- ✨ 代码简洁:Category可以像普通类一样添加属性
- 🚀 零侵入:不需要修改原有类,完美解耦
- 🛡️ 内存安全:关联对象会自动随宿主对象释放
- 📊 数据准确:埋点数据自动收集,提高统计准确性
案例4:动态类创建的业务价值
电商场景:运营配置化页面
// 运营同学通过后台配置页面结构
NSDictionary *pageConfig = @{
@"components": @[
@{@"type": @"banner", @"data": @{@"images": @[@"img1.jpg", @"img2.jpg"]}},
@{@"type": @"productList", @"data": @{@"category": @"hot"}},
@{@"type": @"coupon", @"data": @{@"discount": @"0.8"}},
// 运营随时添加新组件...
]
};
// 传统方案:硬编码每个组件
- (UIView *)createComponentWithType:(NSString *)type {
if ([type isEqualToString:@"banner"]) {
return [[BannerView alloc] init];
} else if ([type isEqualToString:@"productList"]) {
return [[ProductListView alloc] init];
} // 又要加代码...
}
// Runtime动态方案:自动创建
- (UIView *)createComponentWithType:(NSString *)type config:(NSDictionary *)config {
// 动态构造类名
NSString *className = [NSString stringWithFormat:@"%@Component", type.capitalizedString];
Class componentClass = NSClassFromString(className);
if (!componentClass) {
// 类不存在?运行时创建!
componentClass = [self createDynamicComponentClassWithType:type];
}
id component = [[componentClass alloc] init];
[component configureWithConfig:config];
return component;
}
实际好处:
- ⚡ 发布效率:运营配置页面,从开发1天缩短到运营5分钟
- 🔄 灵活调整:A/B测试、节日活动页面快速调整
- 👥 分工明确:运营专注内容,开发专注组件开发
- 📈 业务响应:快速响应市场变化,提升竞争力
十二、Runtime使用原则
✅ 何时使用Runtime?
- 组件化架构:模块解耦,动态加载
- 插件化系统:运行时扩展功能
- AOP编程:埋点、日志、性能监控
- 向下兼容:新功能兼容老版本
- 配置化:运营可配置的动态界面
❌ 何时不使用Runtime?
- 性能敏感:频繁调用的核心逻辑
- 代码可读性:过度使用会让代码难以理解
- 调试困难:Runtime代码调试相对复杂
- 团队水平:团队成员需要理解Runtime机制
🎯 最佳实践
- 封装抽象:将Runtime逻辑封装成易用的工具类
- 性能监控:监控Runtime操作的性能影响
- 文档注释:详细说明Runtime的使用方式和目的
- 单元测试:确保Runtime逻辑的正确性
文档版本: v2.1 - 价值详解版
最后更新: 2026年1月14日
记住:Runtime不是为了炫技,而是为了解决实际问题!在合适的场景使用,能带来巨大的业务价值和开发效率提升。