web-dev-qa-db-ja.com

Swift 4 KVOを使用して音量の変化を聞く

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

Appleは here のプロパティのほとんどはdynamicである必要があると主張しています(これはAVAudioSessionではなくAVPlayerであることを知っています)。私はそれを調べましたが、AVPlayerプロパティ内にdynamicステートメントを見つけることができず、それがどのように機能するのか疑問に思っていました(私が間違っていない場合、KVOが機能するためにこれらが必要です)。

編集:

単に機能しないためにトリガーされないのか、それともアーカイブしようとしたことが原因であるのかはわかりません。一般的に、hardware-volume-rockerをプッシュすることでトリガーされるボリュームの変更について通知を受けたいと思います。

13
Eternal Black

私はあなたが行を参照していると思います:

Key-Value監視(KVO)を使用して、プレーヤーの動的プロパティの多くに対する状態変化を監視できます...

この「動的」の使用は、Objective-Cの@dynamicまたはSwiftのdynamicと同じものではありません。ドキュメントは、このコンテキストで「変化するプロパティ」を意味するだけであり、AVPlayerは一般に非常にKVOに準拠しており、そのように監視されることを意図していることを伝えています。 「KVO準拠」とは、 変更通知 ルールに従うことを意味します。これを実現するには、自動と手動の両方の方法があります。ドキュメントはAVPlayerが行うことを約束しています。

(Cocoaを他の多くのシステムと区別する重要な点は、Cocoaが多くのことを「慣例に従って」処理することです。コードでは「これはKVOに準拠しています」と言う方法はなく、コンパイラーが強制する方法はありませんが、 Cocoa開発者は、ルールに従うことを非常に好む傾向があります。ARCが開発されたとき、Cocoa開発者は、メモリ管理の処理方法を示す非常に特定のルールに従って名前付きメソッドを何年もの間持っていたという事実に大きく依存していました。ルールCocoa開発者は常に手作業で行っていました。これが、Cocoa開発者が命名規則と大文字化について非常に騒々しい理由です。Cocoaの主要な部分は、一貫した命名規則に従うことに完全に依存しています。)

AVPlayerインターフェースはたまたまSwiftにブリッジされるObjective-C APIであることを思い出してください。その場合、Swift keyword dynamicに相当するものはありません。これは、 Swiftこのプロパティが監視される可能性があるため、そのアクセサを静的ディスパッチに最適化できません。これはObjective-Cが必要とするものではありません(または実行できます。すべてのObjCプロパティはこの中で「動的」です)センス)。

Objective-C @dynamicは完全に異なるものであり、KVOとは弱く関連しています(ただし、Core Dataのような多くのKVOの多いコンテキストで登場します)。これは、「このプロパティのアクセサ実装がどこにも見つからない場合でも、信頼できる、これが実行されるまでに実装が利用可能になる」ことを意味します。これは、動的に実装を生成したり、プログラマーが制御する方法でディスパッチしたりするObjCのランタイムの機能に依存しています(これは、ObjCランタイムを操作することにより、Swiftにまだ存在しますが、実際には "スウィフト」機能)。

KVOの動作に関しては、KVOはCocoaで数少ない真の「手品」の1つです。簡単な紹介については、「 Key-Value監視実装の詳細 」を参照してください。短いバージョンは次のとおりです。

  • オブジェクトを監視すると、そのオブジェクトのサブクラスが動的に作成されます(実行時に新しいクラスが作成されます)。
  • サブクラスは、スーパークラスのプロパティアクセサーへのすべての呼び出しの周囲にwillChangeValue...およびdidChangeValue...への呼び出しを追加します。
  • オブジェクトは、その新しいクラスになるように「ISAスウィズル」されています。
  • マジック! (わかりました、実際には魔法ではありません。それは単なるコードですが、かなりトリックです。)

EDIT:元の質問では、機能していないとは述べられていませんでした。これが機能しない理由は、返されたNSKeyValueObservationをプロパティに割り当てていないためです。捨てるだけです。そのことについての警告がないことに驚いています。レーダーを開くかもしれません。

返されたNSKeyValueObservationの割り当てが解除されると、監視が消えるため、監視が作成され、すぐに破棄されます。観測を終了するまで、プロパティに保存する必要があります。

11
Rob Napier

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)")
        }
    }
} 
4
Cœur