OC 对象的分类

Objective-C中的对象,简称OC对象,主要可以分为3

  • instance对象(实例对象)
  • class对象(类对象)
  • meta-class对象(元类对象)

instance

instance对象就是通过类alloc出来的对象,每次调用alloc都会产生新的instance对象

NSObject *obj1 = [[NSObject alloc] init];
NSObject *obj2 = [[NSObject alloc] init];

NSLog(@"%p", obj1);
NSLog(@"%p", obj2);

打印结果会输出

0x1004992a0
0x1004974f0

由此可以看出,obj1obj2是两个不同的对象,分别占据着两块不同的内存。

instance对象在内存中存储的信息包括

  • isa指针
  • 成员变量的值_age = 5,这个5就是存在于实例对象中的

Class 对象

查看以下代码中的内存地址

NSObject *object1 = [[NSObject alloc] init];
NSObject *object2 = [[NSObject alloc] init];

Class objectClass1 = [object1 class];
Class objectClass2 = [object2 class];
Class objectClass3 = [NSObject class];
Class objectClass4 = object_getClass(object1);
Class objectClass5 = object_getClass(object2);

在控制台调试打印地址

(lldb) p/x (long)objectClass1
(long) $2 = 0x00007fff8a20f140
(lldb) p/x (long)objectClass2
(long) $3 = 0x00007fff8a20f140
(lldb) p/x (long)objectClass3
(long) $4 = 0x00007fff8a20f140
(lldb) p/x (long)objectClass4
(long) $5 = 0x00007fff8a20f140
(lldb) p/x (long)objectClass5
(long) $6 = 0x00007fff8a20f140

经过调试可以发现5Class类指向同一个地址值0x00007fff8a20f140,它和instance对象的区别是instance对象是alloc分配的内存空间,每个实例对象都占用不同的空间,但是Class一个类只占用一份内存空间。

  • objectClass1~objectClass5都是NSObjectClass对象(类对象)
  • 它们是同一个对象,每个类在内存中只有一个Class对象
  • Class对象在内存中存储的信息主要包括
    • isa指针
    • superclass指针
    • 类的属性信息(@property)
    • 类的对象方法信息(instance method)
    • 类的协议信息(protocol)
    • 类的成员变量信息(ivar)

meta-class

classmeta-class内存地址值比较

Class objectClass = [NSObject class];
Class objectMetaClass = object_getClass([NSObject class]);

NSLog(@"%p", objectClass);
NSLog(@"%p", objectMetaClass);
0x7fff8a20f140
0x7fff8a20f0f0

对比发现,Classmeta-class的内存地址不一样。我们可以进行如下总结 :

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

获取元类的内存地址只能通过object_getClass([NSObject class])进行获取,通过[[NSObject class] class]这种获取方法是错误的。

Class objectClass = [NSObject class];
Class objectWrongMetaClass = [[NSObject class] class];
Class objectMetaClass = object_getClass([NSObject class]);

NSLog(@"%p", objectClass);        
NSLog(@"%p", objectWrongMetaClass);
NSLog(@"%p", objectMetaClass);
0x7fff8a20f140
0x7fff8a20f140
0x7fff8a20f0f0

OC 的类信息存放在哪里?

  • 成员变量具体的值,放在instance(实例对象)里。
  • 属性信息、对象方法信息、协议信息、成员变量信息,放在Class(类对象)里。
  • 类方法信息,放在meta-class(元类对象)里。

对象的 isa 指针指向哪里?

根据上面可知,对象方法存储在class的内存里,类方法存在于meta-class内存里。问题来了,假如现在有一个Person类的实例化对象p1,如果想用p1调用Person类的对象方法personMethod该如何调用呢?毕竟,p1是存储在实例化对象instance内存中的,而personMethod方法是存储于Person类的内存中的。

实际上instance实例的对象的isa指针指向class,找到class类以后,再在class类中找存储于其中的对象方法方法进行调用。

调用类方法的过程也是如此,class类通过其内部的isa指针找到meta-class类中存储的类方法,然后再进行调用。

至此,就可以回答上面的问题了。

  • instance对象的isa指向class
    • 当调用对象方法时,通过instanceisa找到class,再找到对象方法进行调用。
  • class对象的isa指向meta-class
    • 当调用类方法时,通过classisa找到meta-class,最后找到类方法的实现进行调用。
  • meta-class对象的isa指向基类的meta-class对象。

class 对象的 superclass 指针

有如下两个类,继承关系如下 :

Student->Person->NSobject

@interface Person : NSObject

- (void)personMethod;
+ (void)personClassMethod;

@end

@implementation Person

- (void)personMethod {};
+ (void)personClassMethod {};

@end
@interface Student : Person

- (void)studentMethod;
+ (void)studentClassMethod;

@end

@implementation Student

- (void)studentMethod {};
+ (void)studentClassMethod {};

@end

创建一个实例student

Student *student = [[Student alloc] init];

调用[student studentMethod]方法的过程

- (void)studentMethod;方法存在于Studentclass内部。

  • 通过student(实例对象)isa指针找到Studentclass
  • Studentclass内部找到- (void)studentMethod;方法并调用。

调用[student personMethod]方法的过程

- (void)personMethod;方法存在于Personclass内部。

  • 通过student(实例对象)isa指针找到Studentclass
  • Studentclass内部找是否存在- (void)personMethod;方法。
  • 不存在,则通过Studentclass内的superclass找到Personclass
  • Personclass内找是否存在- (void)personMethod;方法。
  • 存在->调用方法。

