ios Swift 4使用KVO收听音量变化

bmp9r5qi  于 2023-03-31  发布在  iOS
关注(0)|答案(2)|浏览(186)

我刚刚更新到Swift 4和Xcode 9,并收到以下代码的(swiftlint)警告,告诉我现在应该使用KVO:
警告:
(基于块的KVO违规:在使用Swift 3.2或更高版本时,首选新的基于块的KVO API和键路径。(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)")
}

苹果声称here的大部分属性应该是dynamic(我知道这是AVPlayer,而不是AVAudioSession)。我查了一下,但在AVPlayer属性中找不到任何dynamic语句,我想知道这是如何工作的(如果我没有弄错的话,这些是KVO工作所必需的)。

编辑:

我不确定是因为它根本不起作用,还是因为我试图存档的内容而没有触发。一般来说,我希望在按下硬件音量摇杆触发音量更改时得到通知。

e0bqpujr

e0bqpujr1#

我猜你指的是这句台词
您可以使用键值观察(KVO)来观察许多玩家动态属性的状态变化。
这种“动态”的用法与Objective-C的@dynamic或Swift的dynamic并不相同。文档只是在这种情况下表示“更改的属性”,他们告诉你AVPlayer通常非常符合KVO,并且旨在以这种方式观察。“KVO兼容”意味着它遵循更改通知规则。有很多方法可以实现这一点,自动和手动。文档只是承诺AVPlayer可以做到。
(An可可区别于其他系统的一个重要方面是Cocoa“按照约定”处理许多事情。没有办法在代码中说“这是KVO兼容的”,编译器也没有办法强制执行它,但Cocoa开发人员倾向于非常好地遵守规则。当ARC开发时,它在很大程度上依赖于这样一个事实,即可可开发人员多年来一直遵循非常具体的规则来命名方法,这些规则表明如何处理内存管理。它只是增加了Cocoa开发人员一直手工遵循的规则的编译器强制执行。这就是为什么可可开发人员对命名约定和大小写非常挑剔的原因。Cocoa的主要部分完全依赖于遵循一致的命名规则。
请记住AVPlayer接口是一个Objective-C API,恰好与Swift桥接,在这种情况下没有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释放时,观察就消失了,所以这会创建一个观察并立即销毁它。你需要将它存储在一个属性中,直到你希望观察消失。

vsaztqbk

vsaztqbk2#

  • 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)")
        }
    }
}

相关问题