サーチ…


最も一般的な実生活キー値のコーディング例

キー値コーディングは、 NSKeyValueCodingプロトコルを使用してNSObjectに統合されています。

これはどういう意味ですか?

これは、任意のidオブジェクトがvalueForKeyメソッドやvalueForKeyPathなどのさまざまなバリアントを呼び出すことができることを意味します。

また、どのidオブジェクトもsetValueメソッドとそのさまざまなバリアントを呼び出すことができます。

例:

id obj = [[MyClass alloc] init];
id value = [obj valueForKey:@"myNumber"];

int myNumberAsInt = [value intValue];
myNumberAsInt = 53;
[obj setValue:@(myNumberAsInt) forKey:@"myNumber"];

例外:

上の例では、MyClassにmyNumberというNSNumberプロパティがあると仮定しています。 myNumberがMyClassインタフェース定義に現れない場合、NSUndefinedKeyExceptionはおそらく2行目と5行目の両方で発生する可能性があります。

this class is not key value coding-compliant for the key myNumber.

なぜこんなに強力なのですか?

クラスのプロパティにアクセスできるコードを書くことができます。そのコードは、そのクラスのインターフェイスを必要とせずに動的にアクセスできます。つまり、NSObjectから派生したオブジェクトのプロパティから値を表示することができます。ただし、そのプロパティ名は実行時に動的に提供されます。

上記の例では、コードはMyClassが使用可能でなくても動作し、呼び出しコードで使用可能なid型objは使用できません。

キー値の観測

キー値観測の設定

この場合、オブザーバが所有するオブジェクトのcontentOffsetを観察したい

//
// Class to observe
//
@interface XYZScrollView: NSObject
@property (nonatomic, assign) CGPoint contentOffset;
@end

@implementation XYZScrollView
@end


//
// Class that will observe changes
//
@interface XYZObserver: NSObject
@property (nonatomic, strong) XYZScrollView *scrollView;
@end

@implementation XYZObserver

// simple way to create a KVO context
static void *XYZObserverContext = &XYZObserverContext;


// Helper method to add self as an observer to 
// the scrollView's contentOffset property
- (void)addObserver {

    // NSKeyValueObservingOptions
    //
    // - NSKeyValueObservingOptionNew
    // - NSKeyValueObservingOptionOld
    // - NSKeyValueObservingOptionInitial
    // - NSKeyValueObservingOptionPrior
    //
    // can be combined:
    // (NSKeyValueObservingOptionNew | NSKeyValueObservingOptionOld)
    
    NSString *keyPath = NSStringFromSelector(@selector(contentOffset));
    NSKeyValueObservingOptions options = NSKeyValueObservingOptionNew;    

    [self.scrollView addObserver: self 
                      forKeyPath: keyPath 
                         options: options
                         context: XYZObserverContext];
}

- (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary<NSString *,id> *)change context:(void *)context {
    
    if (context == XYZObserverContext) { // check the context

        // check the keyPath to see if it's any of the desired keyPath's.
        // You can observe multiple keyPath's
        if ([keyPath isEqualToString: NSStringFromSelector(@selector(contentOffset))]) {

            // change dictionary keys:
            // - NSKeyValueChangeKindKey
            // - NSKeyValueChangeNewKey
            // - NSKeyValueChangeOldKey
            // - NSKeyValueChangeIndexesKey
            // - NSKeyValueChangeNotificationIsPriorKey
            
            // the change dictionary here for a CGPoint observation will
            // return an NSPoint, so we can take the CGPointValue of it.
            CGPoint point = [change[NSKeyValueChangeNewKey] CGPointValue];
            
            // handle point
        }
        
    } else {

        // if the context doesn't match our current object's context
        // we want to pass the observation parameters to super
        [super observeValueForKeyPath: keyPath
                             ofObject: object
                               change: change
                              context: context];
    }
}

// The program can crash if an object is not removed as observer 
// before it is dealloc'd
//
// Helper method to remove self as an observer of the scrollView's
// contentOffset property
- (void)removeObserver {
    NSString *keyPath = NSStringFromSelector(@selector(contentOffset));
    [self.scrollView removeObserver: self forKeyPath: keyPath];
}

@end

KVCデータのクエリ

if ([[dataObject objectForKey:@"yourVariable"] isEqualToString:"Hello World"]) {
    return YES;
} else {
    return NO;
}

KVCを使用して保存された値は、ローカル変数として取得またはキャストする必要なく、迅速かつ簡単にクエリできます。

コレクション演算子

コレクション演算子はKVC NSSet "コレクション型"プロパティ(つまりNSArrayNSSetなど)で操作を実行するために使用できます。たとえば、実行する一般的な操作は、コレクション内のオブジェクトを数えることです。これを実現するには、 @count コレクション演算子を使用し@count

self.array = @[@5, @4, @3, @2, @1];
NSNumber *count = [self.array valueForKeyPath:@"@count"];
NSNumber *countAlt = [self valueForKeyPath:@"array.@count"];
// count == countAlt == 5

これは、(私たちがアクセスした可能性があり、ここで完全に冗長ですがcountプロパティを)それはほとんど必要ですが、それは、機会に役立ちます 。ありますが、しかし、はるかに便利な、すなわち、あるいくつかのコレクション演算子@max@min@sum@avg@unionOf家族。これらの事業者はまた 、オペレータが正しく機能するには、以下の独立したキーのパスを必要とすることに注意することが重要です。ここでは、それらのリストと、それらが扱うデータの種類を示します。

