Objective-C中的 self 和 isa
isa指针
- Class 和 id 的typedef
typedef struct objc_class *Class;
typedef struct objc_object *id;
id
是一个objc_object
结构类型的指针,而objc_object
结构就表示是一个对象。所以,id
声明的变量则是一个指向对象的指针。
我们所说的“对象”,就是指这样的一个结构体:
objc_object
,它的成员只有一个指向object_class
结构体的指针isa
。
- isa_t
union isa_t
{
isa_t() { }
isa_t(uintptr_t value) : bits(value) { }
Class cls;
uintptr_t bits;
}
- objc_object
struct objc_object {
private:
isa_t isa;
}
- objc_class
struct objc_class : objc_object {
// Class ISA;
Class superclass;
cache_t cache; // formerly cache pointer and vtable
class_data_bits_t bits; // class_rw_t * plus custom rr/alloc flags
}
- NSObject
@interface NSObject <NSObject> {
Class isa OBJC_ISA_AVAILABILITY;
}
@interface Object {
Class isa;
}
-
Objective-C
对象基本上是C
结构。每个Objective-C
对象(包括每个类)都有一个isa
指针,运行时根据此指针来确定对象是什么类,因此它知道对象响应的选择器,超类是什么,对象具有什么属性等等。 -
isa
:是一个Class
类型的指针. 每个实例对象有个isa
的指针,他指向对象的类,而Class
里也有个isa
的指针, 指向meteClass(元类)
。元类保存了类方法的列表。当类方法被调用时,先会从本身查找类方法的实现,如果没有,元类会向他父类查找该方法。同时注意的是:元类(meteClass)
也是类,它也是对象。元类也有isa
指针,它的isa
指针最终指向的是一个根元类(root meteClass)
.根元类的isa指针指向本身,这样形成了一个封闭的内循环。

对象的实例方法调用时,通过对象的
isa
在类中获取方法的实现。
类对象的类方法调用时,通过类的isa
在元类中获取方法的实现。
self、 super 和 _cmd
-
self
:Objective-C
的函数的前两个隐藏参数之一,指向当前当前调用方法的类,另一个隐藏参数是_cmd
,代表当前类方法的selector
。 -
super
:super
并不是隐藏的参数,它只是一个“编译器指示符”,它和self
指向的是相同的消息接收者,不同的是super
告诉编译器,当调用方法时,要去调用父类的方法,而不是本类里的。
当使用
self
调用方法时,会从当前类的方法列表中开始找,如果没有,就从父类中再找;而当使用super
时,则从父类的方法列表中开始找。然后调用父类的这个方法。
self
、super
是一个指针,self
谁调用了当前方法,self
就指向谁
而super
指向了当前类的父类,如果super
在类方法中,它就代表了调用当前类方法的类的父类,如果在对象方法中,它就代表调用当前对象方法的对象的父类对象
- 如果
self
、super
在类方法中,那么它就代表当前调用这个类方法的类、父类 - 如果
self
、super
在对象方法中,那么它就代表当前调用这个对象方法的对象、父对象
出现在对象方法中,就代表着当前对象,出现在类方法中,就代表着当前类
- _cmd:
SEL
类型的一个变量,代表当前方法的selector
,Objective-C
的函数的前两个隐藏参数的另外一个,它和self
一样都是每个方法调用时都会传入的参数
/// An opaque type that represents a method selector.
typedef struct objc_selector *SEL;
struct objc_method {
SEL method_name;
char *method_types;
IMP method_imp;
};
SEL
是selector
在Objc
中的表示类型,selector
是方法选择器
SEL
可以理解为方法编号,其实它就是个映射到方法的C字符串,objc.h文件中

object_getClass 与 [self class]
-
object_getClass
: 获得的是isa
指针的指向 -
[self class]
: 当self
时实例对象的时候,获取的是isa
的指向,也就是类对象,如果是类对象,则返回自身。
object_getClass | [obj class] | |
---|---|---|
实例对象 | isa指针指向 | isa指针指向 |
类对象 | isa指针指向 | 对象本身 |
元类对象 | isa指针指向 | 对象本身 |
关于 class 和 object_getClass的相关源码:
+ (Class)class {
return self;
}
- (Class)class {
return object_getClass(self);
}
Class object_getClass(id obj)
{
if (obj) return obj->getIsa();
else return Nil;
}
inline Class
objc_object::getIsa()
{
if (isTaggedPointer()) {
uintptr_t slot = ((uintptr_t)this >> TAG_SLOT_SHIFT) & TAG_SLOT_MASK;
return objc_tag_classes[slot];
}
return ISA();
}
inline Class
objc_object::ISA()
{
assert(!isTaggedPointer());
return (Class)(isa.bits & ISA_MASK);
}
如下图所示:

-
类方法中
+ (void)testSelf
的self
是指向类对象本身,[Person class]
和[self class]
也都是指向本身,因为在类方法中self
是类对象,所以和[Person class]
一样调用的是+ (Class)class
返回的都是自身,object_getClass(class)
相当于是self->isa
,所以是指向isa
的指针 -
实例方法中
- (void)testSelf
,self
是指向当前实例对象本身,在实例方法中的[Person class]
和类方法中的p2指针地址一样,都是指向类对象本身,[self class]
因为self
是实例对象所以调用- (Class)class
相当于调用object_getClass(class)
,返回的是self->isa
,所以是指向isa
指针。p2 == p3 == p4,这也说明了实例对象中的isa
指针指向的是类对象。
附:(isKindOfClass 和 isMemberOfClass)源码
+ (BOOL)isKindOfClass:(Class)cls {
for (Class tcls = object_getClass((id)self); 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;
}
+ (BOOL)isMemberOfClass:(Class)cls {
return object_getClass((id)self) == cls;
}
- (BOOL)isMemberOfClass:(Class)cls {
return [self class] == cls;
}
问题:以下代码输出结果?
BOOL res1 = [(id)[NSObject class] isKindOfClass:[NSObject class]];
BOOL res2 = [(id)[NSObject class] isMemberOfClass:[NSObject class]];
BOOL res3 = [(id)[Sark class] isKindOfClass:[Sark class]];
BOOL res4 = [(id)[Sark class] isMemberOfClass:[Sark class]];
答案和详解和可以从神经病院 Objective-C Runtime 入院第一天这里获取
参考链接
神经病院 Objective-C Runtime 入院第一天
从 NSObject 的初始化了解 isa
欢迎留言,如果写的不对的地方还请不吝指出。