1. @dynamic と @synthesize の違い
@property には、対応する単語が 2 つあり、1 つは @synthesize で、もう 1 つは @dynamic です。 @synthesize も @dynamic も記述されていない場合、デフォルトは @syntheszie です。 var = _var;
@synthesize のセマンティクスは、setter メソッドと getter メソッドを手動で実装しない場合、コンパイラが自動的にこれを追加します。 2つの方法があります。
@dynamic は、プロパティの setter メソッドと getter メソッドがユーザー自身によって実装され、自動的には生成されないことをコンパイラーに伝えます。 (もちろん、読み取り専用プロパティの場合は、getter を指定するだけで済みます)。プロパティが @dynamic var として宣言されており、@setter メソッドと @getter メソッドを提供しない場合、コンパイル時には問題ありませんが、プログラムが instance.var = someVar まで実行されると、プログラムがクラッシュします。 setter メソッドが不足している場合、または someVar = var を実行する場合、getter メソッドが不足しているとクラッシュが発生します。コンパイル時には問題なく、実行時には対応するメソッドが実行されます。これがいわゆる動的バインディングです。
2. プライベート変数を使用して @dynamic アクセス メソッドを実装します
1) Book.h
#import
#import
@インターフェース Book :NSObject
{
@private
__strong NSString *_name;
__strong NSString *_author;
}
@property(nonatomic, copy ) NSString *name;
@property (nonatomic, copy) NSString *author;
@property(nonatomic, copy) NSString*version;
@end
2) Book.m
#import "Book.h"
@implementation Book
@動的名;
@動的著者;
@synthesizeversion = _version;
- (id)init
{
self = [super init];
if(self) {
_name = @"inputbook name を忘れました" } p", _name }
Return _author; 上記のコードからわかるように、@ を使用してください。動的の後、アクセス メソッドでプライベート変数にアクセスして、値を割り当てたり取得したりできます。 。また、 @synthesize は @synthesize var = _var; を直接使用して、属性とプライベート変数を直接同等にします。これが両者の書き方の違いです。
3. メッセージ転送による @dynamic アクセス メソッドの実装
@dynamic var = _var が属性に使用されている場合、コンパイラはすぐにエラーを報告します。このように、@synthesizeのようにvarのsetterメソッドやgetterメソッドで_varを使うことはできません
- (void)setVar:(id)newVar
{
self.var = newVar;
}
- (void)var
{
return self.var;
}
これら 2 つのメソッドは自分自身を呼び出します。これにより、無限ループが発生し、プログラム崩壊。
ここでは、メッセージ転送メカニズムを使用して @dynamic setter メソッドと getter メソッドを実装する方法を示します。
最初のコード:
1) Book.h
#import
@interface Book: NSObject
{
@private
NSMutableDictionary *_propertiesDict;
}
@property (非アトミック、コピー) NSString *name;
@property (非アトミック、コピー) NSString*version; ) Book.h"
@implementation Book
@dynamic name; // name = _name として書くことはできません; そうしないと、コンパイラーはすぐにエラーを報告します
@dynamic author;
@synthesizeversion;
- (id)init
{
self = [スーパー init];
if(self)
{
_propertiesDict = [[NSMutableDictionary alloc] init];
}
return self;
}
- ture *)methodSignatureForSelector:(SEL)selector
{
NSString *sel = NSStringFromSelector(selector);
if ([sel rangeOfString:@"set"].location == 0)
{
return [NSMethodSignature signatureWithObjCTypes:"v@:@"];
}
その他
{
return [NSMethodSignature signatureWithObjCTypes:"@@:"];
}
}
- (void)forwardInvocation:(NSInvocation *)invocation
{
NSString *key = NSStringFromSelector([呼び出しselector]);
if ([key rangeOfString:@"set"].location == 0)
{
key= [[key substringWithRange:NSMakeRange(3, [key length]-4)] lowercaseString];
NSString *obj;
[invocation getArgument:&objatIndex:2];
[_propertiesDict setObject:obj forKey:key];
}
else
{
NSString *obj = [_propertiesDict objectForKey:key] ;
[invocation setReturnValue:&obj];
}
}
@end
3)main.m
#import
#import 「本。 h"
int main(int argc, const char * argv[])
{
@autoreleasepool
{
Book *book = [[Book alloc] init];
書籍名= @"c++ 入門書";
book.author = @"Stanley B.Lippman";
book.version = @"5.0";
NSLog(@"%@", book.name);
NSLog (@"%@", book.author);
NSLog(@"%@", book.version);
}
return 0;
}
程序分析:
1)在プログラムはメッセージ送信機能を追加する前に、methodSignatureForSelector: と forwardInvocation: の 2 つのメソッドをカバーする必要があります。methodSignatureForSelector: の機能は、別の種類のメッセージを実現するために有効なメソッド名を作成します。转発行给一个真正实现このメッセージのオブジェクトを完了しました。例 1: - (NSString *)name
このメソッドには 2 つのパラメータがあります: self と_cmd。
例 2: - (void)setValue:(int)val
このメソッドには 3 つのパラメータがあります: self、_cmd および val。
自動的に実現されるメソッドのパラメータ タイプには次のような要求があります:
A. 最初のパラメータ タイプは id (自分自身のタイプ) である必要があります
B. 2 番目のパラメータ タイプSEL (_cmd の種類) である必要があります
C. 3 番目のパラメータから、元のメソッドのパラメータの種類を指定できます。 2 つの例を説明します。
例2: 再比較の場合、setName:(NSString *)name 内のパラメータ名の文字は文字列型であるため、3 番目のパラメータ型は @
3)main.m には、book.name = @"c++ primer";プログラムがここに移動するときに、Book.m 内の setName: このメソッドが存在します。ただし、Book.m にはこのメソッドはありません。このメソッドは、その後、プログラムがmethodSignatureForSelector:に入力してメッセージ送信を実行します。実行が完了すると、メソッドの名前の種類として「v@:@」が返されます。
ここでの v@:@ とは何ですか?実際、ここでの最初の文字 v は、関数の戻り値の型が void であることを表します。次の 3 文字は、上記 2) の説明を参照してください。これらは、self、_cmd、name、の 3 つのパラメータの型です。 id、SEL、NSString。
次に、プログラムは forwardInvocation メソッドに入ります。取得したキーはメソッド名 setName: で、[invocationgetArgument:&objatIndex:2]; を使用してパラメータ値を取得します (ここでは「C++ 入門」)。なぜここのインデックスは 2 である必要があるのでしょうか?前に分析したように、0 番目のパラメータは self、1 番目のパラメータは _cmd、2 番目のパラメータはメソッドの背後にあるパラメータです。
最後に、変数辞書を使用して値を割り当てます。これでセッターのプロセス全体が完了します。
4) main.m には NSLog(@"%@", book.name); というコードがあります。プログラムがここで実行されると、name の値メソッドを見つけるために Book.m に移動します。 。ただし、Book.m にはそのような値メソッドがないため、プログラムは、methodSignatureForSelector: を入力してメッセージを転送します。実行後はメソッドのシグネチャの種類として「@@:」が返されます。ここで、最初の文字 @ は関数の戻り値の型 NSString を表し、2 番目の文字 @ は self の型 ID を表し、3 番目の文字: は _cmd の型 SEL を表します。
次に、プログラムは forwardInvocation メソッドに入ります。取得したキーはメソッド名nameです。最後に、このキーに基づいて辞書から対応する値が取得され、ゲッター プロセス全体が完了します。
5) コードのデバッグのプロセス中に、name と author の割り当てと値のみが、methodSignatureForSelector: と forwardInvocation: の 2 つのメソッドに入力されることが判明したことに注意してください。また、methodSignatureForSelector: と forwardInvocation: の 2 つのメソッドを入力しない属性バージョンもあります。これは、version 属性が @synthesize としてマークされており、コンパイラーが setVersion メソッドと version メソッドを自動的に追加するため、メッセージを転送する必要がないためです。
4. NSManatedObject のサブクラスでの @dynamic の使用
@dynamic の最も一般的な使用は NSManatedObject 内で行われます。現時点では、プログラミングの setter メソッドと getter メソッドを表示する必要はありません。その理由は、@dynamic がコンパイラーに処理を行わないよう指示し、そのゲッター メソッドとセッター メソッドが実行時に動的に作成され、Core Data フレームワークがそのようなプロパティへのアクセス メソッドを生成することを許可するためです。