赞
踩
类对象存储实例方法列表等信息
元类对象存储类方法列表等信息
实例对象通过isa指针找到它的类对象,类对象当中存储方法列表等信息,以及类对象可以通过isa指针找到它的元类对象,可以访问一些关于类方法的相关信息。
类对象和元类对象都是objc_class数据结构,由于objc_class继承了objc_object所以他们才有isa指针,实例可以通过isa指针找到类对象来访问实例方法列表等信息,类对象通过isa指针找到元类对象来访问类方法列表等信息。
元类对象的isa指针指向,对于任何一个元类对象它的isa指针都指向根元类对象。
根元类对象它的superClass指向根类对象,调用类方法从元类方法列表中查找,逐级父类查找,当类方法在元类方法中找不到的时候,就会找根对象同名的实例方法。
由于根元类superClass指针指向了根类对象,在元类对象找类方法列表没查找到的话,就会到实例方法列表中去查找如果有同名方法就会执行同名方法的实例方法调用。
[self class] = objc_msgSend(self,@selector(class)) 编译器层面
[super class] = objc_msgSendSuper(super,@selector(class))
例:给定值是SEL,目标值是对应的bucket_t的IMP。
首先根据给定的SEL选择器通过一个函数来映射出对应的bucket_t在数组中的位置也就是哈希查找。
cache_key_t —— f(key)——bucket_t
哈希查找实际是通过给定的一个值(比如这个方法的选择器)经过哈希函数的算法算出的值实际上就是给定值在对应数组列表中对应的索引位置。采用哈希查找的关键是为了解决查找效率的问题。查找到了选择器因子所对应的bucket _t之后就可以提取它对应的IMP函数指针返回给调用方。
f(key) = key & mask
对于已排序好的列表,采用二分查找算法查找方法对应执行函数。
对于没有排序的列表,采用一般遍历查找方法对应执行函数。
根据superclass指针一点一点往上找查找它的父类,遍历每一个父类,对于每一个父类方法中的查找也是同样首先查找父类的缓存,缓存没有命中在查找父类对应的方法列表,对于方法列表来说仍然是已排序的采用二分查找没排序的采用一般遍历。
步骤一:当根据selector没有找到对应的method时,首先会调用resolveInstanceMethod:这个方法,在该方法中你可以为一个类添加一个方法。并返回yes
步骤二:如果resolveInstanceMethod没有实现,返回No,或者没有动态添加方法的话,就会执行forwardingTargetForSelector: 相当于告诉系统这个选择器或者这次的实例方法调用应该由哪个对象来处理,转发对象是谁,如果我们指定了一个转发目标的话,系统会把这条消息转发给返回转发目标,同时会结束当前的消息转发流程。
步骤三:如果在forwardingTargetForSelector:中仍然没有给它返回一个转发目标的情况下, 系统会最后一次处理,并执行methodSignatureForSelector:有两种结果:
1:如果返回了一个方法签名的话就会执行forwardInvocation:如果forwardInvocation:能够处理这条消息的话,消息转发流程就结束了。
2:如果methodSignatureForSelector:返回的是nil,或者forwardInvocation:没有办法处理这条消息被标记为消息无法处理,一种常见的崩溃就是未识别选择器,就是走到消息转发最后一步所产生的一个打印结果
class_getInstanceMethod( )
method_exchangeimpwentations( )
问题1:举例 我们实现一个Person类 然后Person 其实是没得对象方法eat:的
下面调用person的eat方法 程序是会奔溃的 那么需要借助运行时动态的添加方法
- Person *p = [[Person alloc]init];
- [p performSelector:@selector(eat:) withObject:@"鸡腿"];
- // 定义eat 方法 默认会前面两个隐式参数 id self, SEL _cmd 第三个才是我们自己传入的参数
- void eat(id self, SEL _cmd ,id object ){
- NSLog(@"吃了%@", object);
- }
- //使用到了未定义的对象方法
- +(BOOL)resolveInstanceMethod:(SEL)sel{
- NSLog(@"%@", NSStringFromSelector(sel));
- if (sel == @selector(eat:)) {
- //动态添加方法
-
- /**
- <#Description#>
- @param cls#> 那个类 description#>
- @param name#> 方法名 description#>
- @param imp#> 方法指针 description#>
- @param types#> 方法类型 description#>
- @return <#return value description#>
- */
- // class_addMethod(<#Class _Nullable __unsafe_unretained cls#>, <#SEL _Nonnull name#>, <#IMP _Nonnull imp#>, <#const char * _Nullable types#>)
- class_addMethod([self class], @selector(eat:), (IMP)eat, "v");
- }
- return [super resolveInstanceMethod:sel];
-
- }
@dynamic (getter和setter是在运行时添加)
动态运行时语言将函数决议推迟到运行时。(运行时为方法添加具体执行函数)
编译时语言在编译期进行函数决议。(在编译期间就确定一个方法名称所对应的函数执行体是哪个,在具体运行过程中是没有办法修改的)
在编译器的处理过程之后就变成了objc_msgSend第一个参数是obj,第二个参数是foo选择器,转化成objc_msgSend的函数调用开始runTime的消息传递过程。
查找当前实例所对应类对象的缓存是否有Selector对应的缓存的一个IMP实现,如果缓存命中了就把命中的缓存函数返回给调用方,如果缓存没有命中那么我们在根据当前类的方法列表去查找Selector对应的具体IMP实现,如果当前类没有命中在根据当前类的superClass指针逐级查找父类的方法列表,查找对应Selector对应的IMP实现。
不能增加 我们创建的类他已经完成了实例变量的布局calss_ro_t只读,在编译后是无法修改的。
可以增加 需要我们在动态添加的类过程当中调用它的注册类方法之前去完成实例变量的添加即可。
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。