从 installTapOnBus 闭包中访问 class 属性

Access class properties from within installTapOnBus closure

我有一个非常简单的 installTapOnBus 闭包可以成功更新控制台,但不能更新 UI 元素。这是代码:

self.meter.text="..."
let inputNode = audioEngine.inputNode
let bus = 0

inputNode!.installTapOnBus(bus, bufferSize: 2048, format: inputNode!.inputFormatForBus(bus)) {
    (buffer: AVAudioPCMBuffer!, time: AVAudioTime!) -> Void in
        var someFeature:Float=0.0
        for var i=0; i<Int(buffer.frameLength); i += 1{
           someFeature += fabs(buffer.floatChannelData.memory[i])
        }
        someFeature /= Float(buffer.frameLength)
        self.meter.text="\(someFeature)" // No effect!
        print("\(someFeature)") // This works
    }

也许我需要将对 self 的弱引用发送到闭包,但不确定语法。任何关于如何获取 UI 元素更新的 feedback/ideas 都很棒!感谢阅读。

解决方案似乎是在主线程上调用 self.meter.text = ...

dispatch_async(dispatch_get_main_queue()) { 
    self.meter.text="\(someFeature)"
}

我会在一两天内不检查这个答案,以防有人可以填写更多详细信息。

您应该只从主线程进行 UIKit 调用。

来自UI视图的documentation

Threading Considerations

Manipulations to your application’s user interface must occur on the main thread. Thus, you should always call the methods of the UIView class from code running in the main thread of your application. The only time this may not be strictly necessary is when creating the view object itself but all other manipulations should occur on the main thread.

如果您查看 AVAudioNode and specifically installTapOnBus 的 API 参考:

The tapBlock may be invoked on a thread other than the main thread.

您的 UI 没有更新,因为来自 inputNode!.installTapOnBus() 的回调正在后台线程上执行,然后您试图从该线程更新 UI。

如您所见,您需要通过以下方式从主线程进行 UI 调用:

dispatch_async(dispatch_get_main_queue(), ^{
   /* Make UIKit calls here */
});