我在iPhone上使用AsyncSocket与服务器通信.AsyncSocket基于运行循环,但我的应用程序基于线程.这意味着,我启动一个新线程来写入数据,并等待直到在同一个线程上收到响应.但我不能直接从另一个线程调用AsyncSocket的方法,我必须使用:
[self performSelectorOnMainThread:@selector(writeSomeData:) withObject:dataToWrite waitUntilDone:YES];
Run Code Online (Sandbox Code Playgroud)
它确实有效,但我无法通过这种方式调用我的方法"writeSomeData:",因为performSelectorOnMainThread不返回任何内容.
方法writeSomeData:做这样的事情:
-(NSData *)writeData:(NSData *)dataToWrite {
dataReceived = nil; // AsyncSocket writes data to this variable
[asyncSocket writeData:dataToWrite withTimeout:-1 tag:0];
[asyncSocket readDataToData:[@"<EOF" dataUsingEncoding:NSUTF8StringEncoding] withTimeout:-1 tag:0];
int counter = 0;
while (dataReceived == nil && counter < 5) {
// runLoop is [NSRunLoop currentRunloop]
[runLoop runMode:NSDefaultRunLoopMode beforeDate:[NSDate dateWithTimeIntervalSinceNow:0.3]];
++counter;
}
return [dataReceived copy];
}
Run Code Online (Sandbox Code Playgroud)
我可以通过访问类变量"dataReceived"获得响应,但此时内容已更改.
任何人都可以告诉我如何在不同的线程上使用AsyncSocket(或者一般来说,如何处理基于运行循环的类),这样如果我调用该类的方法,它会一直阻塞,直到执行该方法并收到响应?
谢谢.
如何防止用户滚动表格导致NSTimer延迟?
我找到了答案:
我有一个计时器,重复约8或9次,间隔为0.4到0.8秒.我不需要太多的精度,但如果用户滚动表,计时器将停止工作,直到表完成滚动(这可能是几秒钟等待!).我以为我需要后台线程,但是后台线程上的定时器实现起来有些复杂.
我的问题的答案非常简单容易.我只需要在调用计时器后添加一行:
//////////// start the timer
self.playingTimer = [NSTimer scheduledTimerWithTimeInterval:tempo target:self selector:@selector(playSoundFromArray:) userInfo:nil repeats:YES];
//////////// the magic line:
[[NSRunLoop currentRunLoop] addTimer:self.playingTimer forMode:UITrackingRunLoopMode];
Run Code Online (Sandbox Code Playgroud)
现在我可以根据需要滚动表格,我的计时器工作正常!
现在我需要再研究一下NSRunLoop ......
我试图更好地了解队列及其工作原理。这个片段是为了测试他们的行为:
- (void)dispatchQueueTest
{
NSLog( @"Begin test on %@ thread", [NSThread isMainThread] ? @"main" : @"other" );
dispatch_semaphore_t s = dispatch_semaphore_create(0);
dispatch_async( dispatch_get_main_queue(), ^{
NSLog( @"Signalling semaphore" );
dispatch_semaphore_signal(s);
});
NSLog( @"Waiting for worker" );
while( dispatch_semaphore_wait( s, DISPATCH_TIME_NOW ) ) {
NSDate* timeout = [NSDate dateWithTimeIntervalSinceNow:10.f];
// Process events on the run loop
[[NSRunLoop currentRunLoop] runMode:NSDefaultRunLoopMode beforeDate:timeout];
}
dispatch_release(s);
NSLog( @"All sync'd up" );
}
Run Code Online (Sandbox Code Playgroud)
正如预期的那样,它会在日志中生成以下内容:
Begin test on main thread
Waiting for worker
Signalling semaphore
All sync'd up …Run Code Online (Sandbox Code Playgroud) 主线程及其runloop中的代码如何交互?例如,主线程中的所有代码是否必须运行,直到它在进入runloop之前处于空闲状态?或者runloop在主线程中执行代码的过程中检查其源代码?runloop源是否有可能阻止主线程代码执行(因为它在同一个线程上运行)?
我试图理解主要线程代码如何适应在宏观方案中的runloop图片(反之亦然).
这是一个runloop与我们的代码一起的样子:
主线程:
- runloop以特定间隔运行
- runloop完成运行,我们的代码运行
- 我们的代码运行完毕,转到(1)(如果我们的代码运行得太久以至于runloop没有机会运行怎么办?)
我没有明确地将定时器添加到runloop并且它工作得很好.有一天,当我阅读一些文章时NSRunLoop,它说最好将一个NSTimer实例添加到runloop实例中来执行.我只是想知道如果我不这样做会有什么危害吗?
我的目标是使用 Swift 解析大型 XML 文件 (20 GB)。NSXMLParser 和与 Swift 对象的桥接存在一些性能问题,因此我正在考虑多线程。具体有以下划分:
应按顺序解析数据,因此应保持输入顺序。我的想法是在 1 和 2 上运行 NSRunLoop,允许并行处理而不会阻塞。根据Apple的文档,线程之间的通信可以通过调用来实现performSelector:onThread:withObject:waitUntilDone:。然而这个符号在 Swift 中不可用。
我认为 GCD 不适合作为解决方案。两个工作线程都应该是长时间运行的进程,新工作以随机间隔进入。
如何使用 Swift 实现上述目标(例如,多线程上的 NSRunLoops)?
我知道当你为应用程序提供后台时,计时器会停止运行.但是,当你从后台回来时的行为是什么?计时器是否保持其原始状态fireDate?
我遇到了一个问题,在从背景返回时,NSTimer设置为在十分钟内启动,有时我applicationWillEnterForeground:甚至会在调用之前立即启动计时器.当它没有触发并且applicationWillEnterForeground:被调用时,我有一些逻辑可以通过比较计时器fireDate和当前日期来检查是否已经过了10分钟,但是,即使已经过了10分钟,它总是给我一个值从当前日期开始约525秒.
我的理论如下:
一个NSTimer在未来从背景后面会触发计时器,只要你还没有比原先计划更多的错过了预定时间.(在这种情况下,我们将计时器安排为10分钟,只要您在后台进行10-20分钟,它将在重新进入应用程序时触发.)
如果超过该值,则不会触发,而是在您返回时在将来的同一时间段内安排另一个计时器.(AKA如果您在10分钟的计时器中已经在后台工作了20多分钟,当您回来时,它会在您返回应用程序之后将计时器安排在10分钟内.)
有没有人有关于这种行为的任何文件?我的理论完全基于观察,关于当背景要么严重缺乏或者我无法找到它时会发生什么的文档.
使用新的Combine框架时,您可以指定用于从发布服务器接收元素的调度程序。
在将发布者分配给UI元素时RunLoop.main,DispatchQueue.main在这种情况下,两者之间有很大区别吗?第一个返回主线程的运行循环,第二个返回与主线程关联的队列。
我已经制作了一个带有计时器的RunLoop,它可以更新显示倒计时的标签.倒计时到零时我需要RunLoop停止,对于计时器正常结束的情况,我可以使用runUntilDate,日期是当前日期+倒计时时间.问题是当用户在按钮完成之前取消倒计时.我不知道如何告诉RunLoop停止取消按钮操作.这是RunLoop的代码:
NSInvocation *invocation = [NSInvocation invocationWithMethodSignature:
[self methodSignatureForSelector:@selector(updateCountdownLabel:)]];
[invocation setTarget:self];
[invocation setSelector:@selector(updateCountdownLabel:)];
[[NSRunLoop mainRunLoop] addTimer:[NSTimer timerWithTimeInterval:1 invocation:invocation repeats:YES] forMode:NSRunLoopCommonModes];
Run Code Online (Sandbox Code Playgroud)
该方法只是告诉标签在每个循环中减少1.
我可以告诉取消按钮将标签更改为零,并让运行循环选择器检查值是否为零,但RunLoop自己的选择器是否可以告诉它停止?
cancelPerformSelector:target:argument:
cancelPerformSelectorsWithTarget:
Run Code Online (Sandbox Code Playgroud)
这些是我发现的最接近但它们似乎不是从RunLoops自己的选择器内部工作,或者至少不是我尝试过它们的任何方式.
基本上我需要让按钮告诉RunLoop停止,或以某种方式阻止RunLoop从它自己的选择器.
谢谢.
我正在尝试使用该Perfect库在我的Ubuntu(Ubuntu 15.10 wily,Swift swift-3.0.1-RELEASE)上使用Swift创建一个应用程序.
我希望每X秒都有一个函数.为此,我正在使用模块的Timer类Foundation:
class MyTimer {
init() {
var timer = Timer.scheduledTimer(timeInterval: 1, target: self, selector: #selector(MyTimer.onTimer(timer:)), userInfo: nil, repeats: true)
}
@objc func onTimer(timer: Timer) {
print("MyTimer.onTimer")
}
}
Run Code Online (Sandbox Code Playgroud)
尽管使用此代码找到了几个解决方案,但编译失败了:
$> swift build
Compile Swift Module 'my-app' (7 sources)
/home/.../Sources/MyTimer.swift:8:16: error: method cannot be marked @objc because the type of the parameter cannot be represented in Objective-C
@objc func onTimer(timer: Timer) {
Run Code Online (Sandbox Code Playgroud)
如果我正在扩展我的类NSObject或者如果我删除了参数,则会出现另一个编译错误timer:
$> …Run Code Online (Sandbox Code Playgroud)