iOS底层之类扩展和关联对象
在前面文章中,从底层源码探索了类和分类的加载流程,今天从源码层面实例探索一下类扩展和关联对象的本质。
在iOS日常开发中,我们每天都会创建对象,那么对象的本质是什么呢?对象和类又是如何绑定关联的呢?
OC的底层实现是C++代码,我们可以通过clang中的命令,用C++来还原OC代码。
Clang 是一个C语言、C++、Objective-C语言的轻量级编译器,是由Apple主导编写的Clang 主要用于把源文件编译成底层文件,比如把main.m 文件编译成main.cpp、main.o或者可执行文件。clang 常用命令
clang -rewrite-objc main.m -o main.cpp
//UIKit报错
clang -x objective-c -rewrite-objc -isysroot /Applications/Xcode.app/Contents/Developer/Platforms/iPhoneSimulator.platform/Developer/SDKs/iPhoneSimulator.sdk main.m
// xcrun命令基于clang基础上进行了封装更好用
//3、模拟器编译
xcrun -sdk iphonesimulator clang -arch arm64 -rewrite-objc main.m -o main-arm64.cpp
//4、真机编译
xcrun -sdk iphoneos clang -arch arm64 -rewrite-objc main.m -o main- arm64.cpp
在工程中的mian.m文件中定义如下代码,然后编译成main.cpp文件
@interface LGPerson : NSObject
{
NSString * LGName;
}
@property (nonatomic,assign) NSInteger age;
@end
@implementation LGPerson
@end
int main(int argc, char * argv[]) {
NSString * appDelegateClassName;
@autoreleasepool {
// Setup code that might create autoreleased objects goes here.
appDelegateClassName = NSStringFromClass([AppDelegate class]);
}
return UIApplicationMain(argc, argv, nil, appDelegateClassName);
}
- 在
cpp文件中,可以看出LGPerson的定义其实是struct,也就是对象的本质是结构体。- 在
LGPerson_IMP的定义中,有一个struct NSObject_IMPL NSObject_IVARS;,这里的NSObject_IVARS是继承自NSObject中ivars,在NSObject_IMPL的定义中,可以看到其ivars只有isa一个成员。
isa的定义是Class,那我们再找到Class的定义
Class 类型实际是一个 objc_class类型的结构体指针,这里也能看到我们常用的id其实是objc_object类型的结构体指针,以及SEL是函数的一个结构体指针。
struct LGPerson_IMPL {
struct NSObject_IMPL NSObject_IVARS;
NSString *LGName;
NSInteger _age;
};
static NSInteger _I_LGPerson_age(LGPerson * self, SEL _cmd) { return (*(NSInteger *)((char *)self + OBJC_IVAR_$_LGPerson$_age)); }
static void _I_LGPerson_setAge_(LGPerson * self, SEL _cmd, NSInteger age) { (*(NSInteger *)((char *)self + OBJC_IVAR_$_LGPerson$_age)) = age; }
可以看到age属性变量有生成get和set方法,同时生成了_age变量,而声明的LGName则没有这些方法变化。
对象的本质其实就是一个结构体,其内存大小和成员变量有关,也就是isa+成员+属性,这也验证了在alloc探索中开辟内存大小和ivars有关;- 所有自定义的类都继承自
NSObject,而NSObject是objc_object类型的结构体,其内部有且只有一个成员属性isa。
在前面alloc的底层探究中,我们知道了对象通过obj->initInstanceIsa初始化isa关联类。接下来就看一下isa的结构是怎么样的,以及如何通过isa知道关联类的?
initInstanceIsa方法inline void
objc_object::initInstanceIsa(Class cls, bool hasCxxDtor)
{
ASSERT(!cls->instancesRequireRawIsa());
ASSERT(hasCxxDtor == cls->hasCxxDtor());
// 初始化isa
initIsa(cls, true, hasCxxDtor);
}
initIsa 方法inline void
objc_object::initIsa(Class cls, bool nonpointer, UNUSED_WITHOUT_INDEXED_ISA_AND_DTOR_BIT bool hasCxxDtor)
{
ASSERT(!isTaggedPointer());
isa_t newisa(0); // isa初始化
if (!nonpointer) { // 非nonpointer指针直接绑定cls
newisa.setClass(cls, this);
} else {
ASSERT(!DisableNonpointerIsa);
ASSERT(!cls->instancesRequireRawIsa());
#if SUPPORT_INDEXED_ISA
ASSERT(cls->classArrayIndex() > 0);
newisa.bits = ISA_INDEX_MAGIC_VALUE;
// isa.magic is part of ISA_MAGIC_VALUE
// isa.nonpointer is part of ISA_MAGIC_VALUE
newisa.has_cxx_dtor = hasCxxDtor;
newisa.indexcls = (uintptr_t)cls->classArrayIndex();
#else
newisa.bits = ISA_MAGIC_VALUE;
// isa.magic is part of ISA_MAGIC_VALUE
// isa.nonpointer is part of ISA_MAGIC_VALUE
# if ISA_HAS_CXX_DTOR_BIT
newisa.has_cxx_dtor = hasCxxDtor;
# endif
newisa.setClass(cls, this);
#endif
newisa.extra_rc = 1;
}
// This write must be performed in a single store in some cases
// (for example when realizing a class because other threads
// may simultaneously try to use the class).
// fixme use atomics here to guarantee single-store and to
// guarantee memory order w.r.t. the class index table
// ...but not too atomic because we don't want to hurt instantiation
isa = newisa;
}
在
initIsa方法中,如果是非nonpointer类型指针,则直接调setClass方法绑定;否则现有一些位域赋值操作,再调setClass方法绑定。
isa_t
union isa_t {
isa_t() { }
isa_t(uintptr_t value) : bits(value) { }
uintptr_t bits;
private:
// Accessing the class requires custom ptrauth operations, so
// force clients to go through setClass/getClass by making this
// private.
Class cls;
public:
#if defined(ISA_BITFIELD)
struct {
ISA_BITFIELD; // defined in isa.h
};
bool isDeallocating() {
return extra_rc == 0 && has_sidetable_rc == 0;
}
void setDeallocating() {
extra_rc = 0;
has_sidetable_rc = 0;
}
#endif
void setClass(Class cls, objc_object *obj);
Class getClass(bool authenticated);
Class getDecodedClass(bool authenticated);
};
在联合体isa_t中,有cls和bits两个变量,还有一个ISA_BITFIELD是结构体里面的宏定义变量,是按位域定义的,ISA_BITFIELD的结构如下:
各变量的解释:
nonpointer:表示是否对isa指针开启指针优化, 0:纯isa指针,1:不止是类对象地址,isa中包含了类信息、对象的引用计数等;has_assoc:关联对象标志位,0-没有,1-存在;has_cxx_dtor:该对象时候有C++或Objc的析构器,如果有析构函数,则需要做析构逻辑,没有则可以更快的释放对象;shiftcls:存储类指针的值,开启指针优化的情况下,在ARM64架构中有33位来存储类指针值;magic:用于调试器判断当前对象是真的对象还是未初始化的空间;weakly_referenced:标志对象是否被指向或曾经指向一个ARC的弱变量,没有弱引用对象则可以更快的释放;unused:对象是否释放;has_sidetable_rc:当对象引用计数大于10时,则需要借用该变量存储进位;extra_rc:对象的引用计数值(实际上是引用计数减1),例如:引用计数是10,那么extra_rc为9,如果引用计数大于10,则需要使用到has_sidetable_rc。
isa指针分为nonpointer和非nonpointer类型,非nonpointer类型就是一个纯指针,nonpointer类型指针开启了指针优化。指针优化的设计,可以更好的利用isa的内存,程序中有大量的对象,针对isa的优化节省了大量内存。通过对对象的本质和
isa指针的分析,我们已经知道对象是如何与类进行关联的,后面就来探究一下OC中类的结构。
以上是关于OC中对象的本质分析,如有错误或疑问之处,请在评论区留言吧!!!