- iOS-OC-分类 -objc_getClass 和object
- objc_getClass和object_getClass
- objc_getClass 和 object_getClass
- objc_getClass()、object_getClass(
- objc_getClass/object_getClass/cl
- OC中 objc_getClass 和object_getCla
- 关于runtime的objc_getClass和object_g
- class、objc_getClass、object_getCl
- objc_getClass、object_getClass、cl
- objc_getClass/object_getClass/cl
一.假设有如下继承关系
1. @interface MJStudent : MJPerson //MJStudent.m
2. @interface MJStudent (Test) //MJStudent+Test.m
首先要知道的事,当类和分类中有同样方法的时候,也就是MJStudent 和 MJStudent+Test,优先调用分类的方法.
下面通过源码进行验证:
runtime源码 找到objc4对应的目录下载,你值得拥有
二. 接下来根据源码一步步往里面探究(说明:我下载的runtime版本是objc4-779.1,不同版本行号可能有出入)
1>首先进入objc-os.mm
这个文件,别问我怎么知道的,看到MJ老是视频,找到void _objc_init(void)
这个方法,根据这个方法上面的介绍可以看到,这个模块是由dyld引导初始化的方法
/***********************************************************************
* _objc_init
* Bootstrap initialization. Registers our image notifier with dyld.
* Called by libSystem BEFORE library initialization time
根据map_images
这个引用点击去(函数前面提供了行号,便于后来的小伙伴自己检验)
928: _dyld_objc_notify_register(&map_images, load_images, unmap_image);
2>跟着下面的代码继续,这个方法点进去只有声明,在Xcode左上角搜索,发现实现在objc-os.mm
这个文件
2942: return map_images_nolock(count, paths, mhdrs);
3>继续跟进
592: _read_images(hList, hCount, totalClasses, unoptimizedTotalClasses);
此时我要截个图
image.png
跟着这个方法往下滑,直到你看到下面这个:
3519: objc::unattachedCategories.addForClass(lc, cls);
这个方法翻译过来就是,没有附加分类的方法.添加到某个类,不用多说,添加的这个类在本文就是就是MJStudent,也就是分类继承的那个类
3>addForClass(lc, cls),继续点下去,
1127: attachCategories(cls, list.array(), list.count(), otherFlags | ATTACH_CLASS);
话不多少继续跟进,mlists是需要添加的方法数组,mcount是需要添加的方法个数
image.png
4>核心代码 将原来类中的方法数组往后挪,然后把分类的插在前面,对应到代码就是 把array 这个数组扩大到 count+addedCount那么大,然后将array原来的数组内容总数放到最后,也就是count个,最后把addedLists里面的放到array最前面.
image.png
附送一份分类的结构体
struct category_t {
const char *name;
classref_t cls;
struct method_list_t *instanceMethods;
struct method_list_t *classMethods;
struct protocol_list_t *protocols;
struct property_list_t *instanceProperties;
// Fields below this point are not always present on disk.
struct property_list_t *_classProperties;
method_list_t *methodsForMeta(bool isMeta) {
if (isMeta) return classMethods;
else return instanceMethods;
}
property_list_t *propertiesForMeta(bool isMeta, struct header_info *hi);
protocol_list_t *protocolsForMeta(bool isMeta) {
if (isMeta) return nullptr;
else return protocols;
}
};
三.通过代码验证,在viewController写如下方法
//打印类的所有方法
- (void)printAllMethodWithCategory:(Class )cls{
unsigned int count;
NSMutableString *methodStr = [[NSMutableString alloc]init];
Method *methodList = class_copyMethodList(cls, &count);
for (int i = 0; i<count; i++) {
Method method = methodList[i];
SEL sel = method_getName(method);
NSString *methodTempStr = NSStringFromSelector(sel);
[methodStr appendString:methodTempStr];
[methodStr appendString:@", "];
}
NSLog(@"%@",methodStr);
free(methodList);
}
四.有没有人和我有同样的疑问:
runtime里面挖一挖 根据这个结构体我看不出它竟然可以当做数组用??????
解答:Method *methodList = class_copyMethodList(cls, &count);
这个返回值是一个指针,我们知道数据是可以根据下标来取值的,指针跟数组一样,可以像数组一样访问.
typedef struct method_t *Method; Method的定义
struct method_t { 结构体Method的定义
SEL name;
const char *types;
MethodListIMP imp;
...
}
打印MJStudent这个类
- (void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event{
Class metaCls = object_getClass([MJStudent class]);
[self printAllMethodWithCategory:metaCls];
}
结果如下
2020-04-29 16:36:42.378382+0800 categoryTest[64560:2772078] initialize, load, initialize, load,
有两个load方法 和 initialize,也就是说前面是分类的,后面是元类的,
+load方法在代码初始化的时候由操作系统主动调用,只调用一次;
+initialize方法在当前类给它第一次发送消息的时候调用,如果子类父类同样实现,则会调用多次;
2020-04-29 15:07:08.739100+0800 categoryTest[64560:2772078] --+[MJPerson load]--
2020-04-29 15:07:08.739958+0800 categoryTest[64560:2772078] --+[MJStudent load]--
2020-04-29 15:07:08.740078+0800 categoryTest[64560:2772078] --+[MJStudent(Test) load]--
2020-04-29 15:07:12.276934+0800 categoryTest[64560:2772078] --+[MJPerson initialize]--
2020-04-29 15:07:12.277105+0800 categoryTest[64560:2772078] --+[MJStudent(Test) initialize]--
通过源码也可以验证,以下我直接贴出位置,方便读者验证,在objc-loadmethod.mm
文件,可以发现是直接调用方法,
+load.
204: (*load_method)(cls, @selector(load));
load方法最初由call_load_methods
调起,这个方法的注释比较重要,贴出来
* call_load_methods
* Call all pending class and category +load methods.
* Class +load methods are called superclass-first.
* Category +load methods are not called until after the parent class's
- initialize(在
objc-initialize.mm
这个文件,可以看到是通过objc_msgSend,缓存查找->子类查找-> 父类查找->元类查找->...)
382:
void callInitialize(Class cls)
{
((void(*)(Class, SEL))objc_msgSend)(cls, @selector(initialize));
asm("");
}
五. 在打印方法的时候把objc_getClass
和object_getClass
傻傻分不清楚,查看网上注释如下,
1:Class objc_getClass(const chat *aClassName)
1> 传入字符串类名
2> 返回对应的类对象
2. Class object_getClass(id obj)
1> 传入的obj可能是instance对象,class对象、meta-class对象
2> 返回值
a:如果是instance对象,返回class对象
b:如果是class对象,返回meta-class对象
c:如果是meta-class对象,返回NSObject(基类)的meta-class对象
3:- (class)class、+(class)class
1>返回的就是类对象
不如自己主动验证一波,反正有源码--------->下面直接贴结果
objc-runtime.mm
objc_getClass,下面这个就是返回的Class,继续跟进,
6843 : result = getClassExceptSomeSwift(name);
1607: Class result = getClass_impl(name);
1590: // Try runtime-allocated table
Class result = (Class)NXMapGet(gdb_objc_realized_classes, name);
跟进到最后: 通过一个table表,去查,
没有找到特别有说服力的证据,但是这个方法设定是,传一个字符串,返回对应的类,如果传入一个不存在的类,返回null,各位看官可以自行验证.
if (isEqual(table, pair->key, key)) {
*value = (void *)pair->value;
return (void *)pair->key;
在objc-class.mm
文件
逻辑: 传入类对象->返回类,传入类->返回元类,传入元类->返回NSObject,通过isa指针往上层找
179:
Class object_getClass(id obj)
{
if (obj) return obj->getIsa();
else return Nil;
}
这个Method 接收一个数组methodList,怎么methodList[I]取出来,还是一个Method( 禁止套娃 )
Method *methodList = class_copyMethodList(cls, &count);
for (int i = 0; i<count; i++) {
Method method = methodList[I];
而class_copyMethodList()这个方法列表的实现:-->把重要代码贴出来,依然困惑,
Method *class_copyMethodList(Class cls, unsigned int *outCount){
old_method_list *mlist;
Method *result = nil;
...
if (count > 0) {
result = (Method *)malloc((count+1) * sizeof(Method));
iterator = nil;
while ((mlist = nextMethodList(cls, &iterator))) {
int i;
for (i = 0; i < mlist->method_count; i++) {
result[m++] = (Method)&mlist->method_list[I]; 这似乎是个数组
}
}
}
...
return result;
}
总结
----最近脑子里时长想到一部电视剧的画面--<<宝莲灯>>
三圣母的儿子沉香 和 他的舅舅 二郎神 打架时候的场景,沉香一心救母,苦练武技,觉得自己已经很刻苦,但是在二郎神面前不堪一击,边打二郎神边对沉香说,你老是觉得自己厉害,每次练功不尽全力,觉得差一点点没大碍,跟我过招每次都差一点点,所以你永远都救不了你妈.
做事儿,搞技术也是一样,不管是多小的点,动脑子搞清楚,别人家说什么是什么,最好自己验证,拓展,这样才能成为你的东西,总而言之一句话,事无巨细,认真对待,在你的能力范围内做到最好.
[找到了视频](https://b23.tv/Zfgj5G)
网友评论