创建自定义输入源需要定义以下内容

1)输入源要处理的信息
2)使感兴趣的客户端知道如何和输入源交互的调度例程
3)处理其他任何客户发送请求的例程
4)使输入源失效的取消例程

上图的处理流程:主线程(Main Thread)发起任务(Task)给工作线程(Worker Thread),主线程会给命令缓冲区(send command-->Command Buffer),通知输入源(signal source-->Input Source),并唤醒工作线程(Wake Up-->Worker Thread)。工作线程收到唤醒命令,Run Loop会调用输入源的处理程序,由它来执行命令缓冲区中相应的命令。

注:因为主线程和输入源所在工作线程都可以访问命令缓冲区(Command Buffer),因此这些访问必须使同步的
1)定义输入源(The custom input source object definition
下面代码中,定义了RunLoopSource对象,它管理命令缓冲区,并以此来接收其他线程的消息。RunLoopContext对象是一个用来传递RunLoopSource对象(RunLoopSource* source)和“run loop引用”(CFRunLoopRef runLoop)给程序主线程的一个容器
======
// Listing 3-3 The custom input source object definition

@interface RunLoopSource : NSObject

{

CFRunLoopSourceRef runLoopSource;

NSMutableArray* commands;

}

- (id)init;

- (void)addToCurrentRunLoop;

- (void)invalidate;

// handlermethod

- (void)sourecFired;

// Client interface for registering commands to process

- (void)addCommand:(NSInteger)command withData:(id)data;

- (void)fireAllCommandsOnRunLoop:(CFRunLoopRef)runLoop;

@end

// These are the CFRunLoopSourceRef callback functions.

void RunLoopSourceScheduleRoutine (void *info, CFRunLoopRef rl, CFStringRef mode);

void RunLoopSourcePerformRoutine (void *info);

void RunLoopSourceCancelRoutine (void *info, CFRunLoopRef rl, CFStringRef mode);

// RunLoopContext is a container object used during registration of the input source.

@interface RunLoopContext : NSObject

{

CFRunLoopRef runLoop;

RunLoopSource* source;

}

@property (readonly) CFRunLoopRef runLoop;

@property (readonly) RunLoopSource* source;

- (id)initWithSource:(RunLoopSource*)src andLoop:(CFRunLoopRef)loop;

@end

===当将输入源附加到run loop时,调用这个协调调度例程,将源注册到客户端(可以理解为其他线程)

// Listing 3-4 Scheduling a run loop source

//当source添加进runloop的时候,调用此回调方法 <== CFRunLoopAddSource(runLoop, source, mode);

void RunLoopSourceScheduleRoutine (void *info, CFRunLoopRef rl, CFStringRef mode)

{

RunLoopSource* obj = (RunLoopSource*)info;

AppDelegate* del = [AppDelegate sharedAppDelegate];

RunLoopContext* theContext = [[RunLoopContext alloc] initWithSource:obj andLoop:rl];

[del performSelectorOnMainThread:@selector(registerSource:) withObject:theContext waitUntilDone:NO];

}

===在输入源被告知(signal source)时,调用这个处理例程,这儿只是简单的调用了 [obj sourceFired]方法

// Listing 3-5 Performing work in the input source

//当sourcer接收到消息的时候,调用此回调方法(CFRunLoopSourceSignal(source);CFRunLoopWakeUp(runLoop);

void RunLoopSourcePerformRoutine (void *info)

{

RunLoopSource* obj = (RunLoopSource*)info;

[obj sourceFired];

}

===如果使用CFRunLoopSourceInvalidate/CFRunLoopRemoveSource函数把输入源从run loop里面移除的话,系统会调用这个取消例程,并且把输入源从注册的客户端(可以理解为其他线程)里面移除

// Listing 3-6 Invalidating an input source ==

//当source 从runloop里删除的时候,调用此回调方法 <== CFRunLoopRemoveSource(runLoop, source, mode);

void RunLoopSourceCancelRoutine (void *info, CFRunLoopRef rl, CFStringRef mode)

{

RunLoopSource* obj = (RunLoopSource*)info;

AppDelegate* del = [AppDelegate sharedAppDelegate];

RunLoopContext* theContext = [[RunLoopContext alloc] initWithSource:obj andLoop:rl];

[del performSelectorOnMainThread:@selector(removeSource:) withObject:theContext waitUntilDone:YES];

}

2)安装输入源到Run Loop---分两步首先初始化一个输入源,然后将这个输入源添加到当前Run Loop里面

