欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

RunTime常用场景小结

程序员文章站 2022-07-13 21:34:27
...

RunTime简称运行时,基本用C和汇编写的。

1.消息转发机制;

2.通过分类扩展属性;

3.拦截系统方法调用(高大上的Swizzle 黑魔法),其实也是对系统的方法进行交换方法;

4.序列化和反序列;

下面我们具体来分析一下RunTime的使用场景;

1.消息转发机制:当调用某对象上某个方法(sendMessage),而该方法没有实现时,系统会报unrecognized selector的异常。在此异常之前走了一圈流程(如图),objc的运行时会给出三次拯救程序崩溃的机会。

RunTime常用场景小结

第一次:

当这个类被调用一个没有实现的类方法 ,会调用+ (BOOL)resolveClassMethod:(SEL)sel或者+ (BOOL)resolveInstanceMethod:(SEL)sel,可以在提供一个函数实现;

//当这个类被调用了一个没有实现的对象方法
+ (BOOL)resolveInstanceMethod:(SEL)sel
{
	NSString *methodName = NSStringFromSelector(sel);
	if ([methodName isEqualToString:@"sendMessage:"]) {
//		class_addMethod(<#Class  _Nullable __unsafe_unretained cls#>, <#SEL  _Nonnull name#>, <#IMP  _Nonnull imp#>, <#const char * _Nullable types#>)
		//官方文档 command+shift+0
		//cls name imp types
		return class_addMethod(self, sel,(IMP)sendMessage,"aaa@qq.com:@");
	}
	return [super resolveInstanceMethod:sel];
}

void sendMessage(id self, SEL _cmd ,NSString *message){
	NSLog(@"message ==%@",message);
}

第二次:

当第一次机会没有把握后会调用Fast forwarding 消息转发,此时的消息转发给其他的对象,只要这个方法返回的不是nil和self,整个消息发送的过程就会被重启,当然发送的对象会变成返回的那个对象(Son)。

// Fast forwarding 消息转发 第二步
- (id)forwardingTargetForSelector:(SEL)aSelector
{
	NSString *methodName = NSStringFromSelector(aSelector);
	if ([methodName isEqualToString:@"sendMessage:"]) {
		return [Son new];
	}
	return [super forwardingTargetForSelector:aSelector];
}

第三次:

首先它会发送-methodSignatureForSelector:消息获得函数的参数和返回值类型。如果-methodSignatureForSelector:返回nil,Runtime则会发出-doesNotRecognizeSelector:消息,程序这时发出异常崩溃。如果返回了一个函数签名,Runtime就会创建一个NSInvocation对象并发送-forwardInvocation:消息给目标对象。

//normal forwarding 消息转发 第三步
//越到后面消耗越大
//1
- (NSMethodSignature *)methodSignatureForSelector:(SEL)aSelector
{
	NSString *methodName = NSStringFromSelector(aSelector);
	if ([methodName isEqualToString:@"sendMessage:"])
		{
			return [NSMethodSignature signatureWithObjCTypes:"aaa@qq.com:@"];
		}
		return [super methodSignatureForSelector:aSelector];
}
//2
- (void)forwardInvocation:(NSInvocation *)anInvocation
{
	SEL sel = [anInvocation selector];
	Son *son = [Son new];
	if ([son respondsToSelector:sel]) {
		[anInvocation invokeWithTarget:son];
		return;
	}
	[super forwardInvocation:anInvocation];
}

//消息无法处理
- (void)doesNotRecognizeSelector:(SEL)aSelector
{
	NSLog(@"消息无法处理");
}

2.通过分类扩展属性:分类中是无法设置属性,property只能为其生成get 和 set 方法的声明,但无法生成成员变量。所以需要借助Runtime为分类扩展属性功能。

//通过分类扩展属性
- (void)setIsSelected:(BOOL)isSelected
{
	objc_setAssociatedObject(self, &selected, @(isSelected), OBJC_ASSOCIATION_ASSIGN);
}

- (BOOL)isSeted
{
	return objc_getAssociatedObject(self, &selected);
}

3.拦截系统方法调用:主要函数method_exchangeImplementations,当我们需要改变一个系统方法的实现时,想修改方法的实现就可以用此方法。load在main之前调用,点击APP的时候就是把可以执行的读到内存里面。

//load在main之前调用 点击APP的时候就是把可以执行的读到内存里面
+ (void)load
{
	//交换方法的调用顺序
	Method urlStr = class_getClassMethod(self, @selector(URLWithString:));
	Method WP_urlStr = class_getClassMethod(self, @selector(WP_URLWithString:));
	//交换
	method_exchangeImplementations(urlStr, WP_urlStr);
}

//类似于方法这样的高级功能
//协商注释!
+(instancetype)WP_URLWithString:(NSString *)URLString
{
	//断点时会一点递归,此时已经交换了 所以调用WP就是调用系统消息
//	NSURL *url = [NSURL URLWithString:URLString];
	NSURL *url = [NSURL WP_URLWithString:URLString];

	if (url == nil) {
		NSLog (@"url is nil");
	}
	return url;
}

4.序列化和反序列:(归根到底还是遍历属性)

//序列化和反序列化
- (void)encodeWithCoder:(NSCoder *)aCoder;
{
	unsigned int count = 0;
	// 获取所有成员变量
	Ivar *ivars = class_copyIvarList([self class], &count);
	for (int i = 0; i<count; i++) {
		Ivar var = ivars[i];
		const char *name = ivar_getName(var);
		// 将每个成员变量名转换为NSString对象类型
		NSString *key = [NSString stringWithUTF8String:name];
		id value = [self valueForKey:key];
		[aCoder encodeObject:value forKey:key];
	}
}

- (nullable instancetype)initWithCoder:(NSCoder *)aDecoder
{
	if (self == [self init]) {
		unsigned int count = 0;
		Ivar *ivars = class_copyIvarList([self class], &count);
		for (int i = 0; i<count; i++) {
			Ivar var = ivars[i];
			const char *name = ivar_getName(var);
			NSString *key = [NSString stringWithUTF8String:name];
			id value = [aDecoder decodeObjectForKey:key];
			[aDecoder setValue:value forKey:key];
		}
	}
	return self;
}

 

相关标签: runtime