上面一篇文章讲了OC对象的本质,编译成C++对象是以什么形式存储的,一个对象占多少内存空间等问题,那么在OC语言里面,又分为几种对象呢?其实平时的工作中通过[[NSObject alloc] init]这种形式创建的对象都是实例对象,另外还有两类平时接触甚少的对象,一个是类对象,一个就是元类对象。
开篇引题 类对象分为三种:
实例对象
类对象
元类对象
这三中类型的对象之间是什么关系?每种类型的对象又有什么特点呢?
开始一探究竟
- 实例对象
实现以下代码
NSObject *object1 = [[NSObject alloc] init];
NSObject *object2 = [[NSObject alloc] init];
NSLog(@"%p %p",
object1,
object2);
打印:0x100648340 0x100647440
总结:不多讲了,第一篇文章已经写的很详细,
object1、object2是NSObject的instance对象(实例对象)
它们是不同的两个对象,分别占据着两块不同的内存
instance对象在内存中存储的信息包括
isa指针
其他成员变量
存储的是各个成员变量的值,还有一个isa指针,一个类在内存中的实例对象可以有多个。
内存存储图示
实例对象内存存储图示.png
- 类对象
1.获取类对象
两种方法
NSObject *object1 = [[NSObject alloc] init];
Class objectClass1 = [object1 class];
还可以通过一个runtime函数
NSObject *object1 = [[NSObject alloc] init];
Class objectClass3 = object_getClass(object1);
object_getClass 函数:传入实例对象返回类对象,传入类对象,返回元类对象
下面实现以下代码
NSObject *object1 = [[NSObject alloc] init];
NSObject *object2 = [[NSObject alloc] init];
Class objectClass1 = [object1 class];
Class objectClass2 = [object2 class];
Class objectClass3 = object_getClass(object1);
Class objectClass4 = object_getClass(object2);
Class objectClass5 = [NSObject class];
NSLog(@"%p %p",
object1,
object2);
NSLog(@"%p %p %p %p %p",
objectClass1,
objectClass2,
objectClass3,
objectClass4,
objectClass5);
打印:0x100648340 0x100647440
0x7fff9dab4118 0x7fff9dab4118 0x7fff9dab4118 0x7fff9dab4118 0x7fff9dab4118
会发现两个实例对象的内存地址是不一样的,而通过两个实例对象获得的5个类对象的指针都是一样的,说明,一个类在内存中只有一个类对象。
总结:
objectClass1 ~ objectClass5都是NSObject的class对象(类对象)
它们是同一个对象。每个类在内存中有且只有一个class对象
class对象在内存中存储的信息主要包括
isa指针
superclass指针
类的属性信息(@property)、类的对象方法信息(instance method)
类的协议信息(protocol)、类的成员变量信息(ivar)
......
内存存储图示
类对象内存存储图示.png
- 元类对象
执行以下代码获取元类对象
// object_getClass 函数:传入实例对象返回类对象,传入类对象,返回元类对象
Class objectClass3 = object_getClass([NSObject class]);
元类对象内存图示.png总结:
objectMetaClass是NSObject的meta-class对象(元类对象)
每个类在内存中有且只有一个meta-class对象
meta-class对象和class对象的内存结构是一样的,但是用途不一样,在内存中存储的信息主要包括
isa指针
superclass指针
类的类方法信息(class method)
......
关于如何证明刚刚所总结的三种类型的对象分别存储的什么信息,通过阅读苹果源码可知
- 简单图示
WeChat8f2ef0b9f9040368bb61aece28b4bb6b.png
想要更深层次的了解类的本质还是需要自己去阅读源码
分析:实例对象,类对象和元类对象中都有isa指针,而类对象和元类对象中除了含有isa指针之外还有superclass指针,问题:isa指针和superclass指针分别有什么作用呢?
- 分析 创建一个Person类给person类分别声明一个对象方法和一个类方法
// Person
@interface Person : NSObject <NSCopying>
{
@public
int _age;
}
@property (nonatomic, assign) int no;
- (void)personInstanceMethod;
+ (void)personClassMethod;
@end
@implementation Person
- (void)personInstanceMethod{
}
+ (void)personClassMethod{
}
@end
// 调用
Person *person = [[Person alloc] init];
[person personInstanceMethod];
[Person personClassMethod];
-
isa指针作用
isa指针作用.png
OC语言是消息机制 当执行[person personInstanceMethod]; 这行代码时 实际会被编译成 objc_msgSend(person, @selector(personInstanceMethod))进行调用,那么刚刚说到实例对象中不存储方法,那么当调用时这个对象方法是怎么获得的呢?
答案: 当实例对象调用对象方法时是通过isa指针,指向自己的类对象,找到类对象中的方法信息,进行调用。同理,当执行 [Person personClassMethod]; 这行代码时,实例对象通过isa指针指向自己的类对象,又通过类对象中的isa指针指向元类对象,获取到类方法信息。
图示:
-
创建一个Person类给person类分别声明一个对象方法和一个类方法,再创建一个继承于Person类的Student类给Student类分别声明一个对象方法和一个类方法
// Person
@interface Person : NSObject <NSCopying>
{
@public
int _age;
}
- (void)personInstanceMethod;
+ (void)personClassMethod;
@end
@implementation Person
- (void)personInstanceMethod
{
}
+ (void)personClassMethod
{
}
@end
// Student
@interface Student : Person <NSCoding>
{
@public
int _weight;
}
- (void)studentInstanceMethod;
+ (void)studentClassMethod;
@end
@implementation MJStudent
- (void)studentInstanceMethod
{
}
+ (void)studentClassMethod
{
}
@end
// 调用
Student *student = [[Student alloc] init];
[student personInstanceMethod];
[Student personClassMethod];
- superclass指针作用
总结:当执行[student personInstanceMethod]这行代码时 student对象通过isa指针找到自己类对象,结果发现类对象中没有personInstanceMethod这个方法,然后利用superclass指针找到父类也就是Person的类对象,查看是否有 personInstanceMethod 方法信息,如果有 就调用,没有的话继续向上查找。同理:当执行[Student personClassMethod]; 这行代码时,student对象通过isa指针找到自己类对象,又通过类对象的isa指针找到自己的元类对象结果发现元类对象中没有personClassMethod这个方法,元类对象利用superclass指针向上逐级寻找父类的元类对象是否有此方法。直到NSobject停止,如果找到就调用,找不到就会奔溃 unrecognized selector sent to class 0x1000011a0'
图示:
superclass指针作用.png
最后总结:
instance -> 实例对象,class -> 类对象,meta-class -> 元类对象
1.instance的isa指向class
2.class的isa指向meta-class
3.meta-class的isa指向基类的meta-class
4.class的superclass指向父类的class
5.如果没有父类,superclass指针为nil
6.meta-class的superclass指向父类的meta-class
7.基类的meta-class的superclass指向基类的class
8.instance调用对象方法的轨迹
9.isa找到class,方法不存在,就通过superclass找父类
10.class调用类方法的轨迹
11.isa找meta-class,方法不存在,就通过superclass找父类
图示:
WeChataf27bd994cb5722150a32f853afae3b5.png
-
画一下[student personInstanceMethod]这行代码的执行顺序
[student personInstanceMethod]执行顺序.png -
画一下[Student load]这行代码的执行顺序
[Student load]执行顺序.png
疑问???类方法再调用时先去自己的元类对象中寻找,如果没有就去父类的元类对象中寻找,在没有就去NSObject的元类对象中寻找,再就去NSObject的类对象中寻找(根据最上面的线可以看来)那是这么回事吗?下面来验证一下
//新建一个Car类,继承自NSObject
@interface Car : NSObject
@end
@implementation Car
@end
// 创建一个NSObject的类别
//.h中代码
+ (void)test;
//.m中代码
+ (void)test{
NSLog(@"+[NSObject test] - %p", self);
}
//调用代码
[Car test];
输出 :[Car class] - 0x100001220
别的代码都不变,将类别中.m中的代码改成
//+号变成-号
- (void)test{
NSLog(@"+[NSObject test] - %p", self);
}
输出 :[Car class] - 0x100001220
再调用发现还是可以成功,具体的原因吧,我也说的不是很好,就不误导别人了,如果有大神知晓,欢迎指正
如有疑问欢迎指正~
强烈推荐:
iOS OC对象的本质窥探(一)
iOS获取手机唯一标示
iOS 高德地图实现大头针展示,分级大头针,自定制大头针,在地图上画线,线和点共存,路线规划(驾车路线规划),路线导航,等一些常见的使用场景
网友评论