Swift 4使用KVO收听音量变化

时间:2017-09-20 13:58:02

标签: ios key-value-observing swift4 xcode9 swiftlint

我刚刚更新到Swift 4和Xcode 9并得到(swiftlint)警告,以下代码告诉我现在应该使用KVO:

警告:

  

(基于块的KVO违规:首选基于新块的KVO API   使用Swift 3.2或更高版本时的keypath。 (block_based_kvo))

旧代码:

override func observeValue(forKeyPath keyPath: String?,
                           of object: Any?,
                           change: [NSKeyValueChangeKey : Any]?,
                           context: UnsafeMutableRawPointer?) {
    if keyPath == "outputVolume"{
        guard let newKey = change?[NSKeyValueChangeKey.newKey] as? NSNumber else {
            fatalError("Could not unwrap optional content of new key")
        }

        let volume = newKey.floatValue

        print("volume " + volume.description)
    }
}

我尝试修复:

let audioSession = AVAudioSession.sharedInstance()
    audioSession.observe(\.outputVolume) { (av, change) in
        print("volume \(av.outputVolume)")
}
Apple声称here大多数属性应该是dynamic(我知道这是AVPlayer而不是AVAudioSession)。我查了一下但是在AVPlayer属性中找不到任何dynamic语句,并且想知道它是如何工作的(如果我没弄错的话KVO需要它们工作)。

修改

我不确定它是否不会触发,因为它根本不起作用或者是由于我尝试归档的原因。一般情况下,我希望通过推动硬件音量摇杆来获得有关音量变化的通知。

2 个答案:

答案 0 :(得分:9)

我认为你是指这一行:

  

您可以使用键值观察(KVO)观察许多玩家动态属性的状态变化......

这种使用"动态"与Objective-C @dynamic或Swift' dynamic不同。文档只是意味着"属性改变"在这种情况下,他们告诉你AVPlayer通常非常符合KVO,并且打算以这种方式观察。 "符合KVO"意味着它遵循change notification规则。有很多方法可以实现这一点,包括自动和手动。文档只是承诺AVPlayer。

(关于Cocoa与C许多其他系统的区别在于Cocoa处理很多事情"按照约定"。在代码中没有办法说#34;这是KVO兼容"并且编译器无法强制执行它,但Cocoa开发人员往往非常善于遵守规则。当ARC开发时,它很大程度上依赖于Cocoa开发人员多年来一直在命名的方法。表明如何处理内存管理的特定规则。它只是添加了Cocoa开发人员随后掌握的规则的编译器执行。这就是Cocoa开发人员对命名约定和大小写非常嘈杂的原因.Cocoa的主要部分完全依赖于遵循一致的命名规则。)

记住AVPlayer接口是一个恰好与Swift桥接的Objective-C API,在这种情况下,没有相应的Swift关键字dynamic。这是一个告诉Swift可以观察到这个属性的关键字,因此它的访问者不能被优化为静态调度。这不是Objective-C所要求的(或者可以做的;所有ObjC属性都是"动态"在这个意义上)。

Objective-C @dynamic是一个完全不同的东西,只与KVO微弱相关(虽然它出现在很多像KVO这样的重要环境中,比如Core Data)。它只是意味着即使你无法在任何地方找到这个属性的访问者实现,相信我,到运行时,实现将是可用的。"这依赖于ObjC的运行时能够动态生成实现或以程序员控制的方式调度(这仍然存在于Swift中,通过操纵ObjC运行时,但它并不是真正的" Swift"功能)。

至于KVO如何运作,它是为数不多的真正的魔术技巧之一"在可可。有关快速介绍,请参阅Key-Value Observing Implementation Details。简短版本是:

  • 当你观察一个对象时,会动态创建该对象的子类(是的,在运行时发明了一个新类)。
  • 子类在对超类的属性访问器的所有调用周围添加了对willChangeValue...didChangeValue...的调用。
  • 对象是" ISA-swizzled"成为那个新班级。
  • 魔法! (好吧,不是真的很神奇;它只是代码,但它是一个非常诡计。)

编辑:最初的问题从未提及它不起作用。它无效的原因是因为您没有在属性中分配返回的NSKeyValueObservation;你只是把它扔掉了。我很惊讶那里并没有警告;我可以开雷达。

当返回的NSKeyValueObservation解除分配时,观察结果消失,因此这会产生观察并立即销毁它。您需要将它存储在属性中,直到您希望观察消失为止。

答案 1 :(得分:2)

OP解决方案。

它需要存储在一个属性中。不是变量,不是_,而是属性。否则它将无法正常工作。像这样:

class YourViewController: UIViewController {

    var obs: NSKeyValueObservation?

    override func viewDidLoad() {
        super.viewDidLoad()

        let audioSession = AVAudioSession.sharedInstance()
        self.obs = audioSession.observe( \.outputVolume ) { (av, change) in
            print("volume \(av.outputVolume)")
        }
    }
}