1.介绍下runtime的内存模型(isa、对象、类、metaclass、结构体的存储信息等
对象
struct objc_object{
Class isa OBJC_ISA_AVAILABILITY;
};
isa
union isa_t {
isa_t() { }
isa_t(uintptr_t value) : bits(value) { }
Class cls;
uintptr_t bits; // uintptr_t本质是 unsigned long ,在处理器中占用8个字节,正好是64位的二级制
#if defined(ISA_BITFIELD)
struct {
ISA_BITFIELD; // defined in isa.h
};
#endif
};
类 metaclass
struct objc_class{
Class isa OBJC_ISA_AVAILABILITY;
#if !__OBJC2__
Class super_class OBJC2_UNAVAILABLE; // 父类
const char *name OBJC2_UNAVAILABLE; // 类名
long version OBJC2_UNAVAILABLE; // 类的版本信息,默认为0
long info OBJC2_UNAVAILABLE; // 类信息,供运行期使用的一些位标识
long instance_size OBJC2_UNAVAILABLE; // 该类的实例变量大小
struct objc_ivar_list *ivars OBJC2_UNAVAILABLE; // 该类的成员变量链表
struct objc_method_list *methodLists OBJC2_UNAVAILABLE; // 方法定义的链表
struct objc_cache *cache OBJC2_UNAVAILABLE; // 方法缓存
struct objc_protocol_list *protocols OBJC2_UNAVAILABLE; // 协议链表
#endif
}OBJC2_UNAVAILABLE;
metaclass
2.为什么要设计metaclass?
Why is MetaClass in Objective-C? - 简书
大概意思就是类也是一个对象,指针指向父类,最终指向元类,存放类方法
3.class_copyIvarList & class_copyPropertyList区别
首先先看Ivar
跟Property
是什么,class_copyIvarList
跟class_copyPropertyList区别
在runtime中的实现
- property在编译期会生成_propertyName的ivar,和相应的get/set属性
- ivars在编译期确定,但不完全确定,offset属性在运行时会修改
- 对象的大小是由ivars决定的,当有继承体系时,父类的ivars永远放在子类之前
- class_ro_t的instanceStart和instanceSize会在运行时调整
- class_ro_t的ivarLayout和weakIvarLayout存放的是强ivar和弱ivar的存储规则
struct property_t {
const char *name; //属性名称
const char *attributes; // 属性
};
struct ivar_t {
#if __x86_64__
// *offset was originally 64-bit on some x86_64 platforms.
// We read and write only 32 bits of it.
// Some metadata provides all 64 bits. This is harmless for unsigned
// little-endian values.
// Some code uses all 64 bits. class_addIvar() over-allocates the
// offset for their benefit.
#endif
int32_t *offset; // 偏移量,只读写32位
const char *name; // 变量名称
const char *type; // 类型
// alignment is sometimes -1; use alignment() instead
uint32_t alignment_raw; // 编译时期对齐
uint32_t size; // 变量内存大小
uint32_t alignment() const {
if (alignment_raw == ~(uint32_t)0) return 1U << WORD_SHIFT;
return 1 << alignment_raw;
} // 对齐方法
};
/***********************************************************************
* class_copyIvarList
* fixme
* Locking: read-locks runtimeLock
**********************************************************************/
Ivar *
class_copyIvarList(Class cls, unsigned int *outCount)
{
const ivar_list_t *ivars;
Ivar *result = nil;
unsigned int count = 0;
if (!cls) {
if (outCount) *outCount = 0;
return nil;
}
mutex_locker_t lock(runtimeLock);
ASSERT(cls->isRealized());
if ((ivars = cls->data()->ro->ivars) && ivars->count) {
result = (Ivar *)malloc((ivars->count+1) * sizeof(Ivar));
for (auto& ivar : *ivars) {
if (!ivar.offset) continue; // anonymous bitfield
result[count++] = &ivar;
}
result[count] = nil;
}
if (outCount) *outCount = count;
return result;
}
/***********************************************************************
* class_copyPropertyList. Returns a heap block containing the
* properties declared in the class, or nil if the class
* declares no properties. Caller must free the block.
* Does not copy any superclass's properties.
* Locking: read-locks runtimeLock
**********************************************************************/
objc_property_t *
class_copyPropertyList(Class cls, unsigned int *outCount)
{
if (!cls) {
if (outCount) *outCount = 0;
return nil;
}
mutex_locker_t lock(runtimeLock);
checkIsKnownClass(cls);
ASSERT(cls->isRealized());
auto rw = cls->data();
property_t **result = nil;
unsigned int count = rw->properties.count();
if (count > 0) {
result = (property_t **)malloc((count + 1) * sizeof(property_t *));
count = 0;
for (auto& prop : rw->properties) {
result[count++] = ∝
}
result[count] = nil;
}
if (outCount) *outCount = count;
return (objc_property_t *)result;
}
4.class_rw_t 和 class_ro_t 的区别
- 下面
class_rw_t
跟class_ro_t
结构体 - class_ro_t编译时创建,class_rw_t运行时创建
- 在程序初始化class的时候,会把编译器储存在
bits
里的class_ro_t
取出,然后创建class_rw_t
,并把ro
赋值给rw
,ro
会成为rw
的一个只读的成员变量,最后把rw
设置给bits
struct class_rw_t {
// Be warned that Symbolication knows the layout of this structure.
uint32_t flags;
uint32_t version;
const class_ro_t *ro;
method_array_t methods;
property_array_t properties;
protocol_array_t protocols;
Class firstSubclass;
Class nextSiblingClass;
char *demangledName;
#if SUPPORT_INDEXED_ISA
uint32_t index;
#endif
void setFlags(uint32_t set)
{
OSAtomicOr32Barrier(set, &flags);
}
void clearFlags(uint32_t clear)
{
OSAtomicXor32Barrier(clear, &flags);
}
// 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));
}
};
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;
}
}
};
5.load
跟 initialize
方法的区别是什么,在继承关系中他们有什么区别?
-
load
方法在main方法之前调用,是有系统调用的,只会调用一次 -
initialize
在第一次调用类所属的方法时候才会调用 -
load
是直接调用的 -
initialize
是通过objc_msgSend
调用的 - 调用顺序都为"父类->子类->类别"
- load方法不会被覆盖掉都会调用
- initialize会被子类覆盖
6.IMP
,SEL
, Method
的区别
-
Method
的结构体如下 -
Method
包含SEL
跟IMP
-
SEL
本质就是字符串 -
IMP
是方法
/// An opaque type that represents a method selector.
typedef struct objc_selector *SEL;
/// A pointer to the function of a method implementation.
typedef void (*IMP)(void /* id, SEL, ... */ );
struct method_t {
SEL name;
const char *types;
MethodListIMP imp;
struct SortBySELAddress :
public std::binary_function<const method_t&,
const method_t&, bool>
{
bool operator() (const method_t& lhs,
const method_t& rhs)
{ return lhs.name < rhs.name; }
};
};
7.category
&extention
区别,能给NSObject
添加extention
嘛,结果如何?
-
extention
在编译时期就跟原类编译到一起 -
category
是在运行时添加待原类中
8.在方法调用的时候,方法查询,动态解析,消息转发之前做了什么?
- 判断调用的
SEL
是否要忽略,Mac OS
中垃圾回收机制启动,就会忽略retain
跟release
等方法 - 判断接收的对象是否为
nil
- 在方法缓存列表中查找,
cache_getImp
,有的话直接返回IMP
- 查找当前类的
method list
,看是否有对应的SEL
,有的话就取出对应的Method
对象,并从中获得IMP
,返回IMP
- 如果当前类没有,就去父类查找,一样,先查缓存
cache list
,再查method list
,一直到Nsobject
类 - 如果没有,就进入动态解析,实际上也是通过
objc_msgSend
调用NSObject
中的resolveInstanceMethod
跟resolveClassMethod
两个方法实现动态添加 - 如果动态解析没有做出响应,就进入动态消息转发阶段。
9.category
如何被加载的,两个catefory
的load
方法的加载顺序,两个category
的同名方法的加载顺序
- 在
read_images
循环中,遍历所有取出类对应的category
数组 - 然后遍历取出
category_t
对象 - 然后执行通过
addUnattachedCategoryForClass
函数添加到Catefory
哈希表中 - 然后调用
remethodizeClass
函数从哈希表中取出数组传到attachCategories
中执行操作。向对用的类中添加catefory
信息 - 取出
Category
中方法列表,属性列表,协议列表,通过attachLists
函数,添加到类的class_rw_t
结构体中 - 同名方法的加载顺序后 添加的先调用
10.说说消息转发机制的优劣,以及其他语言的消息机制优劣对比
11.runtime的加载过程
- 程序启动,
dyld
将应用程序加载到二进制中,完成一些初始化操作-
dyld
是系统的动态库
-
- rumtime向dyld中注入回调函数
- 通过ImageLoader将所有image加载到内存中
- ImageLoader 是image的加载器
- image可以理解为编译后的二进制
- dyld在image发生改变时,主动调用回调函数
- runtime收到dyld的函数回调,开始执行map_images,load_images操作并回调load方法
- 调用
_objec_init
,初始化_dyld_objc_notify_register(&map_images, load_images, unmap_image)
- map_images,load_images,unmap_image
- mapimage执行read_image
- read_image中完成大量初始化操作
- 加载所有类到类的
gdb_objc_realized_classes
表中 - 对所有类做映射
- 将所有
SEL
注册到namedSelectors
表中 - 修复函数指针遗留
- 将所有
Protocol
都添加到protocol_map
表中 - 对所有
Protocol
做重映射 - 初始化所有非懒加载的类,进行
rw
,ro
操作 - 遍历已标记的懒加载的类,并做初始化
- 处理所有
Category
,包括Class
跟Meta Class
- 初始化所有未初始化的类
- 加载所有类到类的
- read_image中完成大量初始化操作
- mapimage执行read_image
- 调用
void _objc_init(void)
{
static bool initialized = false;
if (initialized) return;
initialized = true;
// fixme defer initialization until an objc-using image is found?
environ_init();
tls_init();
static_init();
lock_init();
exception_init();
_dyld_objc_notify_register(&map_images, load_images, unmap_image);
}
- 调用main函数。开始执行业务代码
weak的实现原理?SideTale的结构?
- weak属性,会调用
initWeak
- 然后将weak存到弱引用的哈希表中
- SideTale有一个引用计数的表和一个弱引用计数的表
汇编语言基础