聊聊 Runtime

5 阅读19分钟

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)

消息查找流程:

  1. 方法缓存查找 - 先在缓存中查找方法实现
  2. 类的方法列表查找 - 在当前类的方法列表中查找
  3. 继承链查找 - 沿着superclass指针向上查找父类
  4. 消息转发 - 如果找不到,进入消息转发流程

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 MessageTypeRegistrystatic 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))
  • 消息发送比函数调用多了一层方法查找的开销,但提供了动态性

消息查找流程:

  1. 缓存查找:优先在方法缓存中查找,提高性能
  2. 类方法列表查找:在当前类的方法列表中查找
  3. 继承链查找:沿着superclass指针向上查找父类
  4. 消息转发:如果找不到,进入三阶段转发流程

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?

  1. 组件化架构:模块解耦,动态加载
  2. 插件化系统:运行时扩展功能
  3. AOP编程:埋点、日志、性能监控
  4. 向下兼容:新功能兼容老版本
  5. 配置化:运营可配置的动态界面

❌ 何时不使用Runtime?

  1. 性能敏感:频繁调用的核心逻辑
  2. 代码可读性:过度使用会让代码难以理解
  3. 调试困难:Runtime代码调试相对复杂
  4. 团队水平:团队成员需要理解Runtime机制

🎯 最佳实践

  • 封装抽象:将Runtime逻辑封装成易用的工具类
  • 性能监控:监控Runtime操作的性能影响
  • 文档注释:详细说明Runtime的使用方式和目的
  • 单元测试:确保Runtime逻辑的正确性

文档版本: v2.1 - 价值详解版

最后更新: 2026年1月14日

记住:Runtime不是为了炫技,而是为了解决实际问题!在合适的场景使用,能带来巨大的业务价值和开发效率提升。