Objective-C对象模型
isa指针
Objective-C是一门面向对象的编程语言,每一个对象都是一个实例。在其内部,每一个对象都有一个名为isa的指针,指向该对象的类。 NSObject其实就是一个包含isa指针的结构体。查看NSObject的class:
@interface NSObject <NSObject> {
#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Wobjc-interface-ivars"
Class isa OBJC_ISA_AVAILABILITY;
#pragma clang diagnostic pop
}
再点runtime的头文件看看runtime的class,也是一个包含isa指针的结构体:
struct objc_class {
Class _Nonnull isa OBJC_ISA_AVAILABILITY;
#if !__OBJC2__
Class _Nullable super_class OBJC2_UNAVAILABLE;
const char * _Nonnull name OBJC2_UNAVAILABLE;
long version OBJC2_UNAVAILABLE;
long info OBJC2_UNAVAILABLE;
long instance_size OBJC2_UNAVAILABLE;
struct objc_ivar_list * _Nullable ivars OBJC2_UNAVAILABLE;
struct objc_method_list * _Nullable * _Nullable methodLists OBJC2_UNAVAILABLE;
struct objc_cache * _Nonnull cache OBJC2_UNAVAILABLE;
struct objc_protocol_list * _Nullable protocols OBJC2_UNAVAILABLE;
#endif
} OBJC2_UNAVAILABLE;
其中除了isa外还有其他成员变量,但那时为了兼容非objective-c 2.0版的遗留逻辑,现在可以忽视。
因为类也是一个对象,所以它也必须是另一个类的实例,这个类就是元类。元类保存了类方法的列表,当一个类方法被调用时,元类首先查找它本身是否有该类方法的实现,如果没有,则该元类回向它的父类查找该方法,这样可以一直找到继承链的头。
元类也是一个对象,那么元类的isa指针又指向哪里呢?为了设计上的完整,所有的元类isa指针都会指向一个根元类。根元类本身的isa指向自己,这样就形成了一个闭环。上面提到,一个对象能够接收消息列表是保存在它所对应的类中的。在实际编程中,我们几乎不会遇到向元类发送消息的情况,那它的isa指针在实际上很少用到。不过这么设计保证了面向对象概念在OC语言中的完整,即语言中所有的事物都是对象,都有isa指针。
类的成员变量
如果把类的实例看成一个C语言的结构体,上面说的isa指针就是这个结构体的第一个成员变量,而类的其它成员变量依次排列在结构体中。排列顺序如下图所示:
1528185836680.jpg
对象模型的应用
动态创建对象
我们可以使用OC语言提供的tuntime函数,动态的创业一个新的类,并且通过相关的方法来获得isa指针的值,从而了解对象的内部结构
我们先来看动态创建类的代码:
#import "ViewController.h"
#import <objc/runtime.h>
@interface ViewController ()
@end
@implementation ViewController
- (void)viewDidLoad {
[super viewDidLoad];
// Do any additional setup after loading the view, typically from a nib.
//创建一个CustomView的类,它是UIView的子类
Class newClass = objc_allocateClassPair([UIView class], "CustomView", 0);
//为该类增加一个名为report的方法
class_addMethod(newClass, @selector(report), (IMP)ReportFunction, "v@:");
//注册该类
objc_registerClassPair(newClass);
//创建一个CustomView的类的实例
id instanceOfNewClass = [[newClass alloc]init];
//调用report方法
[instanceOfNewClass performSelector:@selector(report)];
}
//实现report方法
void ReportFunction(id self,SEL _cmd){
NSLog(@"This object is %p",self);
NSLog(@"Class is %@,and Super is %@.",[self class],[self superclass]);
Class currentClass = [self class];
for (int i=0; i<5; i++) {
NSLog(@"following the isa pointer %d times gives %p",i,currentClass);
currentClass = object_getClass(currentClass);
}
NSLog(@"NSObject's class is %p",[NSObject class]);
NSLog(@"NSObject's meta class is %p",object_getClass([NSObject class]));
}
最终我们得到的结果如下:
2018-06-05 17:06:08.193400+0800 ThreadStuty01[882:131115] This object is 0x7ffd7ac07630
2018-06-05 17:06:08.193588+0800 ThreadStuty01[882:131115] Class is CustomView,and Super is UIView.
2018-06-05 17:06:08.193676+0800 ThreadStuty01[882:131115] following the isa pointer 0 times gives 0x6000002582a0
2018-06-05 17:06:08.193738+0800 ThreadStuty01[882:131115] following the isa pointer 1 times gives 0x600000257fa0
2018-06-05 17:06:08.193800+0800 ThreadStuty01[882:131115] following the isa pointer 2 times gives 0x1060c8e58
2018-06-05 17:06:08.194008+0800 ThreadStuty01[882:131115] following the isa pointer 3 times gives 0x1060c8e58
2018-06-05 17:06:08.194194+0800 ThreadStuty01[882:131115] following the isa pointer 4 times gives 0x1060c8e58
2018-06-05 17:06:08.194382+0800 ThreadStuty01[882:131115] NSObject's class is 0x1060c8ea8
2018-06-05 17:06:08.194601+0800 ThreadStuty01[882:131115] NSObject's meta class is 0x1060c8e58
从结果可以清晰的看到:
1.从customView对象开始,在我们连续读取了四次isa指针所指向的对象后,isa指针所指向的地址变成了0x1060c8e58,也就是我们说的NSObject元类的地址。之后我们第4次取isa指针所指向的对象时,地址仍是0x1060c8e58,这说明NSObject元类的isa指针确实是指向它自己的。
2.作为对比,我们在代码最后获取了NSObject类的isa地址,我们看到都是0x1060c8e58,这说明所有的元类对象的isa指针都是指向NSObject元类的。
系统相关API及其应用
isa swizzling的应用
系统提供的KVO的实现,就利用了动态的修改isa指针值的技术。
Method swizzling API的说明
OC提供一下API来动态替换类方法或实例方法的实现
class_replaceMethod 替换类方法的定义,当需要替换的方法有可能不存在时,考虑使用该方法,如果不存在,会调用class_addMethod来为该类增加一个新方法。
method_exchangeImplementations 交换两个方法的实现
method_setImplementation 设置一个方法的实现方式。
实际应用如下,例如为viewWillAppear:设置为view的drawRect实现方法
Class class = object_getClass((id)self);
SEL will = @selector(viewWillAppear:);
SEL draw = @selector(drawRect:);
Method oldMethod =class_getClassMethod(class, will);
Method newMethod = class_getInstanceMethod([UIView class], draw);
//替换方法
class_replaceMethod(class, will, method_getImplementation(draw), method_getTypeEncoding(draw));
//交换方法
method_exchangeImplementations(oldMethod, newMethod);
//设置方法实现
method_setImplementation(oldMethod, method_getImplementation(newMethod));
//添加方法
class_addMethod(class, will, method_getImplementation(draw), method_getTypeEncoding(draw));
以上方法在一些第三方开源库中都有大量使用,大家有空可以去看下,使其能熟练应用到自己的开发工作中。












网友评论