Skip to content

Instantly share code, notes, and snippets.

@zhangkn
Last active May 5, 2018 05:33
Show Gist options
  • Star 1 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save zhangkn/2721a43436676e15c8f8be497dac9901 to your computer and use it in GitHub Desktop.
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,让其处理这个事件。
//获取当前的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