// List 3-7 Installing the run loop source

- (id)init

{

/*

// Setup the context.

context.version = 0;

context.info = self;

context.retain = NULL;

context.release = NULL;

context.copyDescription = CFCopyDescription;

context.equal = CFEqual;

context.hash = CFHash;

context.schedule = RunLoopSourceScheduleRoutine;

context.cancel = RunLoopSourceCancelRoutine;

context.perform = RunLoopSourcePerformRoutine;

*/

CFRunLoopSourceContext context = {0, self, NULL, NULL, NULL, NULL, NULL,

&RunLoopSourceScheduleRoutine,

&RunLoopSourceCancelRoutine,

&RunLoopSourcePerformRoutine};

runLoopSource = CFRunLoopSourceCreate(NULL, 0, &context);

commands = [[NSMutableArray alloc] init];

return self;

}

- (void)addToCurrentRunLoop

{

CFRunLoopRef runLoop = CFRunLoopGetCurrent();

//Add the new CFRunLoopSourceRef to the indicated runloop, 并且回调RunLoopSourceScheduleRoutine函数

CFRunLoopAddSource(runLoop, runLoopSource, kCFRunLoopDefaultMode);

}

3)协调输入源的客户端(将输入源注册到客户端)

输入源的主要工作就是将与输入源相关联的线程置于休眠状态,直到有事件发生。要达到这个目的,首先客户端(其他线程)知道有该输入源信息,并且有办法与之通信。

通知客户端关于这个输入源信息的方法之一,就是当该输入源开始安装到你的run loop上面后发送注册请求给相关的客户端,该输入源可以注册到任意数量的客户端

也可以通过由代理将输入源注册到感兴趣的客户端

===下面显示了应用委托(AppDelegate)定义的注册方法及从应用委托(AppDelegate)中移除的方法

- (void)registerSource:(RunLoopContext *)sourceInfo

{

NSMutableArray *sourceToPing;

[sourceToPing addObject:sourceInfo];

}

- (void)removeSource:(RunLoopContext *)sourceInfo

{

id objToRemove = nil;

NSMutableArray *sourceToPing;

for(RunLoopContext *context in sourceToPing)

{

if([context isEqual:sourceInfo])

{

objToRemove = context;

break;

}

}

if(objToRemove)

{

[sourceToPing removeObject:objToRemove];

}

}

注:上面两个函数分别在RunLoopSourceScheduleRoutine/RunLoopSourceCancelRoutine函数中被调用

4)通知输入源---客户端(其他线程)发数据到输入源,分两步首先发信号给输入源(signal source),然后唤醒输入源的run loop

===下面显示了客户端发送数据到输入源的方法,在本例中这个方法被放在RunLoopSource对象里面

// Listing 3-9 Waking up the run loop

- (void)fireAllCommandsOnRunLoop:(CFRunLoopRef)runLoop

{

//当手动调用此方法的时候,将会触发 RunLoopSourceContext的performCallback

CFRunLoopSourceSignal(runLoopSource);

CFRunLoopWakeUp(runLoop);

}

注:1,输入源就是一类事件(命令)处理机制。他是线程间的事件(命令)异步通讯机制,所以不能试图通过这个机制实现进程间的通讯。

2,因为CFRunLoopWakeUp函数不是信号安全的,所以对run loop的唤醒,不能在应用信号处理例程(RunLoopSourcePerformRoutine)里面使用。

 
 
 
 
 评论这张
 

iOS runloop 自定义输入源的更多相关文章

  1. run loop 输入源

    做了一年多的IOS开发,对IOS和Objective-C深层次的了解还十分有限,大多还停留在会用API的级别,这是件挺可悲的事情.想学好一门语言还是需要深层次的了解它,这样才能在使用的时候得心应手,出 ...

  2. ios runloop学习

    今天突然才之间才意识到NSTimer这样的运行方式,是在多线程中实现的循环还是在主线程中去实现的呢.当然不可能是在主线程中的while那么简单,那样什么都干不了,简单看了下NSTimer是以同步方式运 ...

  3. 修改VCL源码实现自定义输入对话框

    来自:https://yq.aliyun.com/wenji/88428 通过修改VCL源码实现自定义输入对话框 在BCB中有两个函数可以实现输入对话框:InputBox和InputQuery,其实I ...

  4. iOS Runloop理解

    一.RunLoop的定义 当有持续的异步任务需求时,我们会创建一个独立的生命周期可控的线程.RunLoop就是控制线程生命周期并接收事件进行处理的机制. RunLoop是iOS事件响应与任务处理最核心 ...

  5. iOS RunLoop详解

    1. RunLoop简介 1.1 什么是RUnLoop 可以理解为字面的意思:Run表示运行,Loop表示循环.结合在一起就是运行的循环.通常叫做运行循环. RunLoop实际上是一个对象,这个对象在 ...

  6. iOS runLoop 原理多线程 总结 NSTimer优化

    可以理解为字面意思:Run 表示运行,Loop 表示循环.结合在一起就是运行的循环的意思.哈哈,我更愿意翻译为『跑圈』.直观理解就像是不停的跑圈. RunLoop 实际上是一个对象,这个对象在循环中用 ...

  7. iOS RunLoop简介

    一.什么是RunLoop? RunLoop是运行循环,每个Cocoa应用程序都由一个处于阻塞状态的do/while循环驱动,当有事件发生时,就把事件分派给合适的监听器,如此反复直到循环停止.处理分派的 ...

  8. IOS RunLoop浅析 二

    上一篇我们说了runloop 的几种模式,那么我们在模式中又要做些什么呢??? 模式中有三个模块: 事件源(输入源) Source Source: 按照官方文档分类 Port-Based Custom ...

  9. [转载]SharePoint 2013搜索学习笔记之自定义结果源

    搜索中心新建好之后在搜索结果页上会默认有所有内容,人员,对话,视频这四个结果分类,每个分类会返回指定范围的搜索结果,这里我再添加了部门日志结果分类,搜索这个分类只会返回部门日志内容类型的搜索结果,要实 ...

随机推荐

  1. 在vim中的复制,剪切,粘贴

    1. 剪切和粘贴 定位鼠标到剪切的开始位置 输入v键开始选择剪切的字符,或者V键是为了选择 整行 移动方向键到结束的地方 d键是剪切,y键是复制 移动鼠标到粘贴的位置 输入P是在鼠标位置前粘贴,输入p ...

  2. 原生的 promise 的局限性

    本文来自:https://ekyu.moe/article/limits-of-native-promise-and-async-await/ 众所周知,Nodejs 已原生支持 Promise 和 ...

  3. [BZOJ2282]消防

    Description 某个国家有n个城市,这n个城市中任意两个都连通且有唯一一条路径,每条连通两个城市的道路的长度为zi(zi<=1000). 这个国家的人对火焰有超越宇宙的热情,所以这个国家 ...

  4. C++ 读取文件所有内容的方法

    方法一 #include <fstream> #include <string> #include <iostream> using namespace std; ...

  5. 【分类】AlexNet论文总结

    目录 0. 论文链接 1. 概述 2. 对数据集的处理 3. 网络模型 3.1 ReLU Nonlinearity 3.2 Training on multiple GPUs 3.3 Local Re ...

  6. DataReader 连接数据库完整过程和代码(Sql Server)

    数据库名叫:Bu 有个表:A 里面有一列:ID 需要引用 using System.Data.SqlClient; 代码部分如下: SqlConnection sqlCon=new SqlConnec ...

  7. Java RMI 简单示例

    一.创建远程服务 1.创建 Remote 接口,MyRemote.java import java.rmi.*; public interface MyRemote extends Remote{ p ...

  8. Python实现单链表

    定义链表结构: class ListNode: def __init__(self, x): self.val = x self.next = None 输出该链表l1的元素: while l1: p ...

  9. CountDownLatch await可能存在的问题

    执行countdown的某个子线程可能会因为某些原因无法执行countdown,这样就会导致await线程一直阻塞下去. 在线程池中多次调用await方法,因为await方法会阻塞一段时间,有可能导致 ...

  10. no crontab for root 解决方案

    root用户下 输入 crontab -l 显示 no crontab for root  例如: [root@localhost ~]# crontab -l no crontab for root ...