【问题标题】:CoreAnimation warning deleted thread with uncommitted CATransactionCoreAnimation 警告删除了未提交 CATransaction 的线程
【发布时间】:2023-03-02 23:32:01
【问题描述】:

我遇到以下警告的问题:

CoreAnimation:警告,删除了未提交 CATransaction 的线程;在环境中设置 CA_DEBUG_TRANSACTIONS=1 以记录回溯。

我正在使用 NSOperation 对象来执行一些计算,一旦完成,它会向 AppDelegate 发送一条消息,然后隐藏进度条并取消隐藏一些按钮。如果我将消息注释回 AppDelegate,则警告会消失,但进度条显然仍然可见且具有动画效果。

我正在使用 xCode 4.4.1 和 OSX 10.8.1,但是,当我在 OSX 10.7.4 上使用相同版本的 xCode 编译和运行代码时,我没有收到警告并且代码按预期运行。

设置 CA_DEBUG_TRANSACTIONS=1 环境变量显示回溯来自 AppDelegate 中的 NSControl setEnabled 消息。

答案可能是盯着我的脸,但也许我喝了太多咖啡!

【问题讨论】:

  • 我今天一直在玩这个。我怀疑问题是 NSOperation 在 CoreAnimation 完成重绘 UI 元素之前完成。回溯显示该方法最初是从 NSOperation 调用的。我尝试从 NSOperation 实现一个 NSNotification 来告诉 AppDelegate 计算完成,希望这意味着可以在不影响 CoreAnimation 的情况下释放 NSOperation,但是警告仍然出现,但这次来源是 CoreFoundation?
  • 在这两种情况下,程序似乎都按预期运行,并且警告仅在 OS 10.8.1 或 10.8.2 上运行时显示

标签: multithreading core-animation nsoperation catransaction


【解决方案1】:

为了与标准 Cocoa 范例保持一致,这里推荐的解决方案是在主线程上执行核心动画工作,使用 GCD 轻松完成:

dispatch_async(dispatch_get_main_queue(), ^{
    [self.delegate redrawSomething];
});

一般来说,在他们不期望的上下文中调用对象是一种糟糕的形式,因此一个好的经验法则是在向外部模块传递消息时始终分派到主线程。

如果从主线程以外的任何上下文调用某些框架(例如 Core Location),它们会发出日志消息。其他人会发出神秘的消息,例如您在此处使用 Core Animation 的示例。

【讨论】:

    【解决方案2】:

    你的怀疑是对的。 如果 NSOperation 在 CoreAnimation 执行完成之前完成,那么你会收到一个很好的警告:

    *CoreAnimation: 警告,删除了未提交 CATransaction 的线程;在环境中设置 CA_DEBUG_TRANSACTIONS=1 以记录回溯。*

    这也可能发生在某些情况下,当在队列中调度的块触发 CoreAnimation 的某些工作并在 CoreAnimation 完成之前返回。

    我使用的解决方案很简单:在向 CoreAnimation 请求工作的块或 NSOperation 上,我在退出之前检查工作是否确实已经完成。

    给你一个概念验证的例子,这是一个在调度队列上调度的块。为了避免警告,我们在退出前检查 CoreAnimation 是否完成。

    ^{
    
       // 1. Creating a completion indicator
    
       BOOL __block animationHasCompleted = NO;
    
       // 2. Requesting core animation do do some work. Using animator for instance.
    
       [NSAnimationContext runAnimationGroup:^(NSAnimationContext *context){
          [[object animator] perform-a-nice-animation];
       } completionHandler:^{
          animationHasCompleted = YES;
       }];
    
       // 3. Doing other stuff…
    
       …
    
       // 4. Waiting for core animation to complete before exiting
    
       while (animationHasCompleted == NO)
       {
           usleep(10000);
       }
    
    }
    

    【讨论】:

    • 这不是最佳解决方案,因为它会消耗不必要的资源。请参阅下文以获得更好的解决方案。具体来说,使用performSelectorOnMainThread:withObject:waitUntilDone: 或dispatch_async()。
    • 这是一个糟糕的解决方案,所有动画都应该分派到主线程。
    • @PPierson 实际上动画被分派到主线程。我看不出解决方案有什么问题。
    • @Jean 如果动画被调度到主线程,那么就不需要睡眠循环(#4),这是不必要的工作。警告的原因是动画是从另一个正在删除的线程中启动的。
    【解决方案3】:

    如 Numist 所述,确保在主线程上进行任何 UI 绘制的另一种方法是使用方法 performSelectorOnMainThread:withObject:waitUntilDone: 或 performSelectorOnMainThread:withObject:waitUntilDone:modes:

    - (void) someMethod
    {
        [...]
    
        // Perform all drawing/UI updates on the main thread.
        [self performSelectorOnMainThread:@selector(myCustomDrawing:)
                               withObject:myCustomData
                            waitUntilDone:YES];
    
        [...]
    }
    
    - (void) myCustomDrawing:(id)myCustomData
    {
        // Perform any drawing/UI updates here.
    }
    


    有关dispatch_async() 和performSelectorOnMainThread:withObjects:waitUntilDone: 之间区别的相关帖子,请参阅Whats the difference between performSelectorOnMainThread and dispatch_async on main queue?

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2013-10-28
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2015-08-30
      • 1970-01-01
      • 2015-05-20
      相关资源
      最近更新 更多