オペレーターデータ・タイプ
@count (無し)
@max NSNumberNSDateint (および関連する)など
@min NSNumberNSDateint (および関連する)など
@sum NSNumberint (および関連する)、 double (および関連する)など
@avg NSNumberint (および関連する)、 double (および関連する)など
@unionOfObjects NSArrayNSSetなど
@distinctUnionOfObjects NSArrayNSSetなど
@unionOfArrays NSArray<NSArray*>
@distinctUnionOfArrays NSArray<NSArray*>
@distinctUnionOfSets NSSet<NSSet*>

@max@minは、コレクション内のオブジェクトのプロパティの最高値または最低値をそれぞれ返します。たとえば、次のコードを見てください。

// “Point” class used in our collection
@interface Point : NSObject

@property NSInteger x, y;

+ (instancetype)pointWithX:(NSInteger)x y:(NSInteger)y;

@end

...

self.points = @[[Point pointWithX:0 y:0],
                [Point pointWithX:1 y:-1],
                [Point pointWithX:5 y:-6],
                [Point pointWithX:3 y:0],
                [Point pointWithX:8 y:-4],
];

NSNumber *maxX = [self valueForKeyPath:@"[email protected]"];
NSNumber *minX = [self valueForKeyPath:@"[email protected]"];
NSNumber *maxY = [self valueForKeyPath:@"[email protected]"];
NSNumber *minY = [self valueForKeyPath:@"[email protected]"];

NSArray<NSNumber*> *boundsOfAllPoints = @[maxX, minX, maxY, minY];

...

Key-Value Codingコレクション演算子の力を借りて、4行のコードと純粋なFoundationで、配列のすべての点をカプセル化する長方形を抽出することができました。

これらの比較はオブジェクトに対してcompare:メソッドを呼び出すことによって行われるので、独自のクラスをこれらの演算子と互換性を持たせたい場合は、このメソッドを実装する必要があります。

@sumはおそらく推測できるように、プロパティのすべての値を追加します。

@interface Expense : NSObject

@property NSNumber *price;

+ (instancetype)expenseWithPrice:(NSNumber *)price;

@end

...

self.expenses = @[[Expense expenseWithPrice:@1.50],
                  [Expense expenseWithPrice:@9.99],
                  [Expense expenseWithPrice:@2.78],
                  [Expense expenseWithPrice:@9.99],
                  [Expense expenseWithPrice:@24.95]
];

NSNumber *totalExpenses = [self valueForKeyPath:@"[email protected]"];

ここでは、 @sumを使用して、配列内のすべての費用の合計価格を@sumました。代わりに、私たちが各費用のために支払っている平均価格を見つけたければ、私は@avgを使うことができます:

NSNumber *averagePrice = [self valueForKeyPath:@"[email protected]"];

最後に、 @unionOfファミリがあります。このファミリには5つの異なる演算子がありますが、それぞれの演算子はほとんど同じですが、それぞれの演算子の違いはわずかです。まず、配列内のオブジェクトのプロパティの配列を返す@unionOfObjectsがあります:

// See "expenses" array above

NSArray<NSNumber*> *allPrices = [self valueForKeyPath:
    @"[email protected]"];

// Equal to @[ @1.50, @9.99, @2.78, @9.99, @24.95 ]

@distinctUnionOfObjects機能に同じ@unionOfObjects 、それは重複を削除します。

NSArray<NSNumber*> *differentPrices = [self valueForKeyPath:
    @"[email protected]"];

// Equal to @[ @1.50, @9.99, @2.78, @24.95 ]

最後に、 @unionOfファミリの最後の3つの演算子は一歩進んで、二重にネストされた配列に含まれるプロパティの値の配列を返します。

NSArray<NSArray<Expense*,Expense*>*> *arrayOfArrays =
    @[
        @[ [Expense expenseWithPrice:@19.99],
           [Expense expenseWithPrice:@14.95],
           [Expense expenseWithPrice:@4.50],
           [Expense expenseWithPrice:@19.99]
         ],

        @[ [Expense expenseWithPrice:@3.75],
           [Expense expenseWithPrice:@14.95]
         ]
     ];

// @unionOfArrays
NSArray<NSNumber*> allPrices = [arrayOfArrays valueForKeyPath:
    @"@unionOfArrays.price"];
// Equal to @[ @19.99, @14.95, @4.50, @19.99, @3.75, @14.95 ];

// @distinctUnionOfArrays
NSArray<NSNumber*> allPrices = [arrayOfArrays valueForKeyPath:
    @"@distinctUnionOfArrays.price"];
// Equal to @[ @19.99, @14.95, @4.50, @3.75 ];

この例では見つからないものは@distinctUnionOfSets 、これは@distinctUnionOfSetsとまったく同じ@distinctUnionOfArrays 、代わりにNSSetを使用して返されます(セットではすべてのオブジェクトが区別されなければならないため、非distinctバージョンはありません)。

以上です!コレクション演算子は、正しく使用すると本当に強力なものになり、不必要なものをループする必要を避けるのに役立ちます。

最後の注意: NSNumberの配列に標準コレクション演算子を使用することもできます(追加のプロパティアクセスなし)。これを行うには、オブジェクトを返すだけのself擬似プロパティにアクセスします。

NSArray<NSNumber*> *numbers = @[@0, @1, @5, @27, @1337, @2048];

NSNumber *largest = [numbers valueForKeyPath:@"@max.self"];
NSNumber *smallest = [numbers valueForKeyPath:@"@min.self"];
NSNumber *total = [numbers valueForKeyPath:@"@sum.self"];
NSNumber *average = [numbers valueForKeyPath:@"@avg.self"];


Modified text is an extract of the original Stack Overflow Documentation
ライセンスを受けた CC BY-SA 3.0
所属していない Stack Overflow