objc_方法查找流程
1. 窥探 struct objc_class 的结构
struct objc_class {
Class _Nonnull isa OBJC_ISA_AVAILABILITY;
}
struct objc_class : objc_object {
// Class ISA;
Class superclass;
cache_t cache; // formerly cache pointer and vtable
class_data_bits_t bits; // class_rw_t * plus custom rr/alloc flags
}
struct class_rw_t {
// Be warned that Symbolication knows the layout of this structure.
uint32_t flags;
uint16_t version;
uint16_t witness;
const class_ro_t *ro;
method_array_t methods; //方法列表
property_array_t properties;//属性列表
protocol_array_t protocols; //协议列表
Class firstSubclass;
Class nextSiblingClass;
char *demangledName;
}
struct class_ro_t {
uint32_t flags;
uint32_t instanceStart;
uint32_t instanceSize; // 当前对象占用的内存大小
#ifdef __LP64__
uint32_t reserved;
#endif
const uint8_t * ivarLayout;
const char * name; // 类的名字
method_list_t * baseMethodList;
protocol_list_t * baseProtocols;
const ivar_list_t * ivars; // 成员变量列表
const uint8_t * weakIvarLayout;
property_list_t *baseProperties;
}
这个可以在runtime objc 的Demo 通过过lldb 获取 方法列表,属性列表,协议列表等 那么在正常的项目中怎么获取方法列表呢?我们可以通过Apple 提供的 Runtime API 来获取
void lgObjc_Debug(Class pClass){
unsigned int count = 0;
Ivar *ivars = class_copyIvarList(pClass, &count);
NSLog(@"class_copyIvarList: %@ ivarCount = %u", NSStringFromClass(pClass), count);
for (unsigned int i=0; i < count; i++) {
Ivar const ivar = ivars[i];
//获取实例变量名
const char*cName = ivar_getName(ivar);
NSString *ivarName = [NSString stringWithUTF8String:cName];
NSLog(@"class_copyIvarList:%@",ivarName);
}
free(ivars);
unsigned int pCount = 0;
objc_property_t *properties = class_copyPropertyList(pClass, &pCount);
NSLog(@"class_copyPropertyList: %@ propertiesCount = %u", NSStringFromClass(pClass), pCount);
for (unsigned int i=0; i < pCount; i++) {
objc_property_t const property = properties[i];
//获取属性名
NSString *propertyName = [NSString stringWithUTF8String:property_getName(property)];
//获取属性值
NSLog(@"class_copyProperiesList:%@",propertyName);
}
free(properties);
unsigned int mCount = 0;
Method *methods = class_copyMethodList(pClass, &mCount);
NSLog(@"class_copyMethodList: %@ methodsCount = %u", NSStringFromClass(pClass), mCount);
for (unsigned int i=0; i<mCount; i++) {
Method method = methods[i];
SEL sel = method_getName(method);
const char *encoding = method_getTypeEncoding(method);
NSLog(@"class_copyMethodList:%@(%s)",NSStringFromSelector(sel), encoding);
}
unsigned int protocolCount = 0;
Protocol * __unsafe_unretained * protocols = class_copyProtocolList(pClass, &protocolCount);
NSLog(@"class_copyProtocolList: %@ protocolCount = %u", NSStringFromClass(pClass), protocolCount);
for (unsigned int i=0; i<protocolCount; i++) {
Protocol *proto = protocols[i];
const char *name = protocol_getName(proto);
NSLog(@"class_copyProtocolList:%s",name);
}
}
@interface Person : NSObject <NSCopying>
{
NSString *hobby;
NSObject *objc;
}
@property (nonatomic, copy) NSString *nickName;
@property (nonatomic, strong) NSString *name;
+ (void)test;
@end
@implementation Person
+ (void) test {
NSLog(@"%s", __func__);
}
@end
int main(int argc, const char * argv[]) {
@autoreleasepool {
//1. 获取 类对象 的成员变量和属性
lgObjc_Debug([Person class]);
NSLog(@"————————————————————————————————————————");
//2. 获取 元类对象 的成员变量和属性
lgObjc_Debug(object_getClass([Person class]));
}
return 0;
}
// output:
class_copyIvarList: LGPerson ivarCount = 4
class_copyIvarList:hobby
class_copyIvarList:objc
class_copyIvarList:_nickName
class_copyIvarList:_name
class_copyPropertyList: LGPerson propertiesCount = 2
class_copyProperiesList:nickName
class_copyProperiesList:name
class_copyMethodList: LGPerson methodsCount = 5
class_copyMethodList:.cxx_destruct(v16@0:8)
class_copyMethodList:name(@16@0:8)
class_copyMethodList:setName:(v24@0:8@16)
class_copyMethodList:setNickName:(v24@0:8@16)
class_copyMethodList:nickName(@16@0:8)
class_copyProtocolList: LGPerson protocolCount = 1
class_copyProtocolList:NSCopying
————————————————————————————————————————
class_copyIvarList: LGPerson ivarCount = 0
class_copyPropertyList: LGPerson propertiesCount = 0
class_copyMethodList: LGPerson methodsCount = 1
class_copyMethodList:test(v16@0:8)
class_copyProtocolList: LGPerson protocolCount = 1
class_copyProtocolList:NSCopying
2. 对象方法和类方法的实际调用流程
3. SEL 和 IMP
SEL:方便标号,在不同类中相同的函数名字的编号是一样 IMP: 函数指针 通过Runtime API ,通过SEL 可以获取到 IMP 函数指针
4. 理解 Set 方法 的两种形式
@interface Person : NSObject
@property (nonatomic, strong) NSString* name;
@property (nonatomic, assign) NSInteger age;
@property (nonatomic, copy) NSString *nickName;
- (void)test;
@end
通过 编译 生成了以下几个方法
// 1. test 方法
static void _C_Person_test(Class self, SEL _cmd) {
NSLog((NSString *)&self,__func__);
}
// 2. name(属性声明:strong) get 方法 ,通过 self + offset 直接读取
static NSString * _I_Person_name(Person * self, SEL _cmd) {
return (*(NSString **)((char *)self + OBJC_IVAR_$_Person$_name));
}
// 3. name(属性声明:strong) set 方法, 通过 self + offset 直接赋值
static void _I_Person_setName_(Person * self, SEL _cmd, NSString *name) {
(*(NSString **)((char *)self + OBJC_IVAR_$_Person$_name)) = name;
}
// 4. age(属性声明:assign) get 方法 ,通过 self + offset 直接读取
static NSInteger _I_Person_age(Person * self, SEL _cmd) {
return (*(NSInteger *)((char *)self + OBJC_IVAR_$_Person$_age));
}
// 5. age(属性声明:assign) set 方法, 通过 self + offset 直接赋值
static void _I_Person_setAge_(Person * self, SEL _cmd, NSInteger age) {
(*(NSInteger *)((char *)self + OBJC_IVAR_$_Person$_age)) = age;
}
// 6. nickname(属性声明:copy) get 方法,通过 self + offset 直接读取
static NSString * _I_Person_nickName(Person * self, SEL _cmd) {
return (*(NSString **)((char *)self + OBJC_IVAR_$_Person$_nickName));
}
extern "C"void objc_setProperty (id, SEL, long, id, bool, bool);
// 6. nickname(属性声明:copy) get 方法,通过 调用 objc_setProperty 方法
static void _I_Person_setNickName_(Person * self, SEL _cmd, NSString *nickName) {
objc_setProperty (self, _cmd, __OFFSETOFIVAR__(struct Person, _nickName), (id)nickName, 0, 1);
}
5. MachO View 工具介绍
通过MachO软件查看编译生成的可执行文件里面类和方法的存储
在Data段 可以看到我们定义的类
在Function-Starts可以看到所有的方法
这个只针对自己编译后的可执行文件或者已经脱壳的可执行文件 测试发现,只有用到的类,它才会存在在可执行文件。
6. method_getImplementation 实现
// 1. 获取所有的类方法
Method class_getClassMethod(Class cls, SEL sel)
{
if (!cls || !sel) return nil;
// 传入类是元类对象
return class_getInstanceMethod(cls->getMeta(), sel);
}
// 2. 获取所有的实例方法
Method class_getInstanceMethod(Class cls, SEL sel)
{
if (!cls || !sel) return nil;
return _class_getMethod(cls, sel);
}
// 3. 从当前类寻找,如果没有递归从父类寻找
static method_t * getMethod_nolock(Class cls, SEL sel)
{
method_t *m = nil;
while (cls && ((m = getMethodNoSuper_nolock(cls, sel))) == nil) {
cls = cls->superclass;
}
return m;
}
// 4. 遍历当前类的所有方法 cls->data()->methods
static method_t * getMethodNoSuper_nolock(Class cls, SEL sel)
{
for (auto mlists = cls->data()->methods.beginLists(),
end = cls->data()->methods.endLists();
mlists != end;
++mlists)
{
method_t *m = search_method_list_inline(*mlists, sel);
if (m) return m;
}
return nil;
}
// 5. 怎么匹配 方法SEL, 用到 二分查找 提高查询速度
typedef unsigned long uintptr_t;
static method_t * findMethodInSortedMethodList(SEL key, const method_list_t *list)
{
const method_t * const first = &list->first;
const method_t *base = first;
const method_t *probe;
uintptr_t keyValue = (uintptr_t)key;
uint32_t count;
for (count = list->count; count != 0; count >>= 1) {
probe = base + (count >> 1);
uintptr_t probeValue = (uintptr_t)probe->name;
if (keyValue == probeValue) {
// `probe` is a match.
// Rewind looking for the *first* occurrence of this value.
// This is required for correct category overrides.
while (probe > first && keyValue == (uintptr_t)probe[-1].name)
{
probe--;
}
return (method_t *)probe;
}
if (keyValue > probeValue) {
base = probe + 1;
count--;
}
}
return nil;
}
大家看一道面试题
@interface Person : NSObject
- (void)instanceMethod;
+ (void)classMethod;
@end
int main(int argc, const char * argv[]) {
@autoreleasepool {
Person *person = [Person alloc];
Class pClass = object_getClass(person);
{
const char *className = class_getName(pClass);
Class metaClass = objc_getMetaClass(className);
Method method1 = class_getInstanceMethod(pClass, @selector(instanceMethod));
Method method2 = class_getInstanceMethod(metaClass, @selector(instanceMethod));
Method method3 = class_getInstanceMethod(pClass, @selector(classMethod));
Method method4 = class_getInstanceMethod(metaClass, @selector(classMethod));
NSLog(@"class_getInstanceMethod-> %p-%p-%p-%p",method1,method2,method3,method4);
// 0x1000031b0-0x0-0x0-0x100003148
}
{
const char *className = class_getName(pClass);
Class metaClass = objc_getMetaClass(className);
// 获取到当前类的元类
Method method1 = class_getClassMethod(pClass, @selector(instanceMethod));
Method method2 = class_getClassMethod(metaClass, @selector(instanceMethod));
Method method3 = class_getClassMethod(pClass, @selector(classMethod));
Method method4 = class_getClassMethod(metaClass, @selector(classMethod));
NSLog(@"class_getClassMethod->%p-%p-%p-%p",method1,method2,method3,method4);
// 0x0-0x0-0x100003148-0x100003148
}
{
const char *className = class_getName(pClass);
Class metaClass = objc_getMetaClass(className);
IMP imp1 = class_getMethodImplementation(pClass, @selector(instanceMethod));
IMP imp2 = class_getMethodImplementation(metaClass, @selector(instanceMethod));
IMP imp3 = class_getMethodImplementation(pClass, @selector(classMethod));
IMP imp4 = class_getMethodImplementation(metaClass, @selector(classMethod));
NSLog(@"class_getMethodImplementation->%p-%p-%p-%p",imp1,imp2,imp3,imp4);
}
}
return 0;
参考class_getMethodImplementation定义
The function pointer returned may be a function internal to the runtime instead of an actual method implementation. For example, if instances of the class do not respond to the selector, the function pointer returned will be part of the runtime's message forwarding machinery.
源码实现
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
if (!imp) {
return _objc_msgForward;
}
return imp;
}
8. iskindOfClass && isMemberOfClass
BOOL re1 = [(id)[NSObject class] isKindOfClass:[NSObject class]];
BOOL re2 = [(id)[NSObject class] isMemberOfClass:[NSObject class]];
BOOL re3 = [(id)[LGPerson class] isKindOfClass:[LGPerson class]];
BOOL re4 = [(id)[LGPerson class] isMemberOfClass:[LGPerson class]];
NSLog(@" re1 :%hhd\n re2 :%hhd\n re3 :%hhd\n re4 :%hhd\n",re1,re2,re3,re4);
BOOL re5 = [(id)[NSObject alloc] isKindOfClass:[NSObject class]];
BOOL re6 = [(id)[NSObject alloc] isMemberOfClass:[NSObject class]];
BOOL re7 = [(id)[LGPerson alloc] isKindOfClass:[LGPerson class]];
BOOL re8 = [(id)[LGPerson alloc] isMemberOfClass:[LGPerson class]];
NSLog(@" re5 :%hhd\n re6 :%hhd\n re7 :%hhd\n re8 :%hhd\n",re5,re6,re7,re8);
#####大家先看下isKindOfClass
的定义
Returns a Boolean value that indicates whether the receiver is an instance of given class or an instance of any class that inherits from that class.
如果receiver(就是方法调用者,就是消息的接受者)是 传入参数的类或者继承于这个类的实例对象,那么返回是真。
#####isMemberOfClass
Returns a Boolean value that indicates whether the receiver is an instance of a given class.
// 类对象 调用 class 方法返回 自己
+ (Class)class {
return self;
}
// 对象方法 调用 class 返回 类对象
- (Class)class {
return object_getClass(self);
}
// 返回父类
+ (Class)superclass {
return self->superclass;
}
- (Class)superclass {
return [self class]->superclass;
}
- (BOOL)isKindOfClass:(Class)cls {
// 1. 获取到当前类对象
// 2. 循环条件 当前类对象 不为 nil
// 3. 下一次循环,获取当前类对象的 superclass
// 4. 如果两个类对象一致,返回 YES。
for (Class tcls = [self class]; tcls; tcls = tcls->superclass) {
if (tcls == cls) return YES;
}
return NO;
}
+ (BOOL)isKindOfClass:(Class)cls {
// 1. 获取到当前 元类 对象
// 2. 循环条件 当前类对象 不为 nil
// 3. 下一次循环,获取当前类对象的 superclass
// 4. 如果两个 元类对象 一致,返回 YES。
for (Class tcls = self->ISA(); tcls; tcls = tcls->superclass) {
if (tcls == cls) return YES;
}
return NO;
}
// 类方法,比较 当前类的元类 和参数 是否一致
+ (BOOL)isMemberOfClass:(Class)cls {
return self->ISA() == cls;
}
// 比较 当前的类对象 和参数 是否一致
- (BOOL)isMemberOfClass:(Class)cls {
return [self class] == cls;
}
输入的答案是:1000 1111 答案解读:
- [NSObject class] 的 元类 的 父类 就是 [NSObject class], 所以是真
- [NSObject class] 的 元类 和 [NSObject class] 不一致,所以是假
- [LGPerson class] 的 元类 不是 LGPerson class] ,一直找到底都不是,所以是假
- [LGPerson class] 的 元类 和 [LGPerson class] 不一致,所以是假
- [NSObject alloc] 是 [NSObject class] 的实例对象
- [NSObject alloc] 的 类对象 就是 [NSObject class]
- [LGPerson alloc] 的类对象 是 [LGPerson class]
- [LGPerson alloc] 的类对象 是 [LGPerson class]