任务发布网站建设,小程序制作拼图,wordpress个人支付,美食网页设计素材isa指针#xff1a;isa指针是一个指向对象所属类或元类的指针。它决定了对象可以调用的方法和属性。isa指针在对象的结构中存在#xff0c;并且在运行时会被自动设置。isa 指针#xff0c;表示这个对象是一个什么类。而 Class 类型#xff0c; 也就是 struct objc_class * …isa指针isa指针是一个指向对象所属类或元类的指针。它决定了对象可以调用的方法和属性。isa指针在对象的结构中存在并且在运行时会被自动设置。isa 指针表示这个对象是一个什么类。而 Class 类型 也就是 struct objc_class * 这是苹果在下面的注释中写到的。这说明类本身也是一个对象。在类对象中的 isa 指向的类叫做“元类”类方法就定义在元类中。总的来说就是一个类可以有很多的实例这些实例有着唯一的一个类对象而这个类对象也有着唯一的一个元类。
在Objective-C中每个对象都有一个isa指针它指向该对象所属的类或元类。isa指针决定了对象可以调用的方法和属性。通过isa指针Objective-C运行时可以在运行时动态地确定对象所属的类并在该类或其父类中查找对应的方法实现。 下面是一些示例代码来说明isa指针的作用
interface MyClass : NSObject
- (void)myMethod;
endimplementation MyClass
- (void)myMethod {NSLog(MyClasss myMethod);
}
endint main() {MyClass *myObject [[MyClass alloc] init];[myObject myMethod];return 0;
}
在上面的示例中创建了一个名为MyClass的类它继承自NSObject。MyClass类中定义了一个名为myMethod的方法。
当我们创建一个MyClass对象并调用myMethod方法时实际上发生了以下过程
分配内存并初始化MyClass对象。运行时为该对象设置isa指针使其指向MyClass的类对象。在myObject上调用myMethod方法时运行时首先通过isa指针找到MyClass的类对象。运行时在类对象中查找名为myMethod的方法实现并执行。
通过这个过程我们可以看到isa指针在动态确定对象所属的类的过程中起到了关键作用。它使得我们可以在运行时根据对象的实际类型来调用适当的方法。
isa类对象元类对象
OC的对象及其alloc和init。验证了OC对象底层是结构体然后在alloc的方法调用栈的最后一个关键方法creatinstance中有一个创建isa指针的方法。这篇我们就先聊一聊isa指针。 我们知道OC中的绝大部分对象都是继承自NSObject(目前我已知的只有一个 NSProxy 类不继承自NSObject它跟NSObject一样是基类)。按住command键跳进NSObject的头文件就能看到下面的代码
interface NSObject NSObject {#pragma clang diagnostic push #pragma clang diagnostic ignored -Wobjc-interface-ivarsClass isa OBJC_ISA_AVAILABILITY;#pragma clang diagnostic pop
}这就是NSObject的声明了它只有一个isa指针万物继承自NSObject就有了自己的isa指针了。那这个isa到底有什么用呢它究竟是个啥东西
位域
在说isa之前我们先了解一下[[共用体]]和[[位域]]这种技术。
在armv7、armv7s时代由于是32位操作系统苹果并没有对指针进行优化实例对象的isa是直接指向类对象的。进入到arm64架构时代后isa占用8个字节共计64位如果只是单纯存一个指针太浪费所以苹果通过共用体技术充分的让这64位存储了非常多的信息不用再额外的开辟空间来存储减少了内存开支减少了内存的操作。下面我们具体看一下isa的结构。
isa的定义
union isa_t {isa_t() { }isa_t(uintptr_t value) : bits(value) { }Class cls;uintptr_t bits;
#if defined(ISA_BITFIELD)struct{ISA_BITFIELD; // defined in isa.h 说明在isa.h中};
#endif
};
里面有个ISA_BITFIELD再看这个是怎么定义的
# if __arm64__
# define ISA_MASK 0x0000000ffffffff8ULL
# define ISA_MAGIC_MASK 0x000003f000000001ULL
# define ISA_MAGIC_VALUE 0x000001a000000001ULL
# define ISA_BITFIELD \uintptr_t nonpointer : 1; //表明这个属性占一位且从低位开始\uintptr_t has_assoc : 1; // 同上 \uintptr_t has_cxx_dtor : 1; \uintptr_t shiftcls : 33; /*MACH_VM_MAX_ADDRESS 0x1000000000*/ \uintptr_t magic : 6; \uintptr_t weakly_referenced : 1; \uintptr_t deallocating : 1; \uintptr_t has_sidetable_rc : 1; \uintptr_t extra_rc : 19
# define RC_ONE (1ULL45)
# define RC_HALF (1ULL18)
isa union共用体中各个位的含义 nonpointer 指示位表示是否对isa指针开启指针优化值为0表示纯isa指针 1表示不止是类对象的地址isa中还包含了类信息、对象的引用计数等 has_assoc: 关联对象标志位0没有1存在KVO的实现原理 has_cxx_dtor :该对象是否有C或者Objc的析构器如果有析构函数则需要做析构逻辑如果没有则可以更快的释放对象 shiftcls: 实际存储类指针的值。开启指针优化的情况下在arm64架构中有33位用来存储类指针也是基于此不同的架构中对于类指针的读取需要不同的操作在32位时期由于由于未进行isa优化isa是直接指向类对象的在64位之后需要用isa指针与上掩码(ISA_MASK)才能取到指针这个指针指向类对象。 magic :用于调试器判断当前对象是真的对象还是没有初始化的空间 weakly_refrenced: 对象是否被指向或者曾经指向一个ARC的弱变量没有弱引用的对象可以更快的释放 dellocating :标志对象是否正在释放对象。ing表示进行时 has_sidetable_rc:当对象引用计数大于0时则需要借用该变量存储进位。weak的实现原理就是使用sidetables。后续讲到内存管理的时候会对此进行详细的讲解。 extra_rc:当表示该对象的引用计数值实际上是引用计数值减1.例如如果对象的引用计数为10那么extra_rc为9如果引用计数大于10则需要使用到上面的has_sidetable_rc。
用64位存储了这么多的信息相当的高效我当初知道这个点的时候叹为观止苹果真的把这种细节这种设计做到了极致。苹果的工程师们真他娘的都是人才各个身怀绝技也可能我作为井底之蛙没有了解过这个技术。
好介绍过isa之后接下来就该是实例对象、类对象、和元类对象了。
在arm64架构之后isa通过shiftcls来指向类对象的。类对象也是个对象也有isa指针它的isa中的shiftcls是指向元类对象的。元类对象也有isa它的shiftcls都指向根类的元类对象。 网上有一张非常经典的图来展示了对象和元类对象的关系这幅图将贯穿大部分关于底层原理的博客内容来看一下
这个图用语言来描述一下就是
实例对象的isa指向的类对象类对象的isa指向元类对象。元类对象的isa均指向基类的元类对象基类的元类对象的isa指向自己。需要特别注意arm64架构后isa不直接指向需要先用掩码取出shift_cls指针这个是实际的指向关系的指针。类对象和元类对象中有superclass指针普通类对象的superclass指针指向父类的类对象基类类对象的superclass指向nil。基础对象的元类对象的superclass指针指向父类的元类对象基类的superclass指针指向父类的类对象。
写完这些我都不认识这个类字了。谨记这幅图就能非常好的理解实例对象、类对象、元类对象之间的关系了。这个图画的非常好在后面的runtime的消息转发流程中还会用到这幅图以及这些对象之间的关系。
为什么需要类对象和元类对象
那为啥要有类对象和元类对象呢类对象和元类对象中都放着什么东西呢
先说为什么。我们都知道我们创建的类有很多的属性、协议和方法方法又分为实例方法和类方法。而这些方法的实现都是统一的再调用的过程中只是参数的值不同所以这些方法存一份儿就够了没必要在每个对象中都存一份。所以就有了类对象和元类对象。可以想一下平时写的方法调用的代码假定Person类有一个实例方法叫-(void)instanceFunction有个类方法(void)classFunction。我们在调用的时候是这么调用的
- (void)callFunctions{[person instanceFunction]; //使用实例对象来调用实例方法[Person classFunction]; //使用类名其实就是类对象调用类方法
}
可以很明显的看出方法调用者的不同。而这个不同就是由类对象、元类对象、isa来实现的。类对象中存储了这个类的属性、协议和实例方法。元类对象中存储了这个类的类方法。在方法调用时实例方法通过这个实例对象的isa找到这个类的对象然后在类对象中查找这个方法。类方法通过类对象的isa找到这个类的元类对象在元类对象中查找这个方法。通过这样的描述结合上面的图就能很好的理解isa指针、类对象和元类对象了。
好简单的来总结一下今天的内容。主要是理清实例对象、类对象、元类对象之间的关系要记住那副图保证自己能完整的复述出他们之间的关系。isa就相当于纽带将他们串联了起来。在最后提到了类对象和元类对象是存储属性、协议和方法的我们会在runtime中具体的讲解这些信息是如何存储的。