调用[student init]方法的过程

- (void)init;方法存在于NSObjectclass内部。

  • 通过student(实例对象)isa指针找到Studentclass
  • Studentclass内部找是否存在- (void)init;方法。
  • 不存在,则通过Studentclass内的superclass指针找到Personclass
  • Personclass内找是否存在- (void)init;方法。
  • 不存在,则通过Personclass内的superclass指针找到NSObjectmeta-class
  • NSObjectclass内找是否存在- (void)init;方法;
  • 存在->调用方法。

meta-class 对象的 superclass 指针

有如下两个类,继承关系如下 :

Student->Person->NSobject

@interface Person : NSObject

- (void)personMethod;
+ (void)personClassMethod;

@end

@implementation Person

- (void)personMethod {};
+ (void)personClassMethod {};

@end
@interface Student : Person

- (void)studentMethod;
+ (void)studentClassMethod;

@end

@implementation Student

- (void)studentMethod {};
+ (void)studentClassMethod {};

@end

调用[Student studentClassMethod];方法的过程

+ (void)studentClassMethod;方法存储在Studentmeta-class内。

  • 通过Student类对象的isa找到Studentmeta-class
  • Studentmeta-class内找类方法+ (void)studentClassMethod;并调用。

调用[Student personClassMethod];方法的过程

+ (void)personClassMethod;方法存储在Personmeta-class

  • 通过Student类对象的isa找到Studentmeta-class
  • Studentmeta-class中不存在+ (void)personClassMethod;方法。
  • 通过Studentmeta-class内的superclass找到Personmeta-class
  • Personmeta-class方法内找是否存在+ (void)personClassMethod;方法。
  • 找到方法->调用。

调用[Student load];方法的过程

+ (void)load;NSObject的类方法。

  • 通过Student类对象的isa找到Studentmeta-class
  • Studentmeta-class中不存在+ (void)load;方法。
  • 通过Studentmeta-class内的superclass找到Personmeta-class
  • Personmeta-class方法内找是否存在+ (void)load;方法。
  • Personmeta-class内不存在+ (void)load;方法。
  • 通过Personmeta-class内的superclass找到NSObjectmeta-class
  • NSObjectmeta-class内找是否存在+ (void)load;方法。
  • 找到方法->调用。

isa、superclass 总结

下面是一张广为流传关于isasuperclass的经典图。

isa

  • instanceisa指向class
  • classisa指向meta-class
  • meta-classisa指向基类的meta-class

superclass

  • classsuperclass指向父类的class
    • 如果没有父类,superclass指针为nil
  • meta-classsuperclass指向父类的meta-class
  • 基类的meta-classsuperclass指向基类的class

instance 调用对象方法的轨迹

isa找到class,方法不存在,就通过superclass找父类。

class 调用类方法的轨迹

isameta-class方法不存在,就通过superclass找父类。

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

发表于

我来说两句

0 条评论
登录 后参与评论

相关文章

来自专栏老九学堂

【必读】C语言基础知识大全

C语言程序的结构认识 用一个简单的c程序例子,介绍c语言的基本构成、格式、以及良好的书写风格,使小伙伴对c语言有个初步认识。 例1:计算两个整数之和的c程...

78580
来自专栏yl 成长笔记

链表

链表定义:一种递归的数据结构, 它是在集合类的抽象数据,它或者为空, 或者是指向一个节点 (node) 的引用, 该结点含有一个泛型的元素和一个指向另一条链表的...

19710
来自专栏mukekeheart的iOS之旅

Java 内部类种类及使用解析

  Java 内部类种类及使用解析 内部类Inner Class   将相关的类组织在一起,从而降低了命名空间的混乱。   一个内部类可以定义在另一个类里,可以...

25250
来自专栏向治洪

Kotlin之基本语法

在今年Google IO大会上Google已经明确kotlin作为为Android第一官方语言的地位。我相信Google的决意,就像当初毫不犹豫的抛弃eclip...

23380
来自专栏向治洪

Kotlin之基本语法

在今年Google IO大会上Google已经明确kotlin作为为Android第一官方语言的地位。我相信Google的决意,就像当初毫不犹豫的抛弃eclip...

26570
来自专栏学海无涯

7.字符串

12140
来自专栏一个会写诗的程序员的博客

第2章 Kotlin 语法基础第2章 Kotlin 语法基础

人与人之间通过语言来交流沟通,互相协作。人与计算机之间怎样“交流沟通”呢?答案是编程语言。一门语言有词、短语、句子、文章等,对应到编程语言中就是关键字、标识符、...

16820
来自专栏Golang语言社区

Go 语言数据类型

在 Go 编程语言中,数据类型用于声明函数和变量。 数据类型的出现是为了把数据分成所需内存大小不同的数据,编程的时候需要用大数据的时候才需要申请大内存,就可以充...

32670
来自专栏我的博客

Objcetive-c基础知识

Foundation Kit基础知识 1.范围作用 NSRange typedef struct _NSRange { unsigned int loca...

275100
来自专栏Java爬坑系列

【JAVA零基础入门系列】Day6 Java字符串

  字符串,是我们最常用的类型,每个用双引号来表示的串都是一个字符串。Java中的字符串是一个预定义的类,跟C++ 一样叫String,而不是Char数组。至于...

21280

扫码关注云+社区

领取腾讯云代金券