iOS Runtime知识梳理
源码可在这里查看:https://opensource.apple.com/tarballs/objc4/
一、isa的理解
-
1、对象指向
指向它的类对象,从而找到对象的方法。对象、类、元类的关系如下图所示:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-iZehn30A- -
2、类型
a、纯指针:指向内存地址
b、NON_POINTER_ISA:指向内存地址和一些其他信息
二、class_rw_t和class_ro_t的理解
- 1、class_rw_t:可读可写,存储objc类中的属性、方法列表、协议列表等
//官方源码
struct class_rw_t {
// Be warned that Symbolication knows the layout of this structure.
uint32_t flags;
uint16_t witness;
#if SUPPORT_INDEXED_ISA
uint16_t index;
#endif
explicit_atomic<uintptr_t> ro_or_rw_ext;
Class firstSubclass;
Class nextSiblingClass;
private:
using ro_or_rw_ext_t = objc::PointerUnion<const class_ro_t *, class_rw_ext_t *>;
const ro_or_rw_ext_t get_ro_or_rwe() const {
return ro_or_rw_ext_t{ro_or_rw_ext};
}
void set_ro_or_rwe(const class_ro_t *ro) {
ro_or_rw_ext_t{ro}.storeAt(ro_or_rw_ext, memory_order_relaxed);
}
void set_ro_or_rwe(class_rw_ext_t *rwe, const class_ro_t *ro) {
// the release barrier is so that the class_rw_ext_t::ro initialization
// is visible to lockless readers
rwe->ro = ro;
ro_or_rw_ext_t{rwe}.storeAt(ro_or_rw_ext, memory_order_release);
}
class_rw_ext_t *extAlloc(const class_ro_t *ro, bool deep = false);
public:
void setFlags(uint32_t set)
{
__c11_atomic_fetch_or((_Atomic(uint32_t) *)&flags, set, __ATOMIC_RELAXED);
}
void clearFlags(uint32_t clear)
{
__c11_atomic_fetch_and((_Atomic(uint32_t) *)&flags, ~clear, __ATOMIC_RELAXED);
}
// set and clear must not overlap
void changeFlags(uint32_t set, uint32_t clear)
{
ASSERT((set & clear) == 0);
uint32_t oldf, newf;
do {
oldf = flags;
newf = (oldf | set) & ~clear;
} while (!OSAtomicCompareAndSwap32Barrier(oldf, newf, (volatile int32_t *)&flags));
}
class_rw_ext_t *ext() const {
return get_ro_or_rwe().dyn_cast<class_rw_ext_t *>();
}
class_rw_ext_t *extAllocIfNeeded() {
auto v = get_ro_or_rwe();
if (fastpath(v.is<class_rw_ext_t *>())) {
return v.get<class_rw_ext_t *>();
} else {
return extAlloc(v.get<const class_ro_t *>());
}
}
class_rw_ext_t *deepCopy(const class_ro_t *ro) {
return extAlloc(ro, true);
}
//指向只读结构体,存储类初始信息
const class_ro_t *ro() const {
auto v = get_ro_or_rwe();
if (slowpath(v.is<class_rw_ext_t *>())) {
return v.get<class_rw_ext_t *>()->ro;
}
return v.get<const class_ro_t *>();
}
void set_ro(const class_ro_t *ro) {
auto v = get_ro_or_rwe();
if (v.is<class_rw_ext_t *>()) {
v.get<class_rw_ext_t *>()->ro = ro;
} else {
set_ro_or_rwe(ro);
}
}
//方法列表
const method_array_t methods() const {
auto v = get_ro_or_rwe();
if (v.is<class_rw_ext_t *>()) {
return v.get<class_rw_ext_t *>()->methods;
} else {
return method_array_t{v.get<const class_ro_t *>()->baseMethods()};
}
}
//属性列表
const property_array_t properties() const {
auto v = get_ro_or_rwe();
if (v.is<class_rw_ext_t *>()) {
return v.get<class_rw_ext_t *>()->properties;
} else {
return property_array_t{v.get<const class_ro_t *>()->baseProperties};
}
}
//协议列表
const protocol_array_t protocols() const {
auto v = get_ro_or_rwe();
if (v.is<class_rw_ext_t *>()) {
return v.get<class_rw_ext_t *>()->protocols;
} else {
return protocol_array_t{v.get<const class_ro_t *>()->baseProtocols};
}
}
};
- 2、class_ro_t:只读不可写,存储编译期确定的属性、方法、协议等信息
//官方源码
struct class_ro_t {
uint32_t flags;
uint32_t instanceStart;
uint32_t instanceSize;
#ifdef __LP64__
uint32_t reserved;
#endif
const uint8_t * ivarLayout;
const char * name;
//方法列表
method_list_t * baseMethodList;
//协议列表
protocol_list_t * baseProtocols;
//属性列表
const ivar_list_t * ivars;
const uint8_t * weakIvarLayout;
property_list_t *baseProperties;
// This field exists only when RO_HAS_SWIFT_INITIALIZER is set.
_objc_swiftMetadataInitializer __ptrauth_objc_method_list_imp _swiftMetadataInitializer_NEVER_USE[0];
_objc_swiftMetadataInitializer swiftMetadataInitializer() const {
if (flags & RO_HAS_SWIFT_INITIALIZER) {
return _swiftMetadataInitializer_NEVER_USE[0];
} else {
return nil;
}
}
method_list_t *baseMethods() const {
return baseMethodList;
}
class_ro_t *duplicate() const {
if (flags & RO_HAS_SWIFT_INITIALIZER) {
size_t size = sizeof(*this) + sizeof(_swiftMetadataInitializer_NEVER_USE[0]);
class_ro_t *ro = (class_ro_t *)memdup(this, size);
ro->_swiftMetadataInitializer_NEVER_USE[0] = this->_swiftMetadataInitializer_NEVER_USE[0];
return ro;
} else {
size_t size = sizeof(*this);
class_ro_t *ro = (class_ro_t *)memdup(this, size);
return ro;
}
}
};
三、NSObject对像占用内存解析
- 受内存分配机制的限制,一个NSObject对象都会分配16byte的内存。实际上,在64位系统下,只使用了8bytes;在32位系统下,只使用了4bytes。在iOS中,分配内存都是16的倍数。
#import <objc/runtime.h>
size_t size = class_getInstanceSize([NSObject class]);
NSLog(@"class instance size %zu",size);
//打印输出结果
2021-02-16 13:42:33.664592+0800 DSPracticeDemo[38554:2390822] class instance size 8
====================================================================================
#import <malloc/malloc.h>
NSObject *obj = [[NSObject alloc] init];
size_t mallocSize = malloc_size((__bridge const void *)obj);
NSLog(@"class instance malloc size %zu",mallocSize);
打印输出结果
2021-02-16 13:56:14.341374+0800 DSPracticeDemo[38624:2442458] class instance malloc size 16
- class_getInstanceSize查看源码,本质如下
size_t class_getInstanceSize(Class cls)
{
if (!cls) return 0;
//调用内存对齐大小的方法
return cls->alignedInstanceSize();
}
四、runtime方法缓存、存储形式、数据结构和查找方式
- 方法缓存:cache_t增量扩展的哈希表结构,哈希表结构中存储的是bucket_t
- 存储形式:bucket_t中存储的是SEL、IMP键值对
- 数据结构:
a、cache_t数据结构:
struct cache_t {
#if CACHE_MASK_STORAGE == CACHE_MASK_STORAGE_OUTLINED
explicit_atomic<struct bucket_t *> _buckets;
explicit_atomic<mask_t> _mask;
#elif CACHE_MASK_STORAGE == CACHE_MASK_STORAGE_HIGH_16
explicit_atomic<uintptr_t> _maskAndBuckets;
mask_t _mask_unused;
...
}
b、bucket_t数据结构:
struct bucket_t {
private:
// IMP-first is better for arm64e ptrauth and no worse for arm64.
// SEL-first is better for armv7* and i386 and x86_64.
#if __arm64__
explicit_atomic<uintptr_t> _imp;
explicit_atomic<SEL> _sel;
#else
explicit_atomic<SEL> _sel;
explicit_atomic<uintptr_t> _imp;
#endif
...
}
- 查找方式:有序,采用二分查找法;无序,直接遍历
五、NSObject的dealloc释放机制
- 1、调用release:引用计数变为0
- 2、调用[self dealloc]
- 3、父类调用dealloc(每一层继承都会调用)
- 4、调用NSObject dealloc:在OC中只做一件事,就是在runtime中调用object_dispose方法处理C++相关的代码、引用计数表、弱引用表、关联对象等
//官方源码
//NSObject的dealloc方法调用_objc_rootDealloc方法
- (void)dealloc {
_objc_rootDealloc(self);
}
void _objc_rootDealloc(id obj)
{
ASSERT(obj);
obj->rootDealloc();
}
inline void objc_object::rootDealloc()
{
if (isTaggedPointer()) return; // fixme necessary?
//判断:isa.nonpointer、弱引用表、关联对象对象、C++代码和引用计数表等
if (fastpath(isa.nonpointer &&
!isa.weakly_referenced &&
!isa.has_assoc &&
!isa.has_cxx_dtor &&
!isa.has_sidetable_rc))
{
assert(!sidetable_present());
free(this);
}
else {
object_dispose((id)this);
}
}
id object_dispose(id obj)
{
if (!obj) return nil;
objc_destructInstance(obj);
free(obj);
return nil;
}
/***********************************************************************
* objc_destructInstance
* Destroys an instance without freeing memory.
* Calls C++ destructors.
* Calls ARC ivar cleanup.
* Removes associative references.
* Returns `obj`. Does nothing if `obj` is nil.
**********************************************************************/
void *objc_destructInstance(id obj)
{
if (obj) {
// Read all of the flags at once for performance.
bool cxx = obj->hasCxxDtor();
bool assoc = obj->hasAssociatedObjects();
// This order is important.
if (cxx) object_cxxDestruct(obj);
if (assoc) _object_remove_assocations(obj);
obj->clearDeallocating();
}
return obj;
}
inline void objc_object::clearDeallocating()
{
if (slowpath(!isa.nonpointer)) {
// Slow path for raw pointer isa.
sidetable_clearDeallocating();
}
else if (slowpath(isa.weakly_referenced || isa.has_sidetable_rc)) {
// Slow path for non-pointer isa with weak refs and/or side table data.
clearDeallocating_slow();
}
assert(!sidetable_present());
}
NEVER_INLINE void objc_object::clearDeallocating_slow()
{
ASSERT(isa.nonpointer && (isa.weakly_referenced || isa.has_sidetable_rc));
SideTable& table = SideTables()[this];
table.lock();
if (isa.weakly_referenced) {
weak_clear_no_lock(&table.weak_table, (id)this);
}
if (isa.has_sidetable_rc) {
table.refcnts.erase(this);
}
table.unlock();
}
六、Method Swizzling(黑魔法)
- 方法交换,在OC中调用一个方法,其实是向一个对象发送一个消息,查找消息的唯一依据是selector的名字。利用OC的动态特性,可以在运行时交换selector对应的实现。每个类中都有一个方法列表,存放着方法名和方法实现的映射关系,selector本质是方法名,imp是指向函数实现的指针,通过selector可以找到对应的imp,然后交换方法实现。
- 通过这三个方法可以交换方法method_exchangeImplementations(交换两个方法的实现)、class_replaceMethod(替换方法实现)、method_setImplementation(设置方法实现)
七、分类添加属性
- 关联对象以哈希表的方式存储在全局单例中
@interface NSObject (Extension)
@property (nonatomic,copy) NSString *name;
@end
@implementation NSObject (Extension)
- (void)setName:(NSString *)name{
objc_setAssociatedObject(self, @selector(name), name, OBJC_ASSOCIATION_COPY);
}
- (NSString *)name{
return objc_getAssociatedObject(self, @selector(name));
}
@end
八、类对象数据结构
- 数据结构相对丰富一些,它继承于objc_object
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
//数据,封装好的class_rw_t
class_rw_t *data() const {
return bits.data();
}
...
}
九、Category和原类合并时机、用途和实现原理
-
合并时机
1、程序启动,通过编译之后,runtime会进行初始化,调用__objc_init
2、然后map_images
3、接下来调用map_images_nolock
4、read_images读取类中的所有信息
5、然后调用reMethodizeClass重新方法化
6、reMethodize中调用attachCategories将categories和原类合并 -
用途
1、给系统类添加方法、属性
2、某个类有大量的方法,可实现根据名称进行分类 -
实现原理
在运行时,方法以倒序的方式插入到原类的方法列表中,所以不同的category,添加同一个方法,实际有效的是最后添加的方法
- 转载请标明出处
- 如有错误理解,还请各路大佬批评指出