RunTime/RunLoop
2020-10-15 本文已影响0人
Li_Po
Runtime
runtime源码地址
https://opensource.apple.com/source/objc4/
runloop源码地址
https://opensource.apple.com/tarballs/CF/
所有对象都是ID类型,对应runtime就是objc_object,结构体构成
image.png
image.png
image.png
image.png
image.png
image.png
image.png
image.png
image.png
image.png
image.png
image.png
image.png
image.png
image.png
image.png
[self class]和[super class]都是调用当前对象
image.png
image.png
image.png
image.png
消息转发
image.png
#import "RuntimeObject.h"
#import <objc/runtime.h>
@implementation RuntimeObject
void testImp (void)
{
NSLog(@"test invoke");
}
//动态方法解析 添加方法 第一次机会 lp
+ (BOOL)resolveInstanceMethod:(SEL)sel
{
// 如果是test方法 打印日志
if (sel == @selector(test)) {
NSLog(@"resolveInstanceMethod:");
// 动态添加test方法的实现
class_addMethod(self, @selector(test), testImp, "v@:");
return YES;
}
else{
// 返回父类的默认调用
return [super resolveInstanceMethod:sel];
}
}
//动态方法解析 转发消息 第二次机会 lp
- (id)forwardingTargetForSelector:(SEL)aSelector
{
if (aSelector == @selector(test)) {
NSLog(@"forwardingTargetForSelector:");
return nil;
}
return [super forwardingTargetForSelector:aSelector];
}
//方法签名 lp
- (NSMethodSignature *)methodSignatureForSelector:(SEL)aSelector
{
if (aSelector == @selector(test)) {
NSLog(@"methodSignatureForSelector:");
//返回方法签名
// v 代表返回值是void类型的 @代表第一个参数类型是id,即 self
// : 代表第二个参数是SEL类型的 即@selector(test)
return [NSMethodSignature signatureWithObjCTypes:"v@:"];
}
else{
return [super methodSignatureForSelector:aSelector];
}
}
//消息转发
- (void)forwardInvocation:(NSInvocation *)anInvocation
{
NSLog(@"forwardInvocation:");
}
方法交换
image.png
image.png
Method m1 = class_getClassMethod([NSURL class], @selector(URLWithString:));
Method m2 = class_getClassMethod([NSURL class], @selector(LP_URLWithString:));
method_exchangeImplementations(m1, m2);
image.png
RunLoop
- 什么是RunLoop
RunLoop是通过内部维护的事件循环来对事件/消息进行管理的一个对象。
没有消息需要处理时,休眠以避免资源占用(通过系统调用mach_msg(),用户态->内核态)
有消息处理时立刻唤醒(内核态->用户态)
维护的事件循环可以不断地处理消息或事件,然后对其进行管理。
image.png
image.png
image.png
image.png
#import "ViewController.h"
typedef void (^runloopBlock)(void);
@interface ViewController ()<UITableViewDelegate,UITableViewDataSource>
@property (nonatomic,strong)UITableView *tableView;
@property (nonatomic,strong)NSMutableArray *taskArray;
@property (nonatomic,assign)NSUInteger maxTaskCount;
@property (nonatomic,strong)NSTimer * timer;
@end
@implementation ViewController
- (void)viewDidLoad {
[super viewDidLoad];
//rujnloop 的使用
//创建定时任务
CADisplayLink * play = [CADisplayLink displayLinkWithTarget:self selector:@selector(doSomeThing)];
[play addToRunLoop:[NSRunLoop currentRunLoop] forMode:NSRunLoopCommonModes];
//_timer = [NSTimer scheduledTimerWithTimeInterval:0.001 target:self selector:@selector(doSomeThing) userInfo:nil repeats:YES];
_taskArray = [NSMutableArray array];
_maxTaskCount = 20;
//添加runloop观察者
[self addRunloopObserver];
[self.view addSubview:self.tableView];
}
-(UITableView *)tableView{
if (!_tableView) {
_tableView = [[UITableView alloc]initWithFrame:CGRectMake(0, 0, self.view.bounds.size.width, self.view.bounds.size.height) style:UITableViewStylePlain];
_tableView.delegate = self;
_tableView.dataSource = self;
}
return _tableView;
}
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section{
return 50;
}
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath{
static NSString * cellStr = @"cell";
TableViewCell * cell = [tableView dequeueReusableCellWithIdentifier:cellStr];
if (!cell) {
cell = [[TableViewCell alloc]initWithStyle:UITableViewCellStyleDefault reuseIdentifier:cellStr];
}
cell.myLable.text = [NSString stringWithFormat:@"显示第%ld张图片",indexPath.row];
[self addtask:^{
cell.myImageView1.image = [UIImage imageNamed:@"timg.jpg"];
}];
[self addtask:^{
cell.myImageView2.image = [UIImage imageNamed:@"timg.jpg"];
}];
[self addtask:^{
cell.myImageView3.image = [UIImage imageNamed:@"timg.jpg"];
}];
[self addtask:^{
cell.myImageView4.image = [UIImage imageNamed:@"timg.jpg"];
}];
[self addtask:^{
cell.myImageView5.image = [UIImage imageNamed:@"timg.jpg"];
}];
return cell;
}
- (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath{
return 80;
}
#pragma mark runloop-- c语言代码
-(void)doSomeThing{
//z不做事情 只是让runloop一直运行
}
-(void)addtask:(runloopBlock)task{
//添加任务
[_taskArray addObject:task];
if (_taskArray.count>_maxTaskCount) {
[_taskArray removeObjectAtIndex:0];
}
}
static void callBack(CFRunLoopObserverRef observer, CFRunLoopActivity activity, void *info){
//取出任务执行
ViewController * vc = (__bridge ViewController *)info;
if (vc.taskArray.count==0) {
return;
}
runloopBlock block = [vc.taskArray firstObject];
if (block) {
block();
}
[vc.taskArray removeObjectAtIndex:0];
}
-(void)addRunloopObserver{
//拿到当前的runloop
CFRunLoopRef runloop = CFRunLoopGetCurrent();
//定义上下文
CFRunLoopObserverContext context = {
0,
(__bridge void *)(self),
&CFRetain,
&CFRelease,
NULL
};
//定义一个观察车
static CFRunLoopObserverRef defaultModeObserver;
//创建一个观察者
defaultModeObserver = CFRunLoopObserverCreate(NULL, kCFRunLoopAfterWaiting, YES, 0, &callBack, &context);
CFRunLoopAddObserver(runloop, defaultModeObserver, kCFRunLoopCommonModes);
CFRelease(defaultModeObserver);
}
@end
- 怎样实现一个常住线程
@implementation MCObject
static NSThread *thread = nil;
// 标记是否要继续事件循环
static BOOL runAlways = YES;
+ (NSThread *)threadForDispatch{
if (thread == nil) {
@synchronized(self) {
if (thread == nil) {
// 线程的创建
thread = [[NSThread alloc] initWithTarget:self selector:@selector(runRequest) object:nil];
[thread setName:@"com.imooc.thread"];
//启动
[thread start];
}
}
}
return thread;
}
+ (void)runRequest
{
// 创建一个Source
CFRunLoopSourceContext context = {0, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL};
CFRunLoopSourceRef source = CFRunLoopSourceCreate(kCFAllocatorDefault, 0, &context);
// 创建RunLoop,同时向RunLoop的DefaultMode下面添加Source
CFRunLoopAddSource(CFRunLoopGetCurrent(), source, kCFRunLoopDefaultMode);
// 如果可以运行
while (runAlways) {
@autoreleasepool {
// 令当前RunLoop运行在DefaultMode下面
CFRunLoopRunInMode(kCFRunLoopDefaultMode, 1.0e10, true);
}
}
// 某一时机 静态变量runAlways = NO时 可以保证跳出RunLoop,线程退出
CFRunLoopRemoveSource(CFRunLoopGetCurrent(), source, kCFRunLoopDefaultMode);
CFRelease(source);
}