Last active
May 5, 2018 05:33
-
-
Save zhangkn/2721a43436676e15c8f8be497dac9901 to your computer and use it in GitHub Desktop.
一个 RunLoop 包含若干个 Mode,每个 Mode 又包含若干个 Source/Timer/Observer;ource0 只包含了一个回调(函数指针),它并不能主动触发事件。使用时,你需要先调用 `CFRunLoopSourceSignal(source)`,将这个 Source 标记为待处理,然后手动调用 `CFRunLoopWakeUp(runloop)` 来唤醒 RunLoop,让其处理这个事件。
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
//获取当前的currentRunLoop,设置sourceCtx;运行runLoop--- | |
//一、source0的例子: 只包含了一个回调(函数指针),它并不能主动触发事件。 | |
// 1)使用时,你需要先调用 `CFRunLoopSourceSignal(source)`,将这个 Source 标记为待处理, | |
// 2)然后手动调用 `CFRunLoopWakeUp(runloop)` 来唤醒 RunLoop,让其处理这个事件perform。 | |
- (void) knrun { | |
@autoreleasepool { | |
//获取当前的NSRunLoop | |
self.runloop = [NSRunLoop currentRunLoop];// | |
//构造CFRunLoopSourceContext数据结构,以便创建CFRunLoopSourceRef | |
CFRunLoopSourceContext sourceCtx = { | |
.version = 0, | |
.info = NULL, | |
.retain = NULL, | |
.release = NULL, | |
.copyDescription = NULL, | |
.equal = NULL, | |
.hash = NULL, | |
.schedule = NULL, | |
.cancel = NULL, | |
.perform = NULL | |
}; | |
//创建CFRunLoopSourceRef | |
CFRunLoopSourceRef source = CFRunLoopSourceCreate(NULL, 0, &sourceCtx);//CF_EXPORT CFRunLoopSourceRef CFRunLoopSourceCreate(CFAllocatorRef allocator, CFIndex order, CFRunLoopSourceContext *context); | |
//CF_EXPORT void CFRunLoopAddSource(CFRunLoopRef rl, CFRunLoopSourceRef source, CFRunLoopMode mode); | |
CFRunLoopAddSource(CFRunLoopGetCurrent(), source, kCFRunLoopDefaultMode);//0、添加source到CurrentRunLoop | |
CFRelease(source);//对source进行释放 | |
//判断是否达到时限 | |
NSDate* expire = [NSDate dateWithTimeIntervalSinceNow:timeout]; | |
while ([self.runloop runMode:NSDefaultRunLoopMode beforeDate:expire]) {//// Runs the loop once, blocking for input in the specified mode until a given date. | |
// 1、 一个 RunLoop 包含若干个 Mode,每个 Mode 又包含若干个 Source/Timer/Observer。 | |
//2、每次调用 RunLoop 的主函数时,只能指定其中一个 Mode,这个Mode被称作 CurrentMode。 | |
//如果需要切换 Mode,只能退出 Loop,再重新指定一个 Mode 进入---这样做主要是为了分隔开不同组的 Source/Timer/Observer,让其互不影响。 | |
if ([expire compare: [NSDate date]] == NSOrderedAscending) {// | |
break;//3、运行runLoop 一次,阻塞当前线程以等待处理一次输入源。 | |
//在处理了第一次到达的输入源或设定的beforeDate到时间后,runLoop 会 exit。 | |
} | |
} | |
[self knwillLeaveRunloop];//4、退出运行循环前的处理 | |
} | |
} | |
// 例子二:二、执行特定perform | |
//0)substrate.h | |
typedef const void *MSImageRef; | |
MSImageRef MSGetImageByName(const char *file); | |
void *MSFindSymbol(MSImageRef image, const char *name); | |
void MSHookFunction(void *symbol, void *replace, void **result); | |
//定义perform的参数类型 | |
typedef struct { | |
CFDictionaryRef user; | |
CFUserNotificationCallBack callout; | |
CFRunLoopSourceRef rls; | |
// CFIndex order; | |
} KNReplaceNotificatioContext; | |
//使用例子:特定条件执行 perform,参数为 KNReplaceNotificatioContext 类型的对象,存储着 user、rls、callout | |
static void perform(void *info) {//执行callout函数 | |
KNReplaceNotificatioContext* ctx = (KNReplaceNotificatioContext*)info; | |
NSDictionary* dict = (NSDictionary*)ctx->user; | |
CFOptionFlags flag = [dict[kCFUserNotificationSelect] integerValue];//从user获取CFOptionFlags | |
ctx->callout(ctx->user, flag);//1、执行callout函数,参数为user 字典和CFOptionFlags | |
/* 清理 source */ | |
if (ctx->rls && CFRunLoopSourceIsValid(ctx->rls)) { | |
CFRunLoopSourceInvalidate(ctx->rls);//执行CFRunLoopSourceInvalidate | |
CFRelease(ctx->rls);//2、设置CFRunLoopSourceRef 引用为0 | |
ctx->rls = nil; | |
} | |
} | |
static void release(const void *info) { | |
KNReplaceNotificatioContext* ctx = (KNReplaceNotificatioContext*)info; | |
ctx->user = nil; | |
ctx->callout = nil; | |
ctx->rls = nil; | |
delete ctx; | |
} | |
MSHookFunction((void *)MSFindSymbol(NULL, "_CFUserNotificationCreateRunLoopSource"), | |
(void *) $CFUserNotificationCreateRunLoopSource, | |
(void **)&CFUserNotificationCreateRunLoopSource); | |
// 1)使用时,需要先调用 `CFRunLoopSourceSignal(source)`,将这个 Source 标记为待处理, | |
// https://developer.apple.com/documentation/corefoundation/1534507-cfusernotificationcreaterunloops?language=objc | |
//Creates a run loop source for a user notification. | |
static CFRunLoopSourceRef (*CFUserNotificationCreateRunLoopSource)(CFAllocatorRef allocator,//The allocator to use to allocate memory for the new object. Pass NULL or kCFAllocatorDefault to use the current default allocator. | |
CFTypeRef userNotification,//The user notification to use. | |
CFUserNotificationCallBack callout,//The callback function to invoke when the user notification dialog is dismissed. | |
CFIndex order);//A priority index indicating the order in which run loop sources are processed. User notifications currently ignore this parameter. Pass 0 for this value. | |
static CFRunLoopSourceRef ($CFUserNotificationCreateRunLoopSource)(CFAllocatorRef allocator, | |
CFTypeRef userNotification, | |
CFUserNotificationCallBack callout, | |
CFIndex order) {// 创建CFRunLoopSourceRef | |
if (CFGetTypeID(userNotification) == CFDictionaryGetTypeID()) { | |
//Source0 只包含了一个回调(函数指针),它并不能主动触发事件。 | |
// 1)使用时,需要先调用 `CFRunLoopSourceSignal(source)`,将这个 Source 标记为待处理, | |
// 2)然后手动调用 `CFRunLoopWakeUp(runloop)` 来唤醒 RunLoop,让其处理这个事件。 | |
NSLog(@" dict:%@", userNotification);//打印sb接受到的通知的信息 | |
CFRunLoopSourceContext context = {0};//Source0context | |
KNReplaceNotificatioContext* kntx = new KNReplaceNotificatioContext();// 作为perform的参数 | |
kntx->user = (CFDictionaryRef)userNotification;//以便传递信息给callout | |
kntx->callout = callout;//以便在perform 进行调用 | |
context.perform = perform;//perform 就是回调(函数指针),当调用 `CFRunLoopWakeUp(runloop)` 来唤醒 RunLoop的时候,将执行此perform | |
context.release = release; | |
context.info = kntx;//new KNReplaceNotificatioContext(); | |
//CFRunLoopSourceRef source = CFRunLoopSourceCreate(NULL, 0, &sourceCtx);//CF_EXPORT CFRunLoopSourceRef CFRunLoopSourceCreate(CFAllocatorRef allocator, CFIndex order, CFRunLoopSourceContext *context); | |
CFRunLoopSourceRef source = CFRunLoopSourceCreate(kCFAllocatorDefault, 0, &context);//创建Source0 | |
CFRunLoopSourceSignal(source);//将这个 Source 标记为待处理 | |
CFRetain(source); | |
kntx->rls = source; | |
return source; | |
} | |
return CFUserNotificationCreateRunLoopSource(allocator, userNotification, callout, order);//不进行处理 | |
} | |
//2)手动调用 `CFRunLoopWakeUp(runloop)` 来唤醒 RunLoop,让其处理这个事件 | |
-(void) knwakeUpForReason:{ | |
if (self.runloop) { | |
CFRunLoopWakeUp([self.runloop getCFRunLoop]);//手动调用 `CFRunLoopWakeUp(runloop)` 来唤醒 RunLoop,让其处理这个事件 | |
NSLog(@"after CFRunLoopWakeUp! :%@", [NSThread currentThread]); | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment