标签: runloop

有没有办法让drawRect现在正常工作?

原来的问题...............................................

如果您是drawRect的高级用户,您将知道当然"drawRect"在"所有处理完成"之前不会实际运行.

setNeedsDisplay将视图标记为无效和操作系统,并且基本上等待所有处理完成.在您想要拥有的常见情况下,这可能会令人愤怒:

  • 视图控制器1
  • 开始一些功能2
  • 逐渐增加3
    • 创造了一个越来越复杂的艺术品和4
    • 在每一步,你setNeedsDisplay(错!)5
  • 直到所有工作完成6

当然,当你执行上面的1-6时,所有发生的事情是drawRect 在步骤6之后运行一次.

您的目标是在第5点刷新视图.怎么办?


解决原始问题............................................. .

总之,您可以(A)背景大画,并调用前景进行UI更新或(B)可争议地有四种"即时"方法建议不使用后台进程.为了起作用的结果,运行演示程序.它有所有五种方法的#defines.


Tom Swift介绍的真正令人震惊的替代解决方案..................

汤姆斯威夫特解释了非常简单地操纵运行循环的惊人想法.以下是触发运行循环的方法:

[[NSRunLoop currentRunLoop] runMode:NSDefaultRunLoopMode beforeDate:[NSDate date]];

这是一个真正令人惊叹的工程.当然,在操作运行循环时应​​该非常小心,并且许多人指出这种方法严格适用于专家.


引起的奇怪问题............................................. .

尽管许多方法都有效,但实际上并没有"工作",因为在演示中你会看到一个奇怪的渐进式减速神器.

滚动到我在下面粘贴的"答案",显示控制台输出 - 您可以看到它逐渐减慢的速度.

这是新的SO问题:
运行循环/ drawRect中的神秘"渐进式减速"问题

这是演示应用程序的V2 ...
http://www.fileswap.com/dl/p8lU3gAi/stepwiseDrawingV2.zip.html

你会看到它测试所有五种方法,

#ifdef TOMSWIFTMETHOD
 [self setNeedsDisplay];
 [[NSRunLoop currentRunLoop]
      runMode:NSDefaultRunLoopMode beforeDate:[NSDate date]];
#endif
#ifdef HOTPAW
 [self setNeedsDisplay];
 [CATransaction flush];
#endif
#ifdef LLOYDMETHOD
 [CATransaction begin];
 [self setNeedsDisplay];
 [CATransaction commit];
#endif
#ifdef DDLONG
 [self setNeedsDisplay];
 [[self layer] displayIfNeeded];
#endif …
Run Code Online (Sandbox Code Playgroud)

iphone cocoa quartz-graphics runloop ios

44
推荐指数
4
解决办法
2万
查看次数

如何,简单地等待iOS中的任何布局?

在开始注意之前,这与后台处理无关.没有"计算"涉及到背景.

只有UIKit.

view.addItemsA()
view.addItemsB()
view.addItemsC()
Run Code Online (Sandbox Code Playgroud)

让我们说6s的iPhone

其中每一个都需要一秒时间才能构建UIKit.

这将发生:

一大步

他们一直都在出现.重复一遍,屏幕只会挂起3秒,而UIKit会做大量的工作.然后他们都出现了.

但是,让我说我希望这种情况发生:

在此输入图像描述

他们一直在进步.当UIKit构建一个屏幕时,屏幕会暂停1秒钟.它出现.它会在构建下一个时再次挂起.它出现.等等.

(注意"一秒钟"只是一个简单的例子.为了更全面的例子,请参阅本文末尾.)

你是如何在iOS中做到的?

您可以尝试以下方法.它似乎不起作用.

view.addItemsA()

view.setNeedsDisplay()
view.layoutIfNeeded()

view.addItemsB()
Run Code Online (Sandbox Code Playgroud)

你可以试试这个:

 view.addItemsA()
 view.setNeedsDisplay()
 view.layoutIfNeeded()_b()
 delay(0.1) { self._b() }
}

func _b() {
 view.addItemsB()
 view.setNeedsDisplay()
 view.layoutIfNeeded()
 delay(0.1) { self._c() }...
Run Code Online (Sandbox Code Playgroud)
  • 请注意,如果该值太小 - 这种方法很简单,显然也没有做任何事情.UIKit将继续努力.(它还会做什么?).如果价值太大,那就没有意义了.

  • 请注意,目前(iOS10),如果我没有弄错的话:如果你尝试使用零延迟的技巧,它最好不正常工作.(正如你可能期望的那样.)

跳转运行循环......

view.addItemsA()
view.setNeedsDisplay()
view.layoutIfNeeded()

RunLoop.current.run(mode: .defaultRunLoopMode, before: Date())

view.addItemsB()
view.setNeedsDisplay()
view.layoutIfNeeded()
Run Code Online (Sandbox Code Playgroud)

合理. 但是我们最近的实际测试显示,在许多情况下这似乎不起作用.

(也就是说,Apple的UIKit现在足够精致,可以将UIKit的工作涂抹在那个"技巧"之外.)

思考:在UIKit中,或许有一种方法可以获得一个回调,基本上,它已经绘制了你所堆积的所有视图了吗?还有其他解决方案吗?

一个解决方案似乎是......将子视图放在控制器中,因此您可以获得"didAppear"回调,并跟踪这些回调. 这似乎是婴儿,但也许这是唯一的模式?无论如何它真的会起作用吗?(仅仅是一个问题:我没有看到任何保证,确保确保所有子视图都已被绘制.)


如果这还不清楚......

日常用例示例:

•假设可能有七个部分.

•假设每个人通常需要0.01到0.20来构建UIKit(取决于您显示的信息).

•如果你只是"让一切都搞砸了"它通常会好或可以接受(总时间,比如0.05到0.15)......但......

•当"新屏幕出现"时,用户经常会有一个单调乏味的停顿.(.1到.5或更糟 …

performance runloop ios

29
推荐指数
2
解决办法
3780
查看次数

iOS游戏和运行循环管理

首先,我的问题是:你如何管理你的iOS Run-Loop?

接下来我的理由是:我一直在研究各种原型(v.早期开发),并发现了许多令人困惑的问题.

  • 首先,输入问题和运行循环让我尝试以下方法:
    • 当使用最推荐的系统(CADisplayLink)时,我注意到一旦CPU负载导致缓冲区翻转(presentRenderBuffer)必须等待一帧,某些触摸输入就会被丢弃.这只发生在设备而不是模拟器中(烦人的 - 这似乎与等待主线程上的vsync阻塞以及应用程序运行循环过程触摸输入和吃消息的方式有关)
    • 当使用下一个最推荐的系统(NSTimer)时,我注意到一旦CPU负载达到模拟器中的某个点而不是设备中的某个点(也令人讨厌),某些触摸输入就会被丢弃.NSTimer在我的更新发布时也会导致精度降低
    • 当使用推荐最少的系统(在内部使用由mach_absolute_time构建的高精度定时器管理自己的线程中运行运行循环时,我的所有触摸输入问题都会消失,但是我的ASSERT代码现在陷入了错误的线程,只有我睡着了跟随软件中断.(我的断言代码类似于http://iphone.m20.nl/wp/?p=1)我真的很喜欢我的断言代码陷阱导致问题,所以这个解决方案是对我来说不太适合:更难调试.
  • 第二,失去的时间:
    • 在调查系统时,我发现无论帧率如何(奇怪的是,但我认为统计上它仍然有意义w/vsync)我在vsync上等待大约22%的时间.我已经通过移动glFlush/glFinish并通过玩我多久经常进行presentRenderBuffer调用来证实这一点.这是我喜欢处理AI等关键时刻,而不是简单地停止阻塞gl调用.我能想到的唯一方法就是将渲染转移到它自己的线程中,但我不确定是否有理由开始在单处理器设备上重新构建多线程.

那么有没有人围绕这些问题找到了灵丹妙药?有没有人在这个平台上有一个杀手运行循环架构?目前看起来我必须选择较少的邪恶.

iphone optimization opengl-es runloop ios

28
推荐指数
1
解决办法
8515
查看次数

NSTimer在背景上工作

我根本不明白它,但NSTimer在我的应用程序肯定是在后台运行.我有一个NSLog由计时器运行的mehod,它在后台进行记录.它位于带有iOS 4.2.1的iPhone 4上.我在Info.plist中声明了位置背景支持.

我在这里和其他地方阅读了文档和许多讨论,这是不可能的.这是一个iOS错误吗?还是没有文档的功能?我不想使用它并在不久的将来发现,例如随着iOS 4.3的出现,Apple默默地"修复"它并且应用程序无法正常工作.

有人知道更多吗?

cocoa-touch background nstimer runloop ios

17
推荐指数
2
解决办法
2万
查看次数

iOS runloop机制有什么指南吗?

我正在学习iPhone上的套接字通信,它的指南说了些什么CFRunloop(这是一个指南CFNetwork,可以在iOS上使用吗?)我在哪里可以了解iOS上的runloop?API参考是不够的.

sockets iphone runloop ios

15
推荐指数
2
解决办法
2万
查看次数

将选择器排入运行循环 - 是[NSObject performSelector:withObject:afterDelay:]的方法吗?

我希望在当前方法通过并且UI已更新之后执行一个方法.为此,我现在正在使用[object performSelector:@selector(someSelector) withObject:someObject afterDelay:0.0].根据Apple的文档,这会创建一个NSTimer,然后触发并将选择器附加到当前的NSRunLoop.但我不认为这很优雅.有没有一种简单的方法可以直接将选择器排入当前的运行循环,而不会让Cocoa创建一个Timer等?

performSelectorOnMainThread:withObject:waitUntilDone:(如果我在主线程),或者performSelector:onThread:withObject:waitUntilDone:waitUntilDone:NO做我想做的开销更少?

欢呼并提前谢谢

MrMage

iphone cocoa cocoa-touch objective-c runloop

9
推荐指数
1
解决办法
4858
查看次数

如何在当前的RunLoop中执行GCDAsyncSocket中的didReadData?

我正在尝试使用GCDAsyncSocket一个简单的例子,并且发现我缺少某些理解,并希望你们好,人们可以帮助解释这一点.

我在下面设置了GCDAsyncSocket:

dispatch_queue_t mainQueue = dispatch_get_main_queue();
asyncSocket = [[GCDAsyncSocket alloc] initWithDelegate:self delegateQueue:mainQueue];

NSString *host = @"192.168.169.132";
uint16_t port = 2112;

DDLogInfo(@"Connecting to \"%@\" on port %hu...", host, port);
self.viewController.label.text = @"Connecting...";

NSError *error = nil;
if (![asyncSocket connectToHost:host onPort:port withTimeout:5.0 error:&error])
{
    DDLogError(@"Error connecting: %@", error);
    self.viewController.label.text = @"Oops";
}
else
{
    DDLogVerbose(@"Connecting...");
}


- (void)socket:(GCDAsyncSocket *)sock didConnectToHost:(NSString *)host port:(UInt16)port
{
    DDLogInfo(@"socket:%p didConnectToHost:%@ port:%hu", sock, host, port);
    self.viewController.label.text = @"Connected";

    // We're just going to send a test string to …
Run Code Online (Sandbox Code Playgroud)

runloop cocoaasyncsocket

7
推荐指数
1
解决办法
1万
查看次数

是否可以检查主线程是否空闲/排出主运行循环?

我刚刚阅读了以下帖子并尝试实施那里描述的方法:

使用 Kiwi 编写 iOS 验收测试 - 敏捷

那里描述的所有东西都可以完美运行。但!当我运行验收测试时,有一件事会破坏确定性。

这是 Github 上的 repo,帖子的作者在这里推送了他的实验(可以在页面底部的评论中找到):https : //github.com/moredip/2012-Olympics-iOS--iPad-and -iPhone--source-code/tree/kiwi-acceptance-mk1

考虑他用于点击视图的这段代码:

- (void) tapViewViaSelector:(NSString *)viewSelector{
    [UIAutomationBridge tapView:[self viewViaSelector:viewSelector]];
    sleepFor(0.1); //ugh
}
Run Code Online (Sandbox Code Playgroud)

...其中sleepFor以下定义

#define sleepFor(interval) (CFRunLoopRunInMode(kCFRunLoopDefaultMode, interval, false))
Run Code Online (Sandbox Code Playgroud)

等待一小段时间直到处理完所有动画并吸收所有可能的东西是一种幼稚的尝试(“幼稚”与作者无关,而是关于它是第一个想到的事实)已(或可能)调度到主运行循环的事件(另请参阅此评论)。

问题是这个简单的代码不能以一种确定性的方式工作。有一堆 UI 交互导致在当前编辑的文本字段的键盘消失之前按下 fx next 按钮点击,等等......

如果我只是将时间从 0.1 增加到 fx 1,所有问题都会消失,但这会导致每一次交互,例如“用文本填充文本字段...”或“点击带有标题的按钮...”都会花费一个第二!

所以我的意思不是仅仅增加这里的等待时间,而是一种使这种人为等待保证我可以继续下一步的测试用例的方法。

我希望它应该是一种更可靠的方法,可以等到所有由当前操作(所有转换/动画或任何主运行循环的东西)引起的东西都完成为止。

总结一下就是一个问题:

有没有办法排空/排空/浸泡所有调度到主线程及其运行循环的东西,以确保主线程空闲并且其运行循环是“空的”?

这是我最初的解决方案:

// DON'T like it
static inline void runLoopIfNeeded() {
    // https://developer.apple.com/library/mac/#documentation/CoreFOundation/Reference/CFRunLoopRef/Reference/reference.html

    while (CFRunLoopRunInMode(kCFRunLoopDefaultMode, 0.1, YES) == kCFRunLoopRunHandledSource);

    // DON'T like it
    if (CFRunLoopRunInMode(kCFRunLoopDefaultMode, …
Run Code Online (Sandbox Code Playgroud)

objective-c runloop ios

5
推荐指数
2
解决办法
2746
查看次数

如何在不调用NSApplication或NSRunLoop的情况下运行Cocoa GUI类

为什么以下代码有效?这是一个小型Cocoa程序,它使用NSOpenPanel选择文件并在Emacs.app中打开它.它可以从命令行运行,并以起始目录作为参数.

NSOpenPanel如何在不调用NSApplication或NSRunLoop的情况下运行?没有显式启动NSApplication或NSRunLoop的Cocoa程序有哪些限制?我原以为其中一个是:你不能使用任何类型的GUI.也许通过调用NSOpenPanel,调用一些调用NSRunLoop的后备代码?我把断点放在+ [NSApplication alloc]和+ [NSRunLoop alloc]上并且它们没有被触发.

main.m:

#import <Cocoa/Cocoa.h>

NSString *selectFileWithStartPath(NSString *path) {
  NSString *answer = nil;
  NSOpenPanel* panel = [NSOpenPanel openPanel];
  panel.allowsMultipleSelection = NO;
  panel.canChooseFiles = YES;
  panel.canChooseDirectories = NO;
  panel.resolvesAliases = YES;
  if([panel runModalForDirectory:path file:nil] == NSOKButton)
    answer = [[[panel URLs] objectAtIndex:0] path];
  return answer;
}

int main(int argc, const char * argv[]) {
  NSString *startPath = argc > 1 ? [NSString stringWithUTF8String:argv[1]] : @"/Users/Me/Docs";
  printf("%s\n", argv[1]);
  BOOL isDir;
  if([[NSFileManager defaultManager] fileExistsAtPath:startPath isDirectory:&isDir] && isDir) {
    system([[NSString stringWithFormat:@"find …
Run Code Online (Sandbox Code Playgroud)

cocoa program-entry-point objective-c nsapplication runloop

5
推荐指数
1
解决办法
359
查看次数

有没有办法指定运行循环和模式以从发布者接收元素

我可以将调度程序指定为RunLoop.main,但我找不到提供关联RunLoop.Mode模式以从发布者接收元素的本机方式。

为什么我需要这个:我正在更新我的发布者的 tableView 单元格,但如果用户正在滚动,UI 不会更新,然后在用户交互或滚动停止后立即更新。这是滚动视图的已知行为,但我希望我的内容尽快显示,并且能够指定运行循环跟踪模式可以解决此问题。

组合 API:我认为该receive(on:options:)方法没有任何匹配选项来提供此功能。我认为在内部,如果我打电话,receive(on:RunLoop.main)那么RunLoop.main.perform { }就会被调用。此执行方法可以将模式作为参数,但这不会暴露给组合 API。


当前想法:为了解决这个问题,我可以自己执行执行操作而不使用组合 API,所以不要这样做:

cancellable = stringFuture.receive(on: RunLoop.main) // I cannot specify the mode here
                          .sink { string in
    cell.textLabel.text = string
}
Run Code Online (Sandbox Code Playgroud)

我可以这样做:

cancellable = stringFuture.sink { string in
    RunLoop.main.perform(inModes: [RunLoop.Mode.common]) { // I can specify it here
        cell.textLabel.text = string
    }
}
Run Code Online (Sandbox Code Playgroud)

但这并不理想。

理想的解决方案:我想知道如何将其包装到我自己的发布者函数实现中以具有如下内容:

cancellable = stringFuture.receive(on: RunLoop.main, inMode: RunLoop.Mode.common) …
Run Code Online (Sandbox Code Playgroud)

uikit runloop swift combine

5
推荐指数
1
解决办法
619
查看次数