KVC、KVO

15592 ワード

概要
ObjCは主にSmalltalkに基づいて設計されているため、動的タイプ、動的ロード、動的バインドなど、Ruby、Pythonに似た動的特性がたくさんあります.ここでは、ObjCにおけるキー値符号化(KVC)、キー値傍受(KVO)の特性について重点的に説明する.
  • キー値符号化KVC
  • キー値傍受KVO
  • キー値符号化KVC
    C#ではオブジェクトのプロパティを反射して読み書きできることを知っています.文字列でオブジェクトを動的に制御できるため、この方法は特に便利です.実はObjCの言語の特性のため、あなたの根元はいかなる操作を行う必要がなくて属性の動的な読み書きを行うことができて、このような方式はKey Value Coding(略称KVC)です.
    KVCの操作方法はNSKeyValueCodingプロトコルによって提供され、NSObjectはこのプロトコルを実現した.つまり、ObjCのほとんどのオブジェクトがKVC操作をサポートしている.よく使われるKVCの操作方法は以下の通りである.
  • 動的設定:setValue:属性値forKey:属性名(単純パス用)、setValue:属性値forKeyPath:属性パス(PersonにAccountタイプの属性がある場合、person.accountは複合属性)
  • .
  • 動的読み出し:valueForKey:属性名、valueForKeyPath:属性名(複合パス用)
  • 以下、一例を用いてKVCを理解する
    Account.h
    //
    //  Account.h
    //  KVCAndKVO
    //
    //  Created by Kenshin Cui on 14-2-16.
    //  Copyright (c) 2014  Kenshin Cui. All rights reserved.
    //
    
    #import <Foundation/Foundation.h> @interface Account : NSObject #pragma mark -    #pragma mark    @property (nonatomic,assign) float balance; @end

    Account.m
    //
    //  Account.m
    //  KVCAndKVO
    //
    //  Created by Kenshin Cui on 14-2-16.
    //  Copyright (c) 2014  Kenshin Cui. All rights reserved.
    //
    
    #import "Account.h" @implementation Account @end

    Person.h
    //
    //  Person.h
    //  KVCAndKVO
    //
    //  Created by Kenshin Cui on 14-2-16.
    //  Copyright (c) 2014  Kenshin Cui. All rights reserved.
    //
    
    #import <Foundation/Foundation.h> @class Account; @interface Person : NSObject{ @private int _age; } #pragma mark -    #pragma mark    @property (nonatomic,copy) NSString *name; #pragma mark    @property (nonatomic,retain) Account *account; #pragma mark -      #pragma mark        -(void)showMessage; @end

    Person.m
    //
    //  Person.m
    //  KVCAndKVO
    //
    //  Created by Kenshin Cui on 14-2-16.
    //  Copyright (c) 2014  Kenshin Cui. All rights reserved.
    //
    
    #import "Person.h" @implementation Person #pragma mark -      #pragma mark        -(void)showMessage{ NSLog(@"name=%@,age=%d",_name,_age); } @end

    main.m
    //
    //  main.m
    //  KVCAndKVO
    //
    //  Created by Kenshin Cui on 14-2-16.
    //  Copyright (c) 2014  Kenshin Cui. All rights reserved.
    //
    
    #import <Foundation/Foundation.h> #import "Person.h" #import "Account.h" int main(int argc, const char * argv[]) { @autoreleasepool { Person *person1=[[Person alloc]init]; [person1 setValue:@"Kenshin" forKey:@"name"]; [person1 setValue:@28 forKey:@"age"];//                 [person1 showMessage]; //  :name=Kenshin,age=28 NSLog(@"person1's name is :%@,age is :%@",person1.name,[person1 valueForKey:@"age"]); //  :person1's name is :Kenshin,age is :28 Account *account1=[[Account alloc]init]; person1.account=account1;//          account    ,             ,  account nil,           :[person1 setValue:account1 forKeyPath:@"account"]; [person1 setValue:@100000000.0 forKeyPath:@"account.balance"]; NSLog(@"person1's balance is :%.2f",[[person1 valueForKeyPath:@"account.balance"] floatValue]); //  :person1's balance is :100000000.00 } return 0; }

    KVCは使いやすいですが、どのようにして属性を探して読み取りますか?具体的な検索ルール(KVCでaを読み込むと仮定):
  • 動的設定属性の場合はsetaメソッドの呼び出しが優先され、メソッドがない場合はメンバー変数の検索が優先されます_a、まだ存在しない場合はメンバー変数aを検索し、最後に検索されない場合はこのクラスのsetValue:forUndefinedKey:メソッドを呼び出します(検索中にこれらのメソッド、メンバー変数がプライベートであっても共通であっても正しく設定できることに注意してください).
  • 動的読み出し属性であれば、aメソッド(属性aのgetterメソッド)の呼び出しを優先し、検索されなければメンバー変数を優先的に検索するa、まだ存在しない場合はメンバー変数aを検索し、最後に検索されない場合はこのクラスのvalueforUndefinedKeyを呼び出す:メソッド(検索中にこれらのメソッド、メンバー変数がプライベートであっても共通であっても正しく読み取ることができることに注意).

  • キー値モニタKVO
    WPF、Silverlightには双方向バインディングメカニズムがあることが知られているが、データモデルが修正されるとすぐにUIビューに反映され、現在流行しているMVVM設計モデルに基づくフロントエンドフレームワーク、例えばKnockoutがある.js.実はObjCではこのメカニズムが原生的に支持されており、Key Value Observing(KVOと略称)と呼ばれている.KVOは、ビューコンポーネントとデータモデルの分離を容易に実現できる観察者モードであり、データモデルの属性値が変更されるとリスナーとしてのビューコンポーネントが励起され、励起されるとリスナー自体がコールバックされる.ObjCでKVOを実装するにはNSKeyValue Objectプロトコルを実装する必要がありますが、幸いなことにNSObjectは既に実装されているため、ほとんどのObjCオブジェクトでKVOを使用できます.
    ObjCでKVO操作を使用する場合によく用いられる方法は以下の通りである.
  • 指定キーパスを登録するリスナー:addObserver:forKeyPath:options:context:
  • 指定キーパスを削除するリスナー:removeObserver:forKeyPath、removeObserver:forKeyPath:context:
  • コールバックリスニング:observeValueForKeyPath:ofObject:change:context:
  • KVOの使用手順も簡単です.
  • addObserver:forKeyPath:options:context:Listener
  • を、通常はデータモデルであるリスニング対象に登録する.
  • ListenerのobserveValueForKeyPath:ofObject:change:context:メソッド
  • を書き換える
    IOSのインタフェースプログラミングはまだ紹介されていないので、ここでは上記の例に基づいて拡張を続けています.もし私たちの口座残高balanceが変動した後、ユーザーがタイムリーに通知を得ることを望んでいるとします.では、Accountは私たちのリスニング対象として、Personがリスニングを登録する必要があります(addObserver:forKeyPath:options:context:).一方、PersonはリスナーとしてobserveValueForKeyPath:ofObject:change:context:メソッドを書き換える必要があり、リスナーの残高が変更されるとリスナーPersonリスナーメソッド(observeValueForKeyPath:ofObject:change:context:)をコールバックする.次に、上記の手順をコードでシミュレートします.
    Account.h
    //
    //  Account.h
    //  KVCAndKVO
    //
    //  Created by Kenshin Cui on 14-2-16.
    //  Copyright (c) 2014  Kenshin Cui. All rights reserved.
    //
    
    #import <Foundation/Foundation.h> @interface Account : NSObject #pragma mark -    #pragma mark    @property (nonatomic,assign) float balance; @end

    Account.m
    //
    //  Account.m
    //  KVCAndKVO
    //
    //  Created by Kenshin Cui on 14-2-16.
    //  Copyright (c) 2014  Kenshin Cui. All rights reserved.
    //
    
    #import "Account.h" @implementation Account @end

    Person.h
    //
    //  Person.h
    //  KVCAndKVO
    //
    //  Created by Kenshin Cui on 14-2-16.
    //  Copyright (c) 2014  Kenshin Cui. All rights reserved.
    //
    
    #import <Foundation/Foundation.h> @class Account; @interface Person : NSObject{ @private int _age; } #pragma mark -    #pragma mark    @property (nonatomic,copy) NSString *name; #pragma mark    @property (nonatomic,retain) Account *account; #pragma mark -      #pragma mark        -(void)showMessage; @end

    Person.m
    //
    //  Person.m
    //  KVCAndKVO
    //
    //  Created by Kenshin Cui on 14-2-16.
    //  Copyright (c) 2014  Kenshin Cui. All rights reserved.
    //
    
    #import "Person.h" #import "Account.h" @implementation Person #pragma mark -      #pragma mark        -(void)showMessage{ NSLog(@"name=%@,age=%d",_name,_age); } #pragma mark        -(void)setAccount:(Account *)account{ _account=account; //   Account    [self.account addObserver:self forKeyPath:@"balance" options:NSKeyValueObservingOptionNew context:nil]; } #pragma mark -      #pragma mark   observeValueForKeyPath  ,               -(void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary *)change context:(void *)context{ if([keyPath isEqualToString:@"balance"]){//     balance   NSLog(@"keyPath=%@,object=%@,newValue=%.2f,context=%@",keyPath,object,[[change objectForKey:@"new"] floatValue],context); } } #pragma mark        -(void)dealloc{ [self.account removeObserver:self forKeyPath:@"balance"];//     //[super dealloc];//     ARC,        } @end

    main.m
    //
    //  main.m
    //  KVCAndKVO
    //
    //  Created by Kenshin Cui on 14-2-16.
    //  Copyright (c) 2014  Kenshin Cui. All rights reserved.
    //
    
    #import <Foundation/Foundation.h> #import "Person.h" #import "Account.h" int main(int argc, const char * argv[]) { @autoreleasepool { Person *person1=[[Person alloc]init]; person1.name=@"Kenshin"; Account *account1=[[Account alloc]init]; account1.balance=100000000.0; person1.account=account1; account1.balance=200000000.0;//                  observeValueForKeyPath: ofObject: change: context: //  :keyPath=balance,object=<Account: 0x100103aa0>,newValue=200000000.00,context=(null) } return 0; }

    上記のコードでは、ユーザにアカウントを割り当てる際にアカウントのbalanceプロパティにリスニングを追加し、リスニングコールバックメソッドにリスニングされた情報を出力し、オブジェクトの破棄時にリスニングを削除することで、典型的なKVOアプリケーションを構成しています.