iOS OC对象的本质窥探(对象分类)(二)

上面一篇文章讲了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]);

总结:
objectMetaClass是NSObject的meta-class对象(元类对象)
每个类在内存中有且只有一个meta-class对象
meta-class对象和class对象的内存结构是一样的,但是用途不一样,在内存中存储的信息主要包括
isa指针
superclass指针
类的类方法信息(class method)

元类对象内存图示.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指针作用
    OC语言是消息机制 当执行[person personInstanceMethod]; 这行代码时 实际会被编译成 objc_msgSend(person, @selector(personInstanceMethod))进行调用,那么刚刚说到实例对象中不存储方法,那么当调用时这个对象方法是怎么获得的呢?
    答案: 当实例对象调用对象方法时是通过isa指针,指向自己的类对象,找到类对象中的方法信息,进行调用。同理,当执行 [Person personClassMethod]; 这行代码时,实例对象通过isa指针指向自己的类对象,又通过类对象中的isa指针指向元类对象,获取到类方法信息。
    图示:
    isa指针作用.png

  • 创建一个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获取手机唯一标示
iOS 高德地图实现大头针展示,分级大头针,自定制大头针,在地图上画线,线和点共存,路线规划(驾车路线规划),路线导航,等一些常见的使用场景

如有疑问请看:iOS OC对象的本质窥探(一)

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值