RunTime和Runloop的理解
RunTime
Runtime是一套比较底层的纯C语言API,他将更多的决策从编译时他UI吃到了运行时,我们平时写的iOS代码在程序运行的过程中都会转成Runtime的C语言代码,运行时系统充当着OC的操作系统,他使语言能够工作
我们平时用Runtime可以给系统类动态添加方法属性方法交换等
objc_msg_send
oc中函数调用在底层是改为了objc_msg_send方法
sel:类成员方法的指针
imp:函数指针保存了方法地址
method: 不透明模型里面包括sel和imp和methodtype 一个类(Class)持有一个分发表,在运行期分发消息,表中的每一个实体代表一个方法(Method),它的名字叫做选择子(SEL),对应着一种方法实现(IMP)。
struct objc_method {
SEL method_name;
char *method_types;
IMP method_imp;
};
Person * p = [[Person alloc] init];
[p run];
其中run方法被编译成了 sel_registerName("run"),然后该方法会经过一些类 sel查找IMP的过程
首先是先进行快速查找
每个类的构成有一个cache,存储类的selector和IMP,IMP和selector会组成一个哈希表,然后通过hash直接查找会非常快,当查找一个方法时,首先找cache,如果有会直接返回,如果没有则会经历一个复杂而又缓慢(慢速)的过程,找到了会继续往cache里面存
当执行objc_msg_send时
会先执行 Lookup_NilOrTagged 进行指针优化然后执行 LGetIsaDone
然后进行CacheLookup Normal流程 如果缓存里有则call imp 如果没有则调用objc_msgSend_uncached
此时进入慢速查找
MethodTableLookup
__class_lookupMethodAndLoadCache3
lookupImpOrFowrward
如果缓存存在往缓存里取IMP,如果不存在往下走判断类是不是已知类然后在走相应的流程 ,判断完毕后依然需要从缓存里面取一遍,这样做事为了并发设置及的以及remap(cls)重映射,进行自己->父类->NSObject->getMethodNoSuper_nolock->log_and_fill_cache(将该IMP缓存)
如果都没有找到那么进入到消息转发机制
resolveinstanceMethod 返回true 并添加新的消息进行处理添加方法重新进入慢速查找
没有处理则出发快速转发机制forwardTarget出发消息转发机制来实现快速转发,如果依然没有处理则进行慢速转发,方法签名+消息签名
isa走位
我们创建的一个对象或实例其实就是一个struct objc_object结构体,而我们常用的id也就是这个结构体的指针。这个结构体只有一个成员变量,这是一个Class类型的变量isa,也是一个结构体指针,那这个指针又指向什么呢?面向对象中每一个对象都必须依赖一个类来创建,因此对象的isa指针就指向对象所属的类根据这个类模板能够创建出实例变量、实例方法等。
struct objc_classs结构体里存放的数据称为元数据(metadata),通过成员变量的名称我们可以猜测里面存放有指向父类的指针、类的名字、版本、实例大小、实例变量列表、方法列表、缓存、遵守的协议列表等,这些信息就足够创建一个实例了,该结构体的第一个成员变量也是isa指针,这就说明了Class本身其实也是一个对象,我们称之为类对象,类对象在编译期产生用于创建实例对象
RunLoop
Runlopp是苹果推出的一个消息处理机制,一般情况下线程处理完任务就会退出,但是因为runloop在线程中构成了一个消息循环,使其一直有任务执行。然后线程不会退出,这个机制叫做Runloop
Runlopp有多个model里,但他只能在一个model里运行,model里有items,其中items包括timer,observer,source0,source1,source0处理的app的周期事件比如点击等,souce1主要是用来进行跨线程通讯比较偏底层
源码分析
Runloop工作流程是
1.通知Observer即将进入Runloop
2.通知Observer处理timer
3.通知Observer处理source
4.处理Source0
5.如果有Souce1 跳到第9步
6.通知observer线程即将休眠
7.休眠等待唤醒
8.处理唤醒时收到的消息回到2
9.通知observer,即将推出Runloop
下面通过源码的形势来进行分析
首先外部调用 [NSRunloop current] run]; 底层调用
void CFRunLoopRun(void) { /* DOES CALLOUT */
int32_t result;
do {
result = CFRunLoopRunSpecific(CFRunLoopGetCurrent(), kCFRunLoopDefaultMode, 1.0e10, false);
CHECK_FOR_FORK();
} while (kCFRunLoopRunStopped != result && kCFRunLoopRunFinished != result);
}
Runloop源码第2678行
struct _block_item {
struct _block_item *_next;
CFTypeRef _mode; /// CFString or CFSet
void (^_block)(void);
};
typedef struct _per_run_data {
uint32_t a;
uint32_t b;
uint32_t stopped;
uint32_t ignoreWakeUps;
} _per_run_data;
struct __CFRunLoop {
CFRuntimeBase _base;
pthread_mutex_t _lock; /* locked for accessing mode list */
__CFPort _wakeUpPort; /// used for CFRunLoopWakeUp
Boolean _unused;
volatile _per_run_data *_perRunData; /// reset for runs of the run loop
pthread_t _pthread;
uint32_t _winthread;
CFMutableSetRef _commonModes;
CFMutableSetRef _commonModeItems;
CFRunLoopModeRef _currentMode;
CFMutableSetRef _modes;
struct _block_item *_blocks_head;
struct _block_item *_blocks_tail;
CFAbsoluteTime _runTime;
CFAbsoluteTime _sleepTime;
CFTypeRef _counterpart;
};
此时进入do while循环 里面一直执行 CFRunLoopRunSpecific函数下面分析这个函数
SInt32 CFRunLoopRunSpecific(CFRunLoopRef rl, CFStringRef modeName, CFTimeInterval seconds, Boolean returnAfterSourceHandled) { /* DOES CALLOUT */
CHECK_FOR_FORK();
///判断rloop是否被释放释放的话返回 kCFRunLoopRunFinished 结束runloop
if (__CFRunLoopIsDeallocating(rl)) return kCFRunLoopRunFinished;
__CFRunLoopLock(rl);
///取当前runnloop的model如果model为nil或为空那么退出runloop
CFRunLoopModeRef currentMode = __CFRunLoopFindMode(rl, modeName, false);
if (NULL == currentMode || __CFRunLoopModeIsEmpty(rl, currentMode, rl->_currentMode)) {
Boolean did = false;
if (currentMode) __CFRunLoopModeUnlock(currentMode);
__CFRunLoopUnlock(rl);
return did ? kCFRunLoopRunHandledSource : kCFRunLoopRunFinished;
}
///如果走到这里说明渠道了runloop‘
///_per_run_data是用来描述当前CFRunLoop的状态的,有三种状态:初始状态,wake,stop三种。注意到 volatile 这个关键字,它的意思是告诉编译器不要优化这个变量,要每次都从内存中读取该变量。
volatile _per_run_data *previousPerRun = __CFRunLoopPushPerRunData(rl);
CFRunLoopModeRef previousMode = rl->_currentMode;
rl->_currentMode = currentMode;
int32_t result = kCFRunLoopRunFinished;
///通知observer进入runloop
if (currentMode->_observerMask & kCFRunLoopEntry ) __CFRunLoopDoObservers(rl, currentMode, kCFRunLoopEntry);
///因为__CFRunLoopRun 处理observer timer souce 等
result = __CFRunLoopRun(rl, currentMode, seconds, returnAfterSourceHandled, previousMode);
///通知observer退出runloop
if (currentMode->_observerMask & kCFRunLoopExit ) __CFRunLoopDoObservers(rl, currentMode, kCFRunLoopExit);
__CFRunLoopModeUnlock(currentMode);
__CFRunLoopPopPerRunData(rl, previousPerRun);
rl->_currentMode = previousMode;
__CFRunLoopUnlock(rl);
return result;
}
我们看出处理Runloop的函数全部都在__CFRunLoopRun函数里
/// 内部函数,进入loop
__CFRunLoopRun(runloop, currentMode, seconds, returnAfterSourceHandled) {
Boolean sourceHandledThisLoop = NO;
int retVal = 0;
do {
__CFRunLoopUnsetIgnoreWakeUps ///唤醒runloop
/// 2. 通知 Observers: RunLoop 即将触发 Timer 回调。
__CFRunLoopDoObservers(runloop, currentMode, kCFRunLoopBeforeTimers);
/// 3. 通知 Observers: RunLoop 即将触发 Source0 (非port) 回调。
__CFRunLoopDoObservers(runloop, currentMode, kCFRunLoopBeforeSources);
/// 执行被加入的block
__CFRunLoopDoBlocks(runloop, currentMode);
/// 4. RunLoop 触发 Source0 (非port) 回调。
sourceHandledThisLoop = __CFRunLoopDoSources0(runloop, currentMode, stopAfterHandle);
/// 执行被加入的block
__CFRunLoopDoBlocks(runloop, currentMode);
/// 5. 如果有 Source1 (基于port) 处于 ready 状态,直接处理这个 Source1 然后跳转去处理消息。
if (__Source0DidDispatchPortLastTime) {
Boolean hasMsg = __CFRunLoopServiceMachPort(dispatchPort, &msg)
if (hasMsg) goto handle_msg;
}
/// 通知 Observers: RunLoop 的线程即将进入休眠(sleep)。
if (!sourceHandledThisLoop) {
__CFRunLoopDoObservers(runloop, currentMode, kCFRunLoopBeforeWaiting);
}
/// 7. 调用 mach_msg 等待接受 mach_port 的消息。线程将进入休眠, 直到被下面某一个事件唤醒。
/// ? 一个基于 port 的Source 的事件。
/// ? 一个 Timer 到时间了
/// ? RunLoop 自身的超时时间到了
/// ? 被其他什么调用者手动唤醒
__CFRunLoopServiceMachPort(waitSet, &msg, sizeof(msg_buffer), &livePort) {
mach_msg(msg, MACH_RCV_MSG, port); /// thread wait for receive msg
}
/// 8. 通知 Observers: RunLoop 的线程刚刚被唤醒了。
__CFRunLoopDoObservers(runloop, currentMode, kCFRunLoopAfterWaiting);
/// 收到消息,处理消息。
handle_msg:
/// 9.1 如果一个 Timer 到时间了,触发这个Timer的回调。
if (msg_is_timer) {
__CFRunLoopDoTimers(runloop, currentMode, mach_absolute_time())
}
/// 9.2 如果有dispatch到main_queue的block,执行block。
else if (msg_is_dispatch) {
__CFRUNLOOP_IS_SERVICING_THE_MAIN_DISPATCH_QUEUE__(msg);
}
/// 9.3 如果一个 Source1 (基于port) 发出事件了,处理这个事件
else {
CFRunLoopSourceRef source1 = __CFRunLoopModeFindSourceForMachPort(runloop, currentMode, livePort);
sourceHandledThisLoop = __CFRunLoopDoSource1(runloop, currentMode, source1, msg);
if (sourceHandledThisLoop) {
mach_msg(reply, MACH_SEND_MSG, reply);
}
}
/// 执行加入到Loop的block
__CFRunLoopDoBlocks(runloop, currentMode);
if (sourceHandledThisLoop && stopAfterHandle) {
/// 进入loop时参数说处理完事件就返回。
retVal = kCFRunLoopRunHandledSource;
} else if (timeout) {
/// 超出传入参数标记的超时时间了
retVal = kCFRunLoopRunTimedOut;
} else if (__CFRunLoopIsStopped(runloop)) {
/// 被外部调用者强制停止了
retVal = kCFRunLoopRunStopped;
} else if (__CFRunLoopModeIsEmpty(runloop, currentMode)) {
/// source/timer/observer一个都没有了
retVal = kCFRunLoopRunFinished;
}
/// 如果没超时,mode里没空,loop也没被停止,那继续loop。
} while (retVal == 0);
}
所以runloop的运行流程全部体现在代码及注释里。
在源码里我们发现__CFRunLoopDoBlocks使用来处理source0 timer 等
static Boolean __CFRunLoopDoBlocks(CFRunLoopRef rl, CFRunLoopModeRef rlm) { /// Call with rl and rlm locked
if (!rl->_blocks_head) return false;
if (!rlm || !rlm->_name) return false;
Boolean did = false;
struct _block_item *head = rl->_blocks_head;
struct _block_item *tail = rl->_blocks_tail;
rl->_blocks_head = NULL;
rl->_blocks_tail = NULL;
CFSetRef commonModes = rl->_commonModes;
CFStringRef curMode = rlm->_name;
__CFRunLoopModeUnlock(rlm);
__CFRunLoopUnlock(rl);
struct _block_item *prev = NULL;
struct _block_item *item = head;
while (item) {
struct _block_item *curr = item;
item = item->_next;
Boolean doit = false;
if (CFStringGetTypeID() == CFGetTypeID(curr->_mode)) {
/// timer 加入的mode 和 我们现在runloop的mode 相等
/// curr->_mode = kCFRunLoopCommonModes 相等
/// 事务就能执行
doit = CFEqual(curr->_mode, curMode) || (CFEqual(curr->_mode, kCFRunLoopCommonModes) && CFSetContainsValue(commonModes, curMode));
} else {
doit = CFSetContainsValue((CFSetRef)curr->_mode, curMode) || (CFSetContainsValue((CFSetRef)curr->_mode, kCFRunLoopCommonModes) && CFSetContainsValue(commonModes, curMode));
}
if (!doit) prev = curr;
if (doit) {
if (prev) prev->_next = item;
if (curr == head) head = item;
if (curr == tail) tail = prev;
void (^block)(void) = curr->_block;
CFRelease(curr->_mode);
free(curr);
if (doit) {
__CFRUNLOOP_IS_CALLING_OUT_TO_A_BLOCK__(block);
did = true;
}
Block_release(block); /// do this before relocking to prevent deadlocks where some yahoo wants to run the run loop reentrantly from their dealloc
}
}
__CFRunLoopLock(rl);
__CFRunLoopModeLock(rlm);
if (head) {
tail->_next = rl->_blocks_head;
rl->_blocks_head = head;
if (!rl->_blocks_tail) rl->_blocks_tail = tail;
}
return did;
}
最终函数通过CFRUNLOOP_IS_CALLING_OUT_TO_A_BLOCK(block);进行回调
addTimer和addobserver源码就是经过一系列判断然后将observer复制给runloop的currentmodel的observer/timer
void CFRunLoopAddObserver(CFRunLoopRef rl, CFRunLoopObserverRef rlo, CFStringRef modeName) {
CHECK_FOR_FORK();
CFRunLoopModeRef rlm;
if (__CFRunLoopIsDeallocating(rl)) return;
if (!__CFIsValid(rlo) || (NULL != rlo->_runLoop && rlo->_runLoop != rl)) return;
__CFRunLoopLock(rl);
if (modeName == kCFRunLoopCommonModes) {
CFSetRef set = rl->_commonModes ? CFSetCreateCopy(kCFAllocatorSystemDefault, rl->_commonModes) : NULL;
if (NULL == rl->_commonModeItems) {
rl->_commonModeItems = CFSetCreateMutable(kCFAllocatorSystemDefault, 0, &kCFTypeSetCallBacks);
}
CFSetAddValue(rl->_commonModeItems, rlo);
if (NULL != set) {
CFTypeRef context[2] = {rl, rlo};
/* add new item to all common-modes */
CFSetApplyFunction(set, (__CFRunLoopAddItemToCommonModes), (void *)context);
CFRelease(set);
}
} else {
rlm = __CFRunLoopFindMode(rl, modeName, true);
if (NULL != rlm && NULL == rlm->_observers) {
rlm->_observers = CFArrayCreateMutable(kCFAllocatorSystemDefault, 0, &kCFTypeArrayCallBacks);
}
if (NULL != rlm && !CFArrayContainsValue(rlm->_observers, CFRangeMake(0, CFArrayGetCount(rlm->_observers)), rlo)) {
Boolean inserted = false;
for (CFIndex idx = CFArrayGetCount(rlm->_observers); idx--; ) {
CFRunLoopObserverRef obs = (CFRunLoopObserverRef)CFArrayGetValueAtIndex(rlm->_observers, idx);
if (obs->_order <= rlo->_order) {
CFArrayInsertValueAtIndex(rlm->_observers, idx + 1, rlo);
inserted = true;
break;
}
}
if (!inserted) {
CFArrayInsertValueAtIndex(rlm->_observers, 0, rlo);
}
rlm->_observerMask |= rlo->_activities;
__CFRunLoopObserverSchedule(rlo, rl, rlm);
}
if (NULL != rlm) {
__CFRunLoopModeUnlock(rlm);
}
}
__CFRunLoopUnlock(rl);
}