zoukankan      html  css  js  c++  java
  • iOS runloop 自定义输入源

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

    1)输入源要处理的信息
    2)使感兴趣的客户端知道如何和输入源交互的调度例程
    3)处理其他任何客户发送请求的例程
    4)使输入源失效的取消例程
    iOS runloop 自定义输入源 - 勇者之尊 - 勇者之尊
     上图的处理流程:主线程(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 (readonlyCFRunLoopRef 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 = {0selfNULLNULLNULLNULLNULL,

            &RunLoopSourceScheduleRoutine,

            &RunLoopSourceCancelRoutine,

            &RunLoopSourcePerformRoutine};

        runLoopSource = CFRunLoopSourceCreate(NULL0, &context);

        commands = [[NSMutableArray allocinit];

        return self;

    }

    - (void)addToCurrentRunLoop

    {

        CFRunLoopRef runLoop = CFRunLoopGetCurrent();

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

        CFRunLoopAddSource(runLoop, runLoopSourcekCFRunLoopDefaultMode);

    }

    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

    {

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

        CFRunLoopSourceSignal(runLoopSource);

        CFRunLoopWakeUp(runLoop);

    }

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

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

     
     
     
     
     评论这张
     
  • 相关阅读:
    python3与Excel的完美结合
    Python3连接MySQL
    Jmeter用BeanShell Sampler调用java写的jar包进行MD5加密
    Jmeter 接口测试之MD5加密函数(函数助手篇)
    ubuntu16.04安装python3
    解释Crypto模块怎么就这么"皮"?No module named "Crypto"
    python3 django 安装
    未授权访问的缺陷原理的一种可能性
    一篇RPO漏洞挖掘文章翻译加深理解。
    漏洞挖掘技巧之利用javascript:
  • 原文地址:https://www.cnblogs.com/ZGSmile/p/3491707.html
Copyright © 2011-2022 走看看