既存のクラスで関連オブジェクトを使用してカスタムデータを保存する

3890 ワード

Effective Objective-C 2.0高品質iOSとOS Xコードを記述既存クラスで関連オブジェクトを使用してカスタムデータを格納
クラスのインスタンスは、あるメカニズムによって作成される場合がありますが、開発者は法令を持っていません.このメカニズムは、自分が書いたサブクラスインスタンスを作成します.オブジェクトに多くの他のオブジェクトを関連付けることができます.これらのオブジェクトはキーで区別されます.オブジェクトの値を格納する場合は、対応するメモリ管理の意味を維持するためにストレージポリシーを指定できます.ストレージポリシーはobjc_という名前でAssociationPolicyの列挙定義.関連オブジェクトが属性になると、対応する意味が得られます.
関連タイプ
等価@propertyプロパティ
OBJC_ASSOCIATION_ASSIGN
assign
OBJC_ASSOCIATION_RETAIN_NONATOMIC
nonatomic,retain
OBJC_ASSOCIATION_COPY_NONATOMIC
nonatomic,copy
OBJC_ASSOCIATION_RETAIN
retain
OBJC_ASSOCIATION_COPY
copy
次の方法で、関連オブジェクトを管理します:1 void objc_setAssociatedObject(id object, void *key, id value, objc_AssociationPolicy policy); このメソッドでは、指定したキーとポリシーでオブジェクトに関連付けられたオブジェクト値を設定します.
② void objc_getAssociatedObject(id object, void *key); このメソッドは、指定されたキーに基づいてオブジェクトから対応する関連オブジェクト値を取得します.
③ void objc_removeAssociatedObjects(id object); この方法では、指定したオブジェクトのすべての関連オブジェクトを除去します.
関連オブジェクトを設定するために使用されるキーは、「不透明なポインタ」です.つまり、指向するデータ構造は特定のタイプのポインタに限定されません.さらに関連オブジェクトの値を設定するときに、2つのキーを同じ値に一致させるには、両方のキーが完全に同じポインタでなければなりません.NSDictionaryとは違います.NSDictionaryは「isEqual:」がYESを返すと両者は同じであると考える.したがって、関連オブジェクトの値を設定する場合は、通常、静的グローバル変数をキーとして使用します.
関連オブジェクトの使用方法
iOSを開発する際によく使われるUIalertViewクラスは、ユーザーに警告情報を表示するための標準的なビューを提供する.ユーザがボタンを押すこのビューを閉じる場合、この動作を処理するためにプロトコル(delegate protocol)を依頼する必要があるが、この依頼メカニズムを設定するには、警告ビューの作成とボタンの動作を処理するコードを分離しなければならない.コードが二つに分かれているので、取り締まりが少し乱れている.例えば、UIAlertViewを使用する場合、一般的には次のように書かれています.
- (IBAction)buttonClick {
    UIAlertView *alert = [[UIAlertView alloc]initWithTitle:@"  " message:@"    " delegate:self cancelButtonTitle:@"  " otherButtonTitles:@"  ", nil];
    [alert show];
}

#pragma mark - UIAlertViewDelegate
-(void)alertView:(UIAlertView *)alertView clickedButtonAtIndex:(NSInteger)buttonIndex {
    if (buttonIndex == 0) {
        //     
        NSLog(@"    ");
    } else if(buttonIndex == 1){
        //     
         NSLog(@"   ");
    }
}

同じクラスで複数の警告情報ビューを処理すると、コードがより複雑になり、delegateメソッドで入力alertViewパラメータをチェックし、それに基づいて対応する論理を選択する必要があります.警告ビューを作成するときに、各ボタンを処理する論理を直接書くことができれば、ずっと簡単です.これは、オブジェクトを関連付けることによって行うことができる.警告ビューを作成する後、delegateメソッドが実行されるまで、それに関連付けられたブロックを設定.このシナリオの実装コードは次のとおりです.
#import 

static void *MKAlertViewKey = @"MKAlertViewKey";

- (IBAction)buttonClick {
    UIAlertView *alert = [[UIAlertView alloc]initWithTitle:@"  " message:@"    " delegate:self cancelButtonTitle:@"  " otherButtonTitles:@"  ", nil];
    
    void (^block)(NSInteger) = ^(NSInteger buttonIndex){
        if (buttonIndex == 0) {
            //     
            NSLog(@"    ");
        } else if(buttonIndex == 1){
            //     
            NSLog(@"    ");
        }
    };
    
    objc_setAssociatedObject(alert, MKAlertViewKey, block, OBJC_ASSOCIATION_COPY);
    
    [alert show];
}

#pragma mark - UIAlertViewDelegate
-(void)alertView:(UIAlertView *)alertView clickedButtonAtIndex:(NSInteger)buttonIndex {
    void (^block)(NSInteger) = objc_getAssociatedObject(alertView, MKAlertViewKey);
    block(buttonIndex);
}

このように書き換えた後、作成警告ビューと処理操作結果のコードを一緒に置くことで、2つのコードの間を泳ぐ必要がなく、警告ビューの用途が分かるため、従来よりも読みやすい.しかし、このスキームを採用する際、blockはいくつかの変数をキャプチャする可能性があり、これは循環参照(retain cycle)をもたらす可能性があることに注意する必要がある.
皆さんがご覧のように、このやり方は役に立ちますが、他の方法が通じないときに考えて使うべきです.乱用すると、コードがすぐに暴走し、デバッグが困難になる.ループリファレンスが生じる原因は、インタフェースで予め定められたものではなく、関連付けの際に定義されるため明らかに困難である.このような書き方を使うときは、どこかでその書き方ができるからといって、必ず彼を使うわけにはいかない.このUIAlertViewを作成するには、中継子クラスからblockを子クラスの属性として保存する方法がある.alertビューを複数回使用する必要がある場合は、関連オブジェクトを使用するよりも良い.
要点
  • は、「関連オブジェクト」機構により2つのオブジェクトを連結することができる.
  • 関連オブジェクトを定義際にメモリ管理の意味を指定する、属性を定義する際に用いる「所有関係」と「非所有関係」を模倣することができる.
  • 関連オブジェクトは、通常、検索が困難なバグを導入するため、他の方法が実行できない場合にのみ選択される.