部分实现源码解析 - iOS
类的本质
每个对象都是其 类 的实例 , 被称为实例对象 . 每一个对象都有一个名为 isa 的指针,指向该对象的类。
typedef struct objc_object *id; typedef struct objc_class *Class; struct objc_class : objc_object { // Class ISA; 8 字节 Class superclass;// 8 字节 cache_t cache; // 16 字节 // formerly cache pointer and vtable class_data_bits_t bits; // class_rw_t * plus custom rr/alloc flags /**/ } struct objc_object { private: isa_t isa;// 8 字节,对象实际分配的内存大小: 16 public: // ISA() assumes this is NOT a tagged pointer object Class ISA(); // getIsa() allows this to be a tagged pointer object Class getIsa(); /*...*/ }
对象是一个 id
类型 , 也就是一个指向 objc_object
结构体的指针。而我们看到 Class
是一个指向 objc_class
结构体指针,而 objc_class
继承于 objc_object
, 因此,我们说 类也是一个对象 。 对象的本质实际上是个结构体指针
在 Objective-C
中,每个对象 ( 其根本是一个 objc_object
结构体指针 ) 都有一个名为 isa
的指针,指向该对象的类 ( 其根本是一个 objc_class
结构体 )。
struct cache_t {
struct bucket_t *_buckets; // 8 字节
mask_t _mask; // 4 字节
mask_t _occupied; // 4 字节
}
struct bucket_t {
private:
#if __arm64__
uintptr_t _imp;
SEL _sel;
#else
SEL _sel;
uintptr_t _imp;
#endif
}
struct class_data_bits_t { uintptr_t bits; class_rw_t* data() { return (class_rw_t *)(bits & FAST_DATA_MASK); } /*.其他一些方法省略..*/ } typedef unsigned long uintptr_t; // data pointer #define FAST_DATA_MASK 0x00007ffffffffff8UL
mask_t cache_t::capacity() { return mask() ? mask()+1 : 0; } mask_t cache_t::mask() { return _mask; }
当没有调用过方法 , _mask
为 0 , 容量 capacity
也为 0 , 当 _mask
有值时 , 实际容量为 _mask + 1
.
使用 occupied
来记录当前已经使用容量 .
当使用容量到达总容量的 3/4
时 , 哈希桶会进行扩容 , 扩容容量为当前容量的两倍 ( 当使用超过 4 字节不扩容 ) . 扩容时会清空历史缓存 , 只保留最新 sel
和 imp
.
struct class_rw_t { 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 /*...*/ }
当对象中包含属性, 会按属性占用内存开辟空间. 在结构体内存分配原则下自动偏移和补齐 .对象最终满足 16
字节对齐标准 .属性最终满足 8
字节对齐标准 .
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; }
其实如名称一样 , class_rw_t即 read write
, class_ro_t 即 read only
. OC
为了动态的特性 , 在编译器确定并保存了一份 类的结构数据在 class_ro_t中 , 另外存储一份在运行时加载到 class_rw_t 中 , 供 runtime
动态修改使用 .
ro
是不可变的 , 而 rw
中 methods
, properties
以及 protocols
内存空间是可变的 . 这也是 已有类 为什么可以动态添加方法 , 确不能动态添加属性的原因 ( 添加属性会同样添加成员变量 , 也就是 ivar . 而 ivar 是存储在 ro 中的 ) .1、没有访问局部变量的block,并且没有强指针指向block,则此block为NSGlobalBlock
2、访问了局部变量的block,但是没有强指针指向block,则此block为NSStackBlock
3、访问了局部变量的block,并且有强指针指向block,则此block为NSMallocBlock
4、NSStackBlock类型的block,执行了copy操作之后,生成的block为NSMallocBlock
7、对NSMallocBlock类型的block进行copy操作,此block的引用计数会加1
int a = 10; static int b = 20;
NSObject *obj1 = [[NSObject alloc] init];
NSObject *obj2 = [[NSObject alloc] init];
__weak typeof(obj2) weakObj = obj2;
__block NSObject *obj3 = [[NSObject alloc] init];
__block int c = 10;
void(*test)(void) = (&__main_block_impl_0( __main_block_func_0, &__main_block_desc_0_DATA, a, &b,
obj1,
weakObj,
obj3,
&c));
test->FuncPtr(test);
struct __main_block_impl_0 { //存放了block的一些基本信息,包括isa,函数地址等等 struct __block_impl impl; //存放block的一些描述信息 struct __main_block_desc_0* Desc;
int a;
int *b;
NSObject *_strong obj1;
NSObject *_weak weakObj;
__Block_byref_obj3_0 *obj3;
__Block_byref_c_1 *c; //构造函数 __main_block_impl_0(void *fp, struct __main_block_desc_0 *desc, int _a, int *_b,NSObject *__strong _obj,NSObject *__weak _weakObj, int flags=0) : a(_a), b(_b), obj1(_obj1), weakObj(_weakObj), obj3 { impl.isa = &_NSConcreteStackBlock; impl.Flags = flags; impl.FuncPtr = fp; Desc = desc; } }; struct __block_impl { void *isa; //isa指针,可以看出Block其实就是一个OC对象 int Flags; //标识,默认为0 int Reserved; //保留字段 void *FuncPtr;//函数内存地址 };
static struct __main_block_desc_0 { size_t reserved; //保留字段 size_t Block_size; //__main_block_impl_0结构体所占内存大小
} __main_block_desc_0_DATA = { 0, sizeof(struct __main_block_impl_0), __main_block_copy_0, __main_block_dispose_0};
static void __main_block_func_0(struct __main_block_impl_0 *__cself) {
}
block将我们所要调用的代码封装成了函数__main_block_func_0,并且将函数__main_block_func_0的内存地址保存在到void *FuncPtr中
在__main_block_func_0函数中,访问局部变量a和静态变量b时都是通过传递过来的__main_block_impl_0结构体拿到对应的成员变量进行访问,但是全局变量c并没有存放在结构体中,而是直接进行访问。
当访问局部变量为非引用类型的时候,会将局部变量的值捕获到block中,存放在一个同名的成员变量中。
当访问局部变量为引用类型的时候,生成一个strong类型的指针指向外部对象。
当访问局部变量为引用类型且用__weak修饰的时候,生成一个weak类型的指针指向外部对象。
当访问静态变量时,会将静态变量的地址捕获到block中,存放在一个同名的成员变量中。
当访问全局变量时,因为全局变量是一直存在,不会销毁,所以在block中直接访问全局变量,不需要进行捕获
NSNotification:
存储
name
,那么会是一个双层的存储结构name: {
}
找到NCTable
中的named
表,这个表存储了还有name
的通知
以name
作为key,找到value
,这个value
依然是一个map
map
的结构是以object
作为key,obs
对象为value,这个obs
对象的结构上面已经解释,主要存储了observer & SEL
2、如果name为nil,object不为nil
nameless:{
object: struct Obs {observer, selector}
}
Observation *wildcard
链表中存储Obs {observer, selector}GSNotificationObserver
类型的对象observer
,并把queue
和block
保存下来observer
的didReceiveNotification:
方法,然后在didReceiveNotification:
中把block
抛给指定的queue
去执行name & object
查找到所有的obs
对象(保存了observer
和sel
),放到数组中performSelector:
逐一调用sel
,这是个同步操作name
和object
为维度的,再加上observer
做区分- (void) enqueueNotification: (NSNotification*)notification
postingStyle: (NSPostingStyle)postingStyle
coalesceMask: (NSUInteger)coalesceMask
forModes: (NSArray*)modes {
// 判断是否需要合并通知
if (coalesceMask != NSNotificationNoCoalescing) {
[self dequeueNotificationsMatching: notification
coalesceMask: coalesceMask];
}
switch (postingStyle) {
case NSPostNow:
...
// 如果是立马发送,则调用NSNotificationCenter进行发送
[_center postNotification: notification];
break;
case NSPostASAP:
// 添加到_asapQueue队列,等待发送,队列是双向链表实现
add_to_queue(_asapQueue, notification, modes, _zone);
break;
case NSPostWhenIdle:
// 添加到_idleQueue队列,等待发送,队列是双向链表实现
add_to_queue(_idleQueue, notification, modes, _zone);
break;
}
}
异步通知发送:
runloop
触发某个时机,调用GSPrivateNotifyASAP()
和GSPrivateNotifyIdle()
方法,这两个方法最终都调用了notify()
方法
notify()
所做的事情就是调用NSNotificationCenter
的postNotification:
进行发送通知
依赖runloop
,所以如果在其他子线程使用NSNotificationQueue
,需要开启runloop
方法查找流程
当调用实例对象方法时 , 查找的将是类对象 .
当调用类方法是 , 查找的将是元类对象 .
1、查找本类缓存 , 找到直接跳转到 done;
2、查找本类方法列表 class_data_bits_t
中 , 找到后进行缓存并打印 , 直接跳转到 done;
3、循环遍历父类 , 查找方法缓存 , 找到直接跳转到 done;
4、查找方法列表 , 找到把方法添加到缓存并打印 , 直接跳转到 done;
5、直到遍历结束 superclass 为 nil;
6、判断是否实现 SEL_resolveInstanceMethod
方法,即 +(BOOL)resolveInstanceMethod:(SEL)sel;
7、如果SEL_resolveInstanceMethod方法返回yes向本类发送 SEL_resolveInstanceMethod
消息 , 即调用这个方法;
8、该方法执行完 ,进行 retry,即重新走一遍方法的查找流程;
9、如果SEL_resolveInstanceMethod没有找到方法的实现,进入转发流程;
10、SEL_forwardingTargetForSelector指定一个实现了该方法的对象(不能是self,否则进入死循环);
11、如果指定了target,则进入指定对象的方法查找流程;
12、如果没有指定targe,进入SEL_methodSignatureForSelector,生成一个NSMethodSignature
方法签名并返回;
13、接着对新生成的方法签名进行SEL_forwardInvocation,指派对象来接收这个消息;
Javascript Core:
JSVirtualMachine
一个JSVirtualMachine的实例就是一个完整独立的JavaScript的执行环境,为JavaScript的执行提供底层资源。
这个类主要用来做两件事情:
- 实现并发的JavaScript执行
- JavaScript和Objective-C桥接对象的内存管理
/* 对桥接对象进行内存管理 */ - (void)addManagedReference:(id)object withOwner:(id)owner; /* 取消对桥接对象的内存管理 */ - (void)removeManagedReference:(id)object withOwner:(id)owner;
每一个JavaScript上下文(JSContext对象)都归属于一个虚拟机(JSVirtualMachine)。每个虚拟机可以包含多个不同的上下文,并允许在这些不同的上下文之间传值(JSValue对象)。
然而,每个虚拟机都是完整且独立的,有其独立的堆空间和垃圾回收器(garbage collector ),GC无法处理别的虚拟机堆中的对象,因此你不能把一个虚拟机中创建的值传给另一个虚拟机。
JavaScriptCore API都是线程安全的。你可以在任意线程创建JSValue或者执行JS代码,然而,所有其他想要使用该虚拟机的线程都要等待。
JSContext
一个JSContext对象代表一个JavaScript执行环境。在native代码中,使用JSContext去执行JS代码,访问JS中定义或者计算的值,并使JavaScript可以访问native的对象、方法、函数。
- 如果想并发执行JS,需要使用多个不同的虚拟机来实现。
- 可以在子线程中执行JS代码。
JSContext执行JS代码
- 调用evaluateScript函数可以执行一段
top-level
的JS代码,并可向global对象添加函数和对象定义 - 其返回值是JavaScript代码中最后一个生成的值
JSValue
一个JSValue实例就是一个JavaScript值的引用。使用JSValue类在JavaScript和native代码之间转换数据
每个JSValue实例都来源于一个代表JavaScript执行环境的JSContext对象,这个执行环境就包含了这个JSValue对应的值。每个JSValue对象都持有其JSContext对象的强引用,只要有任何一个与特定JSContext关联的JSValue被持有(retain),这个JSContext就会一直存活。通过调用JSValue的实例方法返回的其他的JSValue对象都属于与最始的JSValue相同的JSContext。