web-dev-qa-db-ja.com

Key-Valueコーディングを使用して、オブジェクトのキーが存在するかどうかをどのように判断しますか?

オブジェクトにiPhoneSDKで書き込み可能な@propertyがあるかどうかをテストしたいと思います。

これを行う1つの可能な方法は、-valueForKey:メソッドをチェックすることですが、それはかなりエレガントではないようです。

例:

  @try {
    id *value = [instance valueForKey:@"myProperty"];
  }
  @catch (NSException * e) {
    // Key did not exist
  }

これを行うためのより良い方法はありますか?

39
Ted

チェックされているオブジェクトを作成している場合は、_valueForUndefinedKey:_と_setValue:forUndefinedKey_をオーバーライドして、例外を発生させるよりも便利なことを行うことができます。

一方、実行時に知らないオブジェクトをイントロスペクトしようとしている場合は、ランタイムメソッドを使用してそれを行う必要があります。 Objective-cランタイム自体を使用して_class_copyPropertyList_または_protocol_copyPropertyList_を呼び出してそれらを処理するか、Foundationを使用してKVCゲッター/セッターのオブジェクトでrespondsToSelectorを呼び出すことができます。特定のプロパティ。たとえば、プロパティfooの場合、[someObject respondsToSelector:NSSelectorFromString(@"foo")];のようなものを呼び出します。

56
Jason Coco

一般に、オブジェクトに特定のキーの値があるかどうかを判断する方法はありません。インスタンスは、-valueForUndefinedKey:メソッドから他の方法で定義されていないキーの値を返すことを決定する場合があります。または、デフォルトの実装で例外がスローされる場合があります。最新のObjective-C(2.0)オブジェクト多くの場合は、パブリックAPIで関連するプロパティを宣言します。これらのオブジェクトの場合、Objective-Cランタイムのclass_copyPropertyListまたはprotocol_copyPropertyListを使用して、使用可能なプロパティのリストを取得できます。 KVC検索リストをエミュレートして、インスタンスが適切なゲッターメソッドに応答するかどうかをrepondsToSelector:でテストすることもできます。最後に、ランタイムのclass_copyIvarListを使用して、ivarが適切なivarであるかどうかを確認できます。これはたくさんのあなたがしてはいけないの仕事です。 valueForKey:メッセージが送信されたときに、オブジェクトインスタンスが例外を発生させるかどうかを保証するものではなく、より大きな問題を示しています...

特定のキーの値をすべて提供する必要があるオブジェクトのコレクションがあり、提供しないものもある場合は、設計上の問題があります。必要なプロパティを宣言する適切なインターフェイス(Objective-Cでは@protocol)を定義する必要があります。次に、このプロトコルへの準拠をテストするか、コンパイラを使用してコンパイル時の型チェックを実行し、インスタンスがプロトコルに準拠していることを確認します(単一のインスタンスを渡す場合)。

5
Barry Wark

@ジェイソンココの答えから続く。

これは、設定しようとしているプロパティの「設定」セレクターの存在を具体的に確認する方法についての私の提案です。

これは純粋な汚物ですが、機能し、私はそれを使用していることに気づきました。あなたは警告されました。

NS_INLINE SEL _Nonnull IBPropertySetSelectorForPropertyName(NSString*_Nonnull propertyName)
{

    NSString* firstLetter = [propertyName substringToIndex:1];
    propertyName = [propertyName substringFromIndex:1];

    propertyName = [[NSString alloc] initWithFormat:@"set%@%@:",firstLetter.capitalizedString,propertyName];

    return NSSelectorFromString(propertyName);
}

使用法:

Swiftの場合:コードスニペットをブリッジヘッダーに追加してから、次のようにします。

let key = "someKey"    
let sel = IBPropertySetSelectorForPropertyName(key)

if SOMETHING.responds(to: sel) {SOMETHING.setValue(value, forKeyPath: key)}
1
Chris Birch