一、准备工作
- objc4可编译源码,可直接跳到文章最后,下载调试好的源码
- 在源码中创建类
GomuPerson
GomuPerson.h
@interface GomuPerson : NSObject
{
//: 成员变量
NSString *hobby;
//: 特殊的成员变量,实例变量,能够被实例的对象的成员变量叫实例变量
NSObject *obj;
}
//: 创建属性
@property (nonatomic, strong) NSString *name;
@property (nonatomic, strong) NSString *sex;
//: 创建实例方法、类方法
- (void)sayNO;
+ (void)sayLove;
GomuPerson.m
//: 实现方法
- (void)sayNO{}
+ (void)sayLove{}
@end
二、通过runtime的api拿方法、属性、成员变量
2.1 通过gomu_copyMethodList
拿GomuPerson
里面的方法
//: 打印类中的所有方法
void gomu_copyMethodList(Class class){
unsigned int count = 0;
Method *methods = class_copyMethodList(class, &count);
for (unsigned int i = 0; i < count; i++) {
Method const method = methods[i];
//: 获取方法名
NSString *methodName = NSStringFromSelector(method_getName(method));
GOMULog(@"Method, name: %@",methodName);
}
free(methods);
}
//: 调用方法
GomuPerson *p = [GomuPerson alloc];
//: 获取实例方法
gomu_copyMethodList(object_getClass(p));
//: 打印
Method, name: sayNO
Method, name: sex
Method, name: setSex:
Method, name: .cxx_destruct
Method, name: name
Method, name: setName:
//: 获取类方法
gomu_copyMethodList(object_getClass([GomuPerson class]));
//: 打印
Method, name: sayLove
结论:
对象方法
存在类
中- 系统在编译中会自动
生成
属性的get
、set
方法- 系统在编译中也会
生成c++
的.cxx_destruct
方法类方法
存在元类
中
2.2 通过gomu_copyPropertyList
拿GomuPerson里面的属性
#pragma mark -- 获取属性
void gomu_copyPropertyList(Class class){
unsigned int count = 0;
objc_property_t *propertys = class_copyPropertyList(class, &count);
for (unsigned int i = 0; i < count; i++) {
objc_property_t property = propertys[i];
//: 获取属性
const char *propertyName = property_getName(property);
GOMULog(@"Property, name: %s",propertyName);
}
free(propertys);
}
//: 调用方法
GomuPerson *p = [GomuPerson alloc];
//: 获取属性
gomu_copyPropertyList(object_getClass(p));
//: 打印
Property, name: name
Property, name: sex
结论:
属性
存在PropertyList
中成员变量
不存在PropertyList
中
2.3 通过gomu_copyIvarList
拿GomuPerson里面的成员变量
#pragma mark -- 获取成员变量
void gomu_copyIvarList(Class class){
unsigned int count = 0;
Ivar *ivars = class_copyIvarList(class, &count);
for (unsigned int i = 0; i < count; i++) {
Ivar const ivar = ivars[i];
//: 获取成员变量
const char *ivarName = ivar_getName(ivar);
GOMULog(@"Ivar, name: %s",ivarName);
}
free(ivars);
}
//: 调用方法
GomuPerson *p = [GomuPerson alloc];
//: 获取属性
gomu_copyIvarList(object_getClass(p));
//: 打印
Ivar, name: hobby
Ivar, name: obj
Ivar, name: _name
Ivar, name: _sex
结论
成员变量
存在IvarList
中- 系统编译中会自动给
属性
生成带_
的成员变量
2.4 通过class_getInstanceMethod
判断对象方法和类方法的归属
void gomuInstanceMethod_classToMetaclass(Class pClass){
//: 获取元类
const char *className = class_getName(pClass);
Class metaClass = objc_getMetaClass(className);
//: 判断类中是否有对象方法`sayNO`:有
Method method1 = class_getInstanceMethod(pClass, @selector(sayNO));
//: 判断元类中是否有对象方法`sayNO`:没有
Method method2 = class_getInstanceMethod(metaClass, @selector(sayNO));
//: 判断类中是否有类方法`sayLove`的对象方法:没有
Method method3 = class_getInstanceMethod(pClass, @selector(sayLove));
//: 判断类元中是否有类方法`sayLove`的对象方法:有
Method method4 = class_getInstanceMethod(metaClass, @selector(sayLove));
GOMULog(@"%s : %p - %p - %p - %p",__func__,method1,method2,method3,method4);
GOMULog(@"%s : %p - %p - %p - %p",__func__,method1,method2,method3,method4);
//: 调用
gomuInstanceMethod_classToMetaclass(pClass);
//: 打印
gomuInstanceMethod_classToMetaclass : 0x1000031e0 - 0x0 - 0x0 - 0x100003178
}
结论:
对象方法
存在类
中类方法
存在元类
,并且以对象方法
的形式存在元类
中
2.5 通过class_getClassMethod
判断对象方法和类方法的归属
void gomuClassMethod_classToMetaclass(Class pClass){
//: 获取元类
const char *className = class_getName(pClass);
Class metaClass = objc_getMetaClass(className);
//: 判断类中是否有对象方法`sayNO`的类方法:没有
Method method1 = class_getClassMethod(pClass, @selector(sayNO));
//: 判断元类中是否有对象方法`sayNO`的类方法:没有
Method method2 = class_getClassMethod(metaClass, @selector(sayNO));
//: 判断类中是否有类方法`sayLove`:有
Method method3 = class_getClassMethod(pClass, @selector(sayLove));
//: 判断元类中是否有类方法`sayLove`:有
Method method4 = class_getClassMethod(metaClass, @selector(sayLove));
GOMULog(@"%s : %p - %p - %p - %p",__func__,method1,method2,method3,method4);
//: 调用
gomuClassMethod_classToMetaclass(pClass);
//: 打印
gomuClassMethod_classToMetaclass : 0x0 - 0x0 - 0x100003180 - 0x100003180
}
结论:
- 不管
类
还是元类
中,都不会有对象方法
的类方法
类
和元类
中,都有类方法
2.5.1 元类中为什么会有类方法,不是说类方法在元类是以对象方法的形成存在的吗,进入class_getClassMethod
内部查看源码
Method class_getClassMethod(Class cls, SEL sel)
{
if (!cls || !sel) return nil;
//: 当cls是元类,走到这里,getMeta()返回元类
//: 所以 class_getClassMethod 传入元类相当于 class_getInstanceMethod传入元类
return class_getInstanceMethod(cls->getMeta(), sel);
}
//: 如果是元类,则直接返回自己
Class getMeta() {
if (isMetaClass()) return (Class)this;
else return this->ISA();
}
结论:
- 当
class_getClassMethod
的cls
传入的是元类,则会调用class_getInstanceMethod
,执行到cls->getMeta()
返回自己。class_getClassMethod(metaClass, @selector(sayLove))
等价于class_getInstanceMethod(metaClass, @selector(sayLove));
类方法
在元类
中还是以对象方法
的方法存在
2.6 查看类方法/对象
方法在类/元类
中的IMP
void gomuIMP_classToMetaclass(Class pClass){
//: 获取元类
const char *className = class_getName(pClass);
Class metaClass = objc_getMetaClass(className);
//: 在类中获取对象方法`sayNO`的IMP
IMP imp1 = class_getMethodImplementation(pClass, @selector(sayNO));
//: 在元类中获取对象方法`sayNO`的IMP
IMP imp2 = class_getMethodImplementation(metaClass, @selector(sayNO));
//: 在类中获取类方法`sayLove`的IMP
IMP imp3 = class_getMethodImplementation(pClass, @selector(sayLove));
//: 在元类中获取类方法`sayLove`的IMP
IMP imp4 = class_getMethodImplementation(metaClass, @selector(sayLove));
GOMULog(@"%p - %p - %p - %p",imp1,imp2,imp3,imp4);
//: 调用
gomuIMP_classToMetaclass(pClass);
//: 打印
0x100001b90 - 0x1002c4140 - 0x1002c4140 - 0x100001b80
}
结论:
- 对象方法
sayNO
在GomuPerson
中找到了IMP
- 对象方法
sayNO
在GomuPerson
的元类中找到了IMP
- 类方法
sayLove
在GomuPerson
的中找到了IMP
- 类方法
sayLove
在GomuPerson
的元类中找到了IMP
问题:imp1,imp4都能理解,imp2,imp3怎么也有IMP?
2.6.1 第一种方式探索:查看源码
IMP class_getMethodImplementation(Class cls, SEL sel)
{
IMP imp;
if (!cls || !sel) return nil;
imp = lookUpImpOrNil(nil, sel, cls, LOOKUP_INITIALIZE | LOOKUP_RESOLVER);
// Translate forwarding function to C-callable external version
//: 当没有找到IMP,会返回一个`_objc_msgForward`
if (!imp) {
return _objc_msgForward;
}
return imp;
}
- 从源码不难看出,IMP是肯定存在的,只是有可能这个IMP可能是
_objc_msgForward
类型
2.6.2 第一种方式探索:lldb调试
//: 下断点,分别打印
(lldb) p/x imp1
(IMP) $0 = 0x0000000100001b90 (GomuTest`-[GomuPerson sayNO])
(lldb) p/x imp2
(IMP) $1 = 0x00000001002c4140 (libobjc.A.dylib`_objc_msgForward)
(lldb) p/x imp3
(IMP) $2 = 0x00000001002c4140 (libobjc.A.dylib`_objc_msgForward)
(lldb) p/x imp4
(IMP) $3 = 0x0000000100001b80 (GomuTest`+[GomuPerson sayLove])
结论:
对象方法的imp
在类
中可以拿到类方法
的imp
在元类
中可以拿到imp
不会为空,为空会默认_objc_msgForward
三、拓展知识
- imp : 函数指针地址
- sel : 方法编号