iOS 底层探索:类中方法的举例分析
文集:iOS 底层探索之路
前言
- 这篇主要内容是通过面试题举例分析类的结构。
准备工作
一 、例子1:
#import <Foundation/Foundation.h>
#import <objc/runtime.h>
#ifdef DEBUG
#define LGLog(format, ...) printf("%s\n", [[NSString stringWithFormat:format, ## __VA_ARGS__] UTF8String]);
#else
#define LGLog(format, ...);
#endif
@interface LGPerson : NSObject
- (void)sayHello;
+ (void)sayHappy;
@end
@implementation LGPerson
- (void)sayHello{
LGLog(@"LGPerson say : Hello!!!");
}
+ (void)sayHappy{
LGLog(@"LGPerson say : Happy!!!");
}
@end
void lgObjc_copyMethodList(Class pClass){
unsigned int count = 0;
Method *methods = class_copyMethodList(pClass, &count);
for (unsigned int i=0; i < count; i++) {
Method const method = methods[I];
//获取方法名
NSString *key = NSStringFromSelector(method_getName(method));
LGLog(@"打印1-Method, name: %@", key);
}
free(methods);
}
void lgInstanceMethod_classToMetaclass(Class pClass){
const char *className = class_getName(pClass);
Class metaClass = objc_getMetaClass(className);
Method method1 = class_getInstanceMethod(pClass, @selector(sayHello)); // 1
Method method2 = class_getInstanceMethod(metaClass, @selector(sayHello)); // 0
Method method3 = class_getInstanceMethod(pClass, @selector(sayHappy)); // 0
Method method4 = class_getInstanceMethod(metaClass, @selector(sayHappy));// 1
LGLog(@"打印2 - %s===%p-%p-%p-%p",__func__,method1,method2,method3,method4);
}
void lgClassMethod_classToMetaclass(Class pClass){
const char *className = class_getName(pClass);
Class metaClass = objc_getMetaClass(className);
Method method1 = class_getClassMethod(pClass, @selector(sayHello));
Method method2 = class_getClassMethod(metaClass, @selector(sayHello));
Method method3 = class_getClassMethod(pClass, @selector(sayHappy));
Method method4 = class_getClassMethod(metaClass, @selector(sayHappy));
LGLog(@"打印3 -%s===%p-%p-%p-%p",__func__,method1,method2,method3,method4);
}
void lgIMP_classToMetaclass(Class pClass){
const char *className = class_getName(pClass);
Class metaClass = objc_getMetaClass(className);
IMP imp1 = class_getMethodImplementation(pClass, @selector(sayHello));
IMP imp2 = class_getMethodImplementation(metaClass, @selector(sayHello));
IMP imp3 = class_getMethodImplementation(pClass, @selector(sayHappy));
IMP imp4 = class_getMethodImplementation(metaClass, @selector(sayHappy));
LGLog(@"打印4:%s -%p-%p-%p-%p",__func__,imp1,imp2,imp3,imp4);
}
int main(int argc, const char * argv[]) {
@autoreleasepool
{
LGPerson *person = [LGPerson alloc];
Class pClass = object_getClass(person);
//执行1
lgObjc_copyMethodList(pClass);
//执行2
lgInstanceMethod_classToMetaclass(pClass);
//执行3
lgClassMethod_classToMetaclass(pClass);
//执行4
lgIMP_classToMetaclass(pClass);
}
return 0;
}
问题:打印输出什么?
方法注释:
-
IMP
:就是Implementation(实现)的缩写,它是指向一个方法实现的指针,每一个方法都有一个对应的IMP -
Method
:方法的结构体; -
class_copyMethodList
:函数传入元类作参数时,获取到的就是该类的所有类方法;函数传入类作参数时,获取一个类的所有实例方法; -
method_getName
和sel_getName
: 获取方法名称。 -
object_getClass
:根据参数类型返回对象 -
objc_getMetaClass
: 总是返回元类对象; -
class_getName
:获取这个类的全类名; -
class_getInstanceMethod
:获取到类的实例方法; -
class_getClassMethod
:获取到类的类方法; -
class_getMethodImplementation
:获取方法的实现指针
分析:
LGPerson *person = [LGPerson alloc];
Class pClass = object_getClass(person);
pClass
:这里是person
对象的类LGPerson
-(void)sayHello; //对象方法 存在类中
+(void)sayHappy; //类方法 存在元类中
执行1
void lgObjc_copyMethodList(Class pClass){
unsigned int count = 2;
//获取类中方法列表
Method *methods = class_copyMethodList(pClass, &count);
//&count是count的指针 传进class_copyMethodList方法中 只找到一个对象方法赋值 count = 1
for (unsigned int i=0; i < count; i++) {
Method const method = methods[I];
//类中获取方法名 只能是对象方法sayHello
NSString *key = NSStringFromSelector(method_getName(method));
//所以只打印sayHello
LGLog(@"Method, name: %@", key);
}
free(methods);
}
结果: sayHello
执行2
void lgInstanceMethod_classToMetaclass(Class pClass){
//class_getName 获得到的className 是LGPerson类
const char *className = class_getName(pClass);
//objc_getMetaClass 获得到的metaClass 是LGPerson的元类
Class metaClass = objc_getMetaClass(className);
//获取到类的实例方法sayHello的对象是存在的 所以打印sayHello方法的地址
Method method1 = class_getInstanceMethod(pClass, @selector(sayHello));
//获取到元类的实例方法sayHello的对象是不存在的 所以打印0x0
Method method2 = class_getInstanceMethod(metaClass, @selector(sayHello));
//获取到类的类方法sayHello的对象是不存在的 所以打印0x0
Method method3 = class_getInstanceMethod(pClass, @selector(sayHappy));
//获取到元类的类方法sayHello的对象是存在的 所以打印sayHappy方法地址
Method method4 = class_getInstanceMethod(metaClass, @selector(sayHappy));
LGLog(@"%s - %p-%p-%p-%p",__func__,method1,method2,method3,method4);
}
结果:
lgInstanceMethod_classToMetaclass
-0x1000031b0
-0x0
-0x0
-0x100003148
执行3
void lgClassMethod_classToMetaclass(Class pClass){
const char *className = class_getName(pClass);
Class metaClass = objc_getMetaClass(className);
//`class_getClassMethod`:获取到类的类方法;
//LGPerson类中获取不到类方法所以打印 0x0
Method method1 = class_getClassMethod(pClass, @selector(sayHello));
//元类中不存在实例方法所以打印 0x0
Method method2 = class_getClassMethod(metaClass, @selector(sayHello));
//LGPerson类中获取类方法所以打印sayHappy的地址
Method method3 = class_getClassMethod(pClass, @selector(sayHappy));
//元类中获取类方法所以打印sayHappy的地址
Method method4 = class_getClassMethod(metaClass, @selector(sayHappy));
LGLog(@"打印3 -%s===%p-%p-%p-%p",__func__,method1,method2,method3,method4);
}
源码中分析:class_getClassMethod
Method class_getClassMethod(Class cls, SEL sel)
{
if (!cls || !sel) return nil;
//真实传入的是 cls的元类 ,元类对象中的方法是类方法
return class_getInstanceMethod(cls->getMeta(), sel);
}
结果:
lgClassMethod_classToMetaclass
-0x0
-0x0
-0x100003148
-0x100003148
执行3
void lgIMP_classToMetaclass(Class pClass){
const char *className = class_getName(pClass);
Class metaClass = objc_getMetaClass(className);
// LGPerson类中可以获取实例方法的实现
IMP imp1 = class_getMethodImplementation(pClass, @selector(sayHello));
IMP imp2 = class_getMethodImplementation(metaClass, @selector(sayHello));
IMP imp3 = class_getMethodImplementation(pClass, @selector(sayHappy));
IMP imp4 = class_getMethodImplementation(metaClass, @selector(sayHappy));
// 通过源码分析如下,以上全部能找到方法的imp
LGLog(@"打印4:%s -%p-%p-%p-%p",__func__,imp1,imp2,imp3,imp4);
}
源码中分析:class_getMethodImplementation
IMP object_getMethodImplementation(id obj, SEL name)
{
//这里有个递归 如果通过传入的obj 和 name在class_getMethodImplementation方法中找不到imp ,
//就调用obj->getIsa() 根据isa的指向去寻找obj的元类,根元类,根根元类,根根元类自己。
//_objc_msgForward 是存在汇编里的 类的所有信息都存在汇编中
//而汇编中不区分类方法和对象方法,所以在汇编中总能找到对应的方法imp,
//因此调用这个方法必然能找到对应的imp。
// 所以以上全部能找到方法的imp
Class cls = (obj ? obj->getIsa() : nil);
return class_getMethodImplementation(cls, name);
}
IMP class_getMethodImplementation(Class cls, SEL sel)
{
IMP imp;
if (!cls || !sel) return nil;
imp = lookUpImpOrNil(nil, sel, cls, LOOKUP_INITIALIZE | LOOKUP_RESOLVER);
if (!imp) {
return _objc_msgForward;
}
return imp;
}
OBJC_EXPORT void
_objc_msgForward(void /* id receiver, SEL sel, ... */ )
OBJC_AVAILABLE(10.0, 2.0, 9.0, 1.0, 2.0);
结果:
lgIMP_classToMetaclass
-0x100000e10
-0x1002c2440
-0x1002c2440
-0x100000de0
提出疑问:为什么 imp2 imp3打印的结果都是 -0x7fff6c09c580?
通过源码配合lldb 调试分析如下:
总结:lgIMP_classToMetaclass
在系统底层通过_objc_msgForward
做了消息转发
的处理。
这个例子的目的:
- 1.考察实例方法和类方法的存在
- 2.考察isa流程图
二 、例子2:isKindOfClass 和 isMemberOfClass
#import <Foundation/Foundation.h>
#import <objc/runtime.h>
@interface LGPerson : NSObject
@end
@implementation LGPerson
@end
int main(int argc, const char * argv[]) {
@autoreleasepool {
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(@"\n 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(@"\n re5 :%hhd\n re6 :%hhd\n re7 :%hhd\n re8 :%hhd\n",re5,re6,re7,re8);
}
return 0;
}
问题:打印输出什么?
源码解析:
类方法调用
:查看isa流程 如果isa 指向cls 则为真
+ (BOOL)isMemberOfClass:(Class)cls {
return self->ISA() == cls;
}
对象方法调用
:查看[self class],如果两个类是一样的则为真
- (BOOL)isMemberOfClass:(Class)cls {
return [self class] == cls;
}
类方法调用
:查看isa流程,从类到元类,根元类的递归对比, 如果isa 指向cls 则为真
+ (BOOL)isKindOfClass:(Class)cls {
// 类 vs 元类
// 根元类 vs NSObject
// NSObject vs NSObject
// LGPerson vs 元类 (根元类) (NSObject)
for (Class tcls = self->ISA(); tcls; tcls = tcls->superclass) {
if (tcls == cls) return YES;
}
return NO;
}
对象方法调用
:如果 [self class] 是cls 的子类或者本类就为真
- (BOOL)isKindOfClass:(Class)cls {
for (Class tcls = [self class]; tcls; tcls = tcls->superclass) {
if (tcls == cls) return YES;
}
return NO;
}
方法注释:
-
isKindOfClass:
判断是否是这个类或者这个类的子类的实例,判断的方式
:前者是后着的类或者子类的实例,则返回真 -
isMemberOfClass:
判断是否是这个类的实例,判断的方式
:前者是后者的实例对象
分析:
[NSObject class] 返回self 也就是 NSObject类
[LGPerson class] 返回self 也就是 LGPerson类
[NSObject alloc] 返回的是<NSObject: 0x10101c700> 对象
[LGPerson alloc] 返回的是<LGPerson: 0x10060b260>对象
注:
-1、2、3、4 是属于类方法的判断
分析1:
BOOL re1 = [(id)[NSObject class] isKindOfClass:[NSObject class]];
- NSObject 类在oc中属于根类,NSObject的根元类指向自己,所以这么为YES;
分析2:
BOOL re2 = [(id)[NSObject class] isMemberOfClass:[NSObject class]];
- NSObject调用类方法 isMemberOfClass的时候,NSObject元类的isa不指向NSObject 所以为NO;
分析3:
BOOL re3 = [(id)[LGPerson class] isKindOfClass:[LGPerson class]];
- LGPerson的元类 isa指向 NSObject根元类,所以为NO;
分析4:
BOOL re4 = [(id)[LGPerson class] isMemberOfClass:[LGPerson class]];
- 同3
分析5:
BOOL re3 = [(id)[LGPerson class] isKindOfClass:[LGPerson class]];
- LGPerson的元类 isa指向 NSObject根元类,所以为NO;
注:
5、6、7、8 是属于对象方法的判断
分析5、6、7、8 :
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]];
- [NSObject alloc] 是一个对象 ,对象在对象方法isKindOfClass和isMemberOfClass中的判断时,对象的类属于自己,所以5,6为YES;
- [LGPerson alloc] 是一个对象 ,判断同上也为真;
结果
1 0 0 0 1 1 1 1
一顿操作猛如虎!打起脸来啪啪啪!!
惊呆了。
进入源码断点:
+ (BOOL)isKindOfClass:(Class)cls {
// 类 vs 元类
// 根元类 vs NSObject
// NSObject vs NSObject
// LGPerson vs 元类 (根元类) (NSObject)
for (Class tcls = self->ISA(); tcls; tcls = tcls->superclass) {
if (tcls == cls) return YES;
}
return NO;
}
- (BOOL)isKindOfClass:(Class)cls {
for (Class tcls = [self class]; tcls; tcls = tcls->superclass) {
if (tcls == cls) return YES;
}
return NO;
}
这两个方法根本不走!虽然分析是对的。但是isKindOfClass
这个方法根本不执行
。
继续查看源码分析
// Calls [obj isKindOfClass]
BOOL
objc_opt_isKindOfClass(id obj, Class otherClass)
{
#if __OBJC2__
if (slowpath(!obj)) return NO;
Class cls = obj->getIsa();
if (fastpath(!cls->hasCustomCore())) {
for (Class tcls = cls; tcls; tcls = tcls->superclass) {
if (tcls == otherClass) return YES;
}
return NO;
}
#endif
return ((BOOL(*)(id, SEL, Class))objc_msgSend)(obj, @selector(isKindOfClass:), otherClass);
}
走objc_opt_isKindOfClass
这个方法 里面执行方法综合了isKindOfClas
s的对象方法和类方法。这是底层对isKindOfClass
的优化处理。
三 、总结:
读懂源码的同时需要手动断点调试,搞懂方法的本质,就很好理解比较。