iOSのキー値符号化(KVC)とキー値傍受(KVO)

7964 ワード

KVCの概要


setter、getterメソッドでオブジェクトのプロパティを設定および変更できることを知っています.また、ポイント構文を簡略化してオブジェクトのプロパティを設定、変更する方法も知っています.実際には、Objective-Cは、オブジェクトの属性を文字列形式で間接的に操作することを可能にするより柔軟な操作方式をサポートし、この方式のフルネームはKey Value Coding(KVCと略称する)であり、キー値符号化である.

シンプルなKVC


最も基本的なKVCはNSKeyValueCodingプロトコルによってサポートされ、最も基本的な操作属性の2つの方法は以下の通りである.
  • setValue:属性値forKey:属性名:指定した属性に値を設定します.
  • valueForKey:属性名:指定した属性の値を取得します.

  • 使用法:@property (nonatomic, copy) NSString *name;設定:[object setValue:@"Suxiaoyao" forKey:@"name"]取値:NSString *nameStr = [object valueForKey:@"name"]setValueの場合:属性値forKey:@"name";コード、最下位の実行メカニズムは以下の通りです.
  • (1).プログラム優先コールsetName:属性値;コードはsetterメソッドで設定を完了します.
  • (2).クラスにsetName:メソッドがない場合、KVCメカニズムはクラス名を検索します.nameのメンバー変数、見つかったら対_nameメンバー変数の割り当て.
  • (3).クラスにsetName:メソッドも定義もない場合_nameメンバー変数、KVCメカニズムはクラス名nameのメンバー変数を検索し、見つかったらnameメンバー変数に値を割り当てます.
  • (4).上記の3つが見つからない場合、システムはオブジェクトのsetValue: forUndefinedKey:メソッドを実行します.デフォルトのsetValue: forUndefinedKey:メソッドでは、例外が発生し、プログラムがクラッシュします.

  • 「valueForKey:@「name」;」コード、下位実行メカニズムは以下の通りです.
  • (1).プログラムは「name;」を優先的に呼び出すコードは、getterメソッドの戻り値を取得します.
  • (2).クラスにnameメソッドがない場合、KVCメカニズムはクラス名を検索します.nameのメンバー変数、見つかったら戻ります_nameメンバー変数の値.
  • (3).クラスにnameメソッドも定義もない場合_nameメンバー変数、KVCメカニズムはクラス名nameのメンバー変数を検索し、見つけたらnameメンバー変数の値を返します.
  • (4).上記の3つが見つからない場合、システムはオブジェクトのvalueForUndefinedKey:メソッドを実行します.デフォルトのvalueForUndefinedKey:メソッドでは、例外が発生し、プログラムがクラッシュします.

  • 存在しないkeyの処理


    前述したように、KVC方式で属性を操作する場合、これらの属性は存在しない可能性があります.この場合、システムは異常を起こしただけで、特別な処理は行われません.しかし、私たちの実際の開発では、私たちのビジネスシーンと結びつけて、私たちはいつもプログラムが崩壊することを望んでいません.このとき、setValue: forUndefinedKey:方法とvalueForUndefinedKey:方法を書き換えることを考えることができます.
    - (void)setValue:(id)value forUndefinedKey:(NSString *)key {
        
        NSLog(@" key:[%@] ", key);
        NSLog(@" value :[%@]", value);
        
    }
    
    - (id)valueForUndefinedKey:(NSString *)key {
        
        NSLog(@" key:[%@] ", key);
        return nil;
        
    }
    

    このように、KVC操作が存在しないkeyの場合、KVCメカニズムは常に書き換えの方法を呼び出して処理し、この処理メカニズムにより、自分の処理行為を非常に便利にカスタマイズすることができる.

    nil値の処理


    KVCを呼び出してオブジェクトのプロパティを設定する場合、プロパティのタイプがNSStringなどのオブジェクトタイプの場合、プロパティをnilに設定しようとしますが、合法的であれば、プログラムは正常に実行できます.
    ただし、属性のタイプがint、float、doubleなどの基本タイプの場合、属性をnilに設定しようとすると、プログラムがクラッシュして次の異常'NSInvalidArgumentException'を引き起こし、ヒント情報からsetNilValueForKey:メソッドがこの異常を引き起こしたことがわかります.プログラムが属性にnil値を設定しようとすると、その属性がnil値を受け入れない場合、プログラムはオブジェクトのsetNilValueForKey:メソッドを自動的に実行します.私たちは同じようにこの方法を書き直すことができます.
    - (void)setNilValueForKey:(NSString *)key {
        
        // nil 
        if ([key isEqualToString:@"price"]) {
            
            // 
            price = 0;
            
        }else {
            
            [super setNilValueForKey:key];
            
        }
        
    }
    

    この方法を書き換えることで、私たちの異なるビジネスシーンに基づいて個別に処理することができます.

    Keyパス(Key Path)


    KVCは、リレーションシップによってオブジェクトにアクセスすることもできます.personオブジェクトに属性addressがあり、addressに属性cityがあると仮定すると、personによってcityにアクセスできます.
    [person valueForKeyPath:@"address.city"];
    

    ここでは-valueForKeyPath:ではなく-valueForKeyPath:を呼び出します.
    KVCプロトコルでKeyパスを操作する方法は以下の通りである.
  • setValue:forKeyPath:Keyパスに基づいて属性値を設定
  • valueForKeyPath:Keyパスから取得する属性値
  • コレクションのアクション


    よく無視されるKVC特性は、集合動作のサポートである.例を挙げると、配列の最大値を得ることができます.
    NSArray *a = @[@4, @84, @2];
    NSLog(@"max = %@", [a valueForKeyPath:@"@max.self"]);
    

    あるいは、Transactionオブジェクトの配列があり、オブジェクトに属性amountがあれば、最大のamountを得ることができます.
    NSArray *a = @[transaction1, transaction2, transaction3];
    NSLog(@"max = %@", [a valueForKeyPath:@"@max.amount"]);
    
    [a valueForKeyPath:@"@max.amount"]を呼び出すと、配列aの各要素で-valueForKey:@amountを呼び出し、最大のものを返します.KVCのアップルの公式ドキュメントには、Collection Operatorsが似たような使い方を詳しく述べている章があります.

    KVC小結


    前にこんなに多くの内容を紹介しましたが、なぜKVCで操作したのか疑問に思うかもしれません.オブジェクトのsetterを直接呼び出してgetterメソッドと操作してはいけませんか?KVC方式のほうが性能がいいのではないでしょうか.実際,setter,getter方式で操作するよりもKVC操作オブジェクトの性能が劣り,KVCプログラミングを用いる利点は,より簡潔で汎用的な性質のコードを抽出するのに適していることである.KVCは、オブジェクトのプロパティを文字列形式で操作できるため、この文字列は定数でも変数でもあるため、柔軟性が非常に高い.

    キー値傍受(KVO)


    iOSアプリケーションの開発過程において、iOSアプリケーションは通常、アプリケーションコンポーネントをデータモデルコンポーネントとビューコンポーネントに分離し、データモデルコンポーネントはアプリケーションの状態データの維持を担当し、ビューコンポーネントはデータモデルコンポーネント内部の状態データの表示を担当する.
    上記の設計構造では、データ・モデル・コンポーネントの状態データが変更された場合、ビュー・コンポーネントは自分を動的に更新し、データ・モデル・コンポーネントの更新後のデータをタイムリーに表示する必要がある場合があります.
    iOSはKVO(Key Value Observing)メカニズムを利用した優れたソリューションを提供しています.
    KVOメカニズムNSKeyValueObservingプロトコルはサポートを提供します.もちろん、NSObjectはこのプロトコルを遵守しています.そのため、NSObjectのサブクラスはこのプロトコルのメソッドを使用することができます.このプロトコルには、リスナーの登録に使用できる一般的な方法が含まれています.
  • addObserver:forKeyPath:options:context:指定キーパス
  • をリスニングするためのリスナーを登録する.
  • removeObserver:forKeyPath:指定したキーパスに対して指定したListener
  • を削除する
  • removeObserver:forKeyPath:context:指定したKeyパスに対して指定したListenerを削除します.ただし、contextパラメータが1つ増えています.

  • 以上のようなニーズに対しては、データモデルコンポーネントの変更をビューコンポーネントに傍受させることが容易に考えられ、データモデルコンポーネントのkeyパスに対応する属性が変更されると、リスナーとしてのビューコンポーネントが励起され、励起されるとリスナー自身の傍受方法がコールバックされ、この傍受方法は以下の通りである:observeValueForKeyPath:ofObject:change:context:ListenerのビューコンポーネントとしてobserveValueForKeyPath:ofObject:change:context:メソッドを書き換える必要があります.このメソッドを書き換えると、最新の変更データが得られ、最新のデータを使用してビューコンポーネントの表示が更新されます.
    KVOプログラミングの手順は以下の通りです.
  • は、リスニング対象(通常はデータモデルコンポーネント)としてリスナー
  • を登録する.
  • ListenerのobserveValueForKeyPath:ofObject:change:context:メソッド
  • を書き換える
  • Listener
  • を除去する.

    KVOインスタンスシーン


    私たちは一人の鼓動を監視し、スクリーンに表示します.
    モデルを定義します
    @interface Person : NSObject
    
    /**
      
     */
    @property (nonatomic, copy) NSString *heartbeat;
    
    @end
    
    @implementation Person
    
    @end
    

    このモデルをControllerのプロパティとして定義し、インスタンス化し、そのプロパティをリスニングし、現在のViewに表示します.
    @interface ViewController ()
    
    @property (nonatomic, strong) Person *person;
    
    @property (nonatomic, strong) UILabel *heartbeatLabel;
    
    @end
    
    @implementation ViewController
    
    - (void)viewDidLoad {
        [super viewDidLoad];
        // Do any additional setup after loading the view, typically from a nib.
        
        self.person = [[Person alloc] init];
        [self.person setValue:@"72" forKey:@"heartbeat"];
        [self.person addObserver:self forKeyPath:@"heartbeat" options:NSKeyValueObservingOptionNew|NSKeyValueObservingOptionOld context:nil];
     
        self.heartbeatLabel = [[UILabel alloc] initWithFrame:CGRectMake(100, 100, 100, 30)];
        self.heartbeatLabel.textColor = [UIColor redColor];
        self.heartbeatLabel.text = [self.person valueForKey:@"heartbeat"];
        [self.view addSubview:self.heartbeatLabel];
        
        UIButton *runButton = [UIButton buttonWithType:UIButtonTypeRoundedRect];
        runButton.frame = CGRectMake(0, 0, 100, 30);
        runButton.backgroundColor = [UIColor redColor];
        [runButton addTarget:self action:@selector(run:) forControlEvents:UIControlEventTouchUpInside];
        [self.view addSubview:runButton];
        
    }
    

    buttonをクリックするとrunメソッドが呼び出され、オブジェクトのプロパティが変更されます.
    - (void)run:(UIButton *)sender {
        
        [self.person setValue:@"100" forKey:@"heartbeat"];
        
    }
    

    コールバックメソッドの実装
    -(void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary *)change context:(void *)context
    {
        if([keyPath isEqualToString:@"heartbeat"])
        {
            self.heartbeatLabel.text = [change objectForKey:@"new"];
        }
    }
    

    観察を増やすこととキャンセルすることはペアで現れるので,最後に観察者を除去する必要がある.
    - (void)dealloc {
        
        [self.person removeObserver:self forKeyPath:@"heartbeat"];
        
    }
    

    KVOノット


    KVOという符号化方式は簡単に使用でき、model修正後に発生するviewの変化に適しており、上記の例のように属性の値を変更すると、リスナーはすぐに通知されます.
    もし文章の中で问题があるならば、コメントの伝言を歓迎して、ありがとうございます支持して関心を歓迎して、私は暇な时間に技术の文章を更新します~