objective-c – 在任何FSEvent或计时器触发之前,NSRunLoop会提前返回

前端之家收集整理的这篇文章主要介绍了objective-c – 在任何FSEvent或计时器触发之前,NSRunLoop会提前返回前端之家小编觉得挺不错的,现在分享给大家,也给大家做个参考。
我正在尝试使用NSRunLoop来监视代理中的FSEvents
应用程序(即没有任何GUI).我想我明白了
RunLoop有效,但我显然没有,因为我看到的行为
是不可理解的.我错过了什么? (我对线程很满意
用几种语言编程,但Objective-C有点新奇
为了我).

下面复制的是(尽可能得到它)最小化的实现
EventHandler类.这是从主函数调用
分配和初始化一个实例,然后发送一个
使用“/ tmp / fussybot-test”启动消息,最后是tidyUp.

下面的实现代码创建,计划和启动附加的事件流
到默认的RunLoop,然后循环,等待runMode:beforeDate
在任何FSEvents上,或在RunLoop的计时器到期时.

#import "EventHandler.h"

void mycallback(ConstFSEventStreamRef streamRef,void *userData,size_t numEvents,void *eventPaths,const FSEventStreamEventFlags eventFlags[],const FSEventStreamEventId eventIds[])
{
    EventHandler *eh = (__bridge EventHandler*)userData;

    size_t i;
    char **paths = eventPaths;
    NSLog(@"callback: %zd events to process...",numEvents);
    for (i=0; i<numEvents; i++) {
        NSLog(@"Event %llu in %s (%x)",eventIds[i],paths[i],eventFlags[i]);
        [eh changedPath:paths[i]];
    }
}

@implementation EventHandler

-(void) startWatching: (NSString*) path
{
    NSRunLoop *theRL = [NSRunLoop currentRunLoop];
    [self createStream:path runLoop:theRL];

    BOOL recentFSActivity_p = YES;
    while (recentFSActivity_p) {

        NSDate* waitEnd = [NSDate dateWithTimeIntervalSinceNow:5.0];
        //NSLog(@"waiting until %@",waitEnd); // XXX
        if (! [theRL runMode:NSDefaultRunLoopMode
                  beforeDate:waitEnd]) {
            NSLog(@"the run loop could not be started");
        }

        int ps = [self pathsSeen];
        NSLog(@"Main loop: pathsSeen=%i",ps);
        if (ps == 0) {
            recentFSActivity_p = NO;
        }
    }
}

- (void) tidyUp
{
    FSEventStreamStop(event_stream);
    FSEventStreamInvalidate(event_stream);
    return;
}


- (FSEventStreamRef) createStream: (NSString*) path
                          runLoop: (NSRunLoop*) theRL
{
    pathsToWatch = [NSArray arrayWithObject:path];
    FSEventStreamContext context = {0,(__bridge void*)self,NULL,NULL};
    CFAbsoluteTime latency = 3.0; /* Latency in seconds */

    /* Create the stream,passing in a callback */
    event_stream = FSEventStreamCreate(NULL,&mycallback,&context,(__bridge CFArrayRef) pathsToWatch,kFSEventStreamEventIdSinceNow,latency,kFSEventStreamCreateFlagNone);

    FSEventStreamScheduleWithRunLoop(event_stream,[theRL getCFRunLoop],kcfRunLoopDefaultMode);
    FSEventStreamStart(event_stream);

    return event_stream;
}

-(void)changedPath:(char *)path
{
    NSLog(@"Path %s changed",path); // log that we got here
    nchangedPaths += 1;              // ...and count the number of calls
}

-(int)pathsSeen
{
    int n = nchangedPaths;      // return instance variable
    nchangedPaths = 0;          // ...and reset it
    return n;
}
@end

好的,所以我们构建它,开始它,然后触摸一个文件
看了目录:

% make && ./fussybot & date '+NOW: %T'; sleep 2; echo hello >/tmp/fussybot-test/hello.txt
cc -c -x objective-c -fobjc-arc -o EventHandler.o EventHandler.m
cc -o fussybot main.o EventHandler.o -framework Cocoa
[1] 57431
NOW: 22:56:54
% 2013-04-22 22:56:57.692 fussybot[57431:707] callback: 1 events to process...
2013-04-22 22:56:57.694 fussybot[57431:707] Event 645428112 in /private/tmp/fussybot-test/ (11400)
2013-04-22 22:56:57.694 fussybot[57431:707] Path /private/tmp/fussybot-test/ changed
2013-04-22 22:56:57.695 fussybot[57431:707] Main loop: pathsSeen=1
2013-04-22 22:56:57.695 fussybot[57431:707] Main loop: pathsSeen=0
Exiting...

[1]  + done       ./fussybot
%

然后我们取消注释NSLog行(@“等到%@”,waitEnd);
(上面标有XXX),我们再试一次:

% make && ./fussybot & date '+NOW: %T'; sleep 2; echo hello >/tmp/fussybot-test/hello.txt
cc -c -x objective-c -fobjc-arc -o EventHandler.o EventHandler.m
cc -o fussybot main.o EventHandler.o -framework Cocoa
[1] 57474
NOW: 22:59:01
2013-04-22 22:59:01.190 fussybot[57474:707] waiting until 2013-04-22 21:59:06 +0000
2013-04-22 22:59:01.190 fussybot[57474:707] Main loop: pathsSeen=0
Exiting...
[1]  + done       ./fussybot
%

现在有两件非常奇怪的事情.

>首先,添加NSLog调用会改变程序的行为.嗯?
>其次在两个示例中,RunLoop似乎立即退出,而不等待FSEvent.

关于第一个,NSLog具有这样的效果的事实是
肯定告诉我一些非常重要的事情,但我不能为生活而努力
我找出了什么.

关于第二个,在每个pathSeen = 0的情况下,
runMode:RunLoop对象上的beforeDate消息没有阻塞,
但是返回YES,即使此消息的文档说明了
它只返回YES“如果运行循环运行并处理输入
来源或达到指定的超时值“,两者都没有
这在pathSeen = 0例中是正确的.在每一个
我希望在pathsSeen = 0 line之前看到5s延迟
因为RunLoop没有看到任何FSEvent,所以会出现阻塞
waitEnd间隔.

这两个特点都表明我误解了一些东西
相当基本的,大概是关于对象的生命周期.我觉得我可以
说明以下各项:

>我确实希望在之前调用NSRunLoop runMode:beforeDate
程序的主线程(该程序没有任何其他内容
在等待的同时做,所以被阻挡是正确的
事情).这与RunLoops的解释兼容
Threading Programming Guide
>每个线程只有一个RunLoop,所以我正在安排
RunLoop上的event_stream我正在等待.
>我通过创建规则拥有event_stream,因此不存在
在我背后回收.
>每次循环循环时waitEnd都会不同 – 即,它就是
没有保留从传球到传球.
>拥有createStream:runLoop初始化一个实例变量
pathsToWatch意味着我不必担心这种消失
在FSEventStreamCreate用此创建流之后
论点. ARC管理层将在年底回收这一计划
方法,如果这是一个局部变量,但不是,因为它是一个
实例变量.
>没有其他事件会导致RunLoop
解除封锁.即使操作系统确实在这个RunLoop上安排了一些东西
(文件似乎小心翼翼地不排除这一点),我
在回调中看到这样的事件.
>第一种情况下事件中的FSEventStreamEventFlags是预期的 –
没有什么可以暗示任何事件已被删除
某些原因.

也就是说,我似乎已经证明这不可行.它
明显不起作用,所以…它是什么我是灾难性的
没得到? (当我确实得到它时,会发出一声巨响
伤害?).

FSEvent API是否代表“基于端口的输入源”
“运行循环事件序列”的术语
Threading Programming Guide
如果是这样,肯定应该在该序列的第7步中接收FSEvent.

上面的代码非常基于示例代码
File System Events API
documentation
.
我认为我的理解与this
thoughtful answer
中的解释是一致的,但是
我找不到许多其他相关的RunLoop问题. SO系统建议的问题主要与专门添加NSTimers而不是使用RunLoop调用的内置计时器有关. @L_404_4@很可能,但(a)没有答案,(b)可能是与DropBox的互动.

这是

% cc --version 
Apple clang version 4.0 (tags/Apple/clang-421.0.60) (based on LLVM 3.1svn)

在OS X 10.8.3上.

(这是一个很长的问题:对不起.通常在你问的时候
问这个问题,你已经为自己找到了答案,但是
– 不 – 我现在和以前一样困惑.)

解决方法

运行循环是共享资源.框架可以并且确实在运行循环上安排自己的运行循环源,尤其是在默认模式下.如果-runMode:beforeDate:返回YES并且它没有处理你的一个源,那么它可能处理了一个由框架调度的源.

如果要以只触发源和计时器的方式运行运行循环,则需要以自定义模式计划源和计时器,并在该模式下运行运行循环.模式实际上只是一个字符串,所以使用像@“com.yourcompany.yourproduct.yourmodename”这样的东西或类似的保证是唯一的东西,你会没事的.

或者您可以简单地编写代码来应对这样一个事实,即并非所有在运行循环中触发的源都是您的.如果要检测超时到期,请安排计时器设置标志并停止运行循环.保持循环直到设置标志.我认为您可以使用计时器方法中的CFRunLoopStop()来强制-runMode:beforeDate:返回,但如果没有,则可以使用-performSelector …方法之一,这些方法可以采用线程或延迟来执行此操作.

猜你在找的cocoa相关文章