当前位置: 首页 > ops >正文

iOS Runtime与RunLoop的对比和使用

Runtime 机制

核心概念

  1. Objective-C 的动态特性:Objective-C 是一门动态语言,很多工作都是在运行时而非编译时决定的
  2. 消息传递机制:方法调用实际上是发送消息 objc_msgSend(receiver, selector, ...)
  3. 方法决议机制:动态方法解析、消息转发流程

重要数据结构

  • Class:类对象,包含 isa 指针、superclass 指针、方法缓存等
  • objc_object:所有对象的基类
  • Method:方法结构体,包含 SEL 和 IMP
  • Ivar:实例变量结构体
  • Property:属性结构体
  • Protocol:协议结构体

核心功能

  1. 方法交换 (Method Swizzling)
Method originalMethod = class_getInstanceMethod([self class], @selector(viewDidLoad));
Method swizzledMethod = class_getInstanceMethod([self class], @selector(xxx_viewDidLoad));
method_exchangeImplementations(originalMethod, swizzledMethod);
  1. 动态添加方法
class_addMethod([self class], @selector(resolveThisMethodDynamically), (IMP)dynamicMethodIMP, "v@:");
  1. 关联对象 (Associated Objects)
static char associatedKey;
objc_setAssociatedObject(object, &associatedKey, value, OBJC_ASSOCIATION_RETAIN_NONATOMIC);
id value = objc_getAssociatedObject(object, &associatedKey);
  1. 消息转发机制
// 1. 动态方法解析 
+ (BOOL)resolveInstanceMethod:(SEL)sel;
// 2. 备用接收者 
- (id)forwardingTargetForSelector:(SEL)aSelector;
// 3. 完整消息转发 
- (NSMethodSignature *)methodSignatureForSelector:(SEL)aSelector;
- (void)forwardInvocation:(NSInvocation *)anInvocation;

使用案例

  1. 无侵入埋点统计
// 交换 viewDidAppear: 方法实现 
+ (void)load {static dispatch_once_t onceToken;dispatch_once(&onceToken, ^{ [self swizzleMethod:@selector(viewDidAppear:) withMethod:@selector(swizzled_viewDidAppear:)];});
}- (void)swizzled_viewDidAppear:(BOOL)animated {[self swizzled_viewDidAppear:animated];[Tracking logEvent:@"ViewAppear" params:@{@"class": NSStringFromClass([self class])}];
}
  1. 防止数组越界崩溃
+ (void)load {Method originalMethod = class_getInstanceMethod(NSClassFromString(@"__NSArrayI"), @selector(objectAtIndex:));Method swizzledMethod = class_getInstanceMethod([self class], @selector(safeObjectAtIndex:));method_exchangeImplementations(originalMethod, swizzledMethod);
}- (id)safeObjectAtIndex:(NSUInteger)index {if (index < [self count]) {return [self safeObjectAtIndex:index];}NSLog(@"数组越界");return nil;
}

RunLoop 机制

核心概念

  1. 事件循环机制:保持线程持续运行并处理各种事件
  2. 运行模式 (Mode):包含 Source/Timer/Observer
    • NSDefaultRunLoopMode:默认模式
    • UITrackingRunLoopMode:界面跟踪模式
    • NSRunLoopCommonModes:通用模式集合

核心组件

  1. Source:

    • Source0:非基于端口的,处理应用内部事件
    • Source1:基于端口的,处理系统事件
  2. Timer:基于时间的触发器

  3. Observer:观察 RunLoop 状态变化

RunLoop 生命周期

  1. 通知即将进入 RunLoop
  2. 通知即将处理 Timer
  3. 通知即将处理 Source0
  4. 处理 Source0
  5. 如果有 Source1 准备就绪,跳转处理
  6. 通知即将进入休眠
  7. 通知即将被唤醒
  8. 处理唤醒时收到的消息
  9. 通知即将退出 RunLoop

使用案例

  1. 保持线程常驻
+ (NSThread *)networkThread {static NSThread *thread = nil;static dispatch_once_t onceToken;dispatch_once(&onceToken, ^{ thread = [[NSThread alloc] initWithTarget:self selector:@selector(networkThreadEntryPoint:) object:nil];[thread start];});return thread;
}+ (void)networkThreadEntryPoint:(id)__unused object {@autoreleasepool {[[NSThread currentThread] setName:@"com.company.network"];NSRunLoop *runLoop = [NSRunLoop currentRunLoop];[runLoop addPort:[NSMachPort port] forMode:NSDefaultRunLoopMode];[runLoop run];}
}
  1. 性能优化 - 图片加载
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {// ...[self performSelector:@selector(loadImageForCell:)withObject:cell afterDelay:0 inModes:@[NSDefaultRunLoopMode]];// ...
}- (void)loadImageForCell:(UITableViewCell *)cell {// 实际图片加载逻辑 
}
  1. 卡顿监测
- (void)startMonitor {CFRunLoopObserverRef observer = CFRunLoopObserverCreateWithHandler(kCFAllocatorDefault, kCFRunLoopAllActivities, YES, 0, ^(CFRunLoopObserverRef observer, CFRunLoopActivity activity) {switch (activity) {case kCFRunLoopEntry:NSLog(@"即将进入RunLoop");break;case kCFRunLoopBeforeTimers:NSLog(@"即将处理Timer");break;case kCFRunLoopBeforeSources:NSLog(@"即将处理Source");break;case kCFRunLoopBeforeWaiting:NSLog(@"即将进入休眠");break;case kCFRunLoopAfterWaiting:NSLog(@"刚从休眠中唤醒");break;case kCFRunLoopExit:NSLog(@"即将退出RunLoop");break;}});CFRunLoopAddObserver(CFRunLoopGetMain(), observer, kCFRunLoopCommonModes);CFRelease(observer);
}
  1. NSTimer 在滚动时保持运行
NSTimer *timer = [NSTimer timerWithTimeInterval:1.0 target:self selector:@selector(timerAction:) userInfo:nil repeats:YES];
[[NSRunLoop currentRunLoop] addTimer:timer forMode:NSRunLoopCommonModes];

Runtime 与 RunLoop 的协同应用

  1. 异步主线程执行检测
- (void)performOnMainThread:(dispatch_block_t)block {if (strcmp(dispatch_queue_get_label(DISPATCH_CURRENT_QUEUE_LABEL), dispatch_queue_get_label(dispatch_get_main_queue())) == 0) {block();} else {// 检查是否在主线程RunLoop中 if ([NSThread isMainThread]) {// 使用RunLoop在当前迭代中执行 CFRunLoopPerformBlock(CFRunLoopGetMain(), kCFRunLoopCommonModes, block);CFRunLoopWakeUp(CFRunLoopGetMain());} else {dispatch_async(dispatch_get_main_queue(), block);}}
}
  1. 方法调用频率限制
- (void)throttledPerformSelector:(SEL)selector withObject:(id)object {// 取消之前的调用 [NSObject cancelPreviousPerformRequestsWithTarget:self selector:selector object:object];// 延迟执行,确保在RunLoop的下一个周期处理 [self performSelector:selector withObject:object afterDelay:0.1 inModes:@[NSDefaultRunLoopMode]];
}

注意事项

  1. Runtime 使用注意事项:

    • Method Swizzling 应该在 +load 方法中进行
    • 注意线程安全问题
    • 避免过度使用,影响代码可读性
  2. RunLoop 使用注意事项:

    • 不要随意停止主线程的 RunLoop
    • 注意 RunLoop Mode 的选择
    • 避免在 RunLoop 中执行耗时操作
  3. 性能考虑:

    • Runtime 的反射操作比直接调用方法慢
    • RunLoop 的 Observer 会增加运行开销
    • 频繁的 Mode 切换会影响性能

通过合理使用 Runtime 和 RunLoop,可以实现许多强大的功能,但同时也要注意它们带来的复杂性和潜在问题。

http://www.xdnf.cn/news/7488.html

相关文章:

  • 基于flask+vue的电影可视化与智能推荐系统
  • PostgreSQL架构
  • HTML应用指南:利用POST请求获取全国申通快递服务网点位置信息
  • 华为云鲲鹏型kC2云服务器——鲲鹏920芯片性能测评
  • 【EI会议火热征稿中】第二届云计算与大数据国际学术会议(ICCBD 2025)
  • 程序运行报错分析文档
  • 使用 adb 命令截取 Android 设备的屏幕截图
  • CentOS 7连接公司网络配置指南
  • BERT 作为Transformer的Encoder 为什么采用可学习的位置编码
  • 打破次元壁,VR 气象站开启气象学习新姿势​
  • 使用SQLite Studio导出/导入SQL修复损坏的数据库
  • 面试突击:消息中间件之RabbitMQ
  • 流复备机断档处理
  • 开疆智能Profinet转RS485网关连接温度送变器配置案例
  • Pytorch分布式训练,数据并行,单机多卡,多机多卡
  • SOC-ESP32S3部分:2-2-VSCode进行编译烧录
  • laravel中如何使用Validator::make定义一个变量是 ,必传的,json格式字符串
  • 大数据Spark(五十九):Standalone集群部署
  • 小白编程学习之巧解「消失的数字」
  • 利用朴素贝叶斯对UCI 的 mushroom 数据集进行分类
  • Ansible模块——通过 URL 下载文件
  • 题海拾贝:P2285 [HNOI2004] 打鼹鼠
  • C#处理印尼地区的数字分隔符方法
  • Python60日基础学习打卡D31
  • OSA实战笔记二
  • 【520 特辑】用 HTML/CSS/JavaScript 打造浪漫炫酷的表白网页
  • Android Edge-to-Edge
  • AI驱动发展——高能受邀参加华为2025广东新质生产力创新峰会
  • 第一章走进java世界
  • HarmonyOS NEXT~鸿蒙系统与Uniapp跨平台开发实践指南