Objective-Cのカテゴリに合成プロパティを作成できないため、次のコードを最適化する方法がわかりません。
@interface MyClass (Variant)
@property (nonatomic, strong) NSString *test;
@end
@implementation MyClass (Variant)
@dynamic test;
- (NSString *)test {
NSString *res;
//do a lot of stuff
return res;
}
@end
test-methodは実行時に複数回呼び出され、結果を計算するために多くのことをしています。通常、合成プロパティを使用して、メソッドが最初に呼び出されたときにIVar _testに値を保存し、次回はこのIVarを返します。上記のコードを最適化するにはどうすればよいですか?
@loreanのメソッドは機能します (注:回答は削除されました)、ただし、ストレージスロットは1つしかありません。したがって、これを複数のインスタンスで使用し、各インスタンスに個別の値を計算させる場合、機能しません。
さいわい、Objective-Cランタイムには Associated Objects と呼ばれるものがあります。これにより、必要な処理を正確に実行できます。
#import <objc/runtime.h>
static void *MyClassResultKey;
@implementation MyClass
- (NSString *)test {
NSString *result = objc_getAssociatedObject(self, &MyClassResultKey);
if (result == nil) {
// do a lot of stuff
result = ...;
objc_setAssociatedObject(self, &MyClassResultKey, result, OBJC_ASSOCIATION_RETAIN_NONATOMIC);
}
return result;
}
@end
。h-file
@interface NSObject (LaserUnicorn)
@property (nonatomic, strong) LaserUnicorn *laserUnicorn;
@end
。m-file
#import <objc/runtime.h>
static void * LaserUnicornPropertyKey = &LaserUnicornPropertyKey;
@implementation NSObject (LaserUnicorn)
- (LaserUnicorn *)laserUnicorn {
return objc_getAssociatedObject(self, LaserUnicornPropertyKey);
}
- (void)setLaserUnicorn:(LaserUnicorn *)Unicorn {
objc_setAssociatedObject(self, LaserUnicornPropertyKey, Unicorn, OBJC_ASSOCIATION_RETAIN_NONATOMIC);
}
@end
通常のプロパティのように-ドット表記でアクセス可能
NSObject *myObject = [NSObject new];
myObject.laserUnicorn = [LaserUnicorn new];
NSLog(@"Laser Unicorn: %@", myObject.laserUnicorn);
簡単な構文
または、次のように静的ポインターキーを作成する代わりに@selector(nameOfGetter)
を使用できます。
- (LaserUnicorn *)laserUnicorn {
return objc_getAssociatedObject(self, @selector(laserUnicorn));
}
- (void)setLaserUnicorn:(LaserUnicorn *)Unicorn {
objc_setAssociatedObject(self, @selector(laserUnicorn), Unicorn, OBJC_ASSOCIATION_RETAIN_NONATOMIC);
}
詳細については、 https://stackoverflow.com/a/16020927/202451 を参照してください
与えられた答えはうまく機能し、私の提案は、単なる定型コードの記述を避けるための単なる拡張です。
カテゴリプロパティのゲッターメソッドとセッターメソッドを繰り返し記述することを避けるために、この回答ではマクロを紹介します。さらに、これらのマクロは、int
やBOOL
などのプリミティブ型プロパティの使用を容易にします。
マクロを使用しない従来のアプローチ
従来、次のようなカテゴリプロパティを定義します。
@interface MyClass (Category)
@property (strong, nonatomic) NSString *text;
@end
次に、関連オブジェクトとgetセレクターをキーとして使用してgetterおよびsetterメソッドを実装する必要があります(- 元の回答を参照 ):
#import <objc/runtime.h>
@implementation MyClass (Category)
- (NSString *)text{
return objc_getAssociatedObject(self, @selector(text));
}
- (void)setText:(NSString *)text{
objc_setAssociatedObject(self, @selector(text), text, OBJC_ASSOCIATION_RETAIN_NONATOMIC);
}
@end
私の提案するアプローチ
次に、代わりにマクロを使用して記述します。
@implementation MyClass (Category)
CATEGORY_PROPERTY_GET_SET(NSString*, text, setText:)
@end
マクロは次のように定義されています。
#import <objc/runtime.h>
#define CATEGORY_PROPERTY_GET(type, property) - (type) property { return objc_getAssociatedObject(self, @selector(property)); }
#define CATEGORY_PROPERTY_SET(type, property, setter) - (void) setter (type) property { objc_setAssociatedObject(self, @selector(property), property, OBJC_ASSOCIATION_RETAIN_NONATOMIC); }
#define CATEGORY_PROPERTY_GET_SET(type, property, setter) CATEGORY_PROPERTY_GET(type, property) CATEGORY_PROPERTY_SET(type, property, setter)
#define CATEGORY_PROPERTY_GET_NSNUMBER_PRIMITIVE(type, property, valueSelector) - (type) property { return [objc_getAssociatedObject(self, @selector(property)) valueSelector]; }
#define CATEGORY_PROPERTY_SET_NSNUMBER_PRIMITIVE(type, property, setter, numberSelector) - (void) setter (type) property { objc_setAssociatedObject(self, @selector(property), [NSNumber numberSelector: property], OBJC_ASSOCIATION_RETAIN_NONATOMIC); }
#define CATEGORY_PROPERTY_GET_UINT(property) CATEGORY_PROPERTY_GET_NSNUMBER_PRIMITIVE(unsigned int, property, unsignedIntValue)
#define CATEGORY_PROPERTY_SET_UINT(property, setter) CATEGORY_PROPERTY_SET_NSNUMBER_PRIMITIVE(unsigned int, property, setter, numberWithUnsignedInt)
#define CATEGORY_PROPERTY_GET_SET_UINT(property, setter) CATEGORY_PROPERTY_GET_UINT(property) CATEGORY_PROPERTY_SET_UINT(property, setter)
マクロCATEGORY_PROPERTY_GET_SET
は、指定されたプロパティのゲッターとセッターを追加します。読み取り専用または書き込み専用のプロパティは、それぞれCATEGORY_PROPERTY_GET
およびCATEGORY_PROPERTY_SET
マクロを使用します。
プリミティブ型にはもう少し注意が必要です
プリミティブ型はオブジェクトではないため、上記のマクロにはunsigned int
をプロパティの型として使用する例が含まれています。これは、整数値をNSNumber
オブジェクトにラップすることにより行います。したがって、その使用法は前の例に類似しています。
@interface ...
@property unsigned int value;
@end
@implementation ...
CATEGORY_PROPERTY_GET_SET_UINT(value, setValue:)
@end
このパターンに従って、単にsigned int
、BOOL
などをサポートするマクロを追加できます。
制限事項
すべてのマクロはデフォルトでOBJC_ASSOCIATION_RETAIN_NONATOMIC
を使用しています。
App CodeなどのIDEは、現在、プロパティの名前をリファクタリングするときにセッターの名前を認識しません。自分で名前を変更する必要があります。
libextobjc ライブラリを使用するだけです:
h-ファイル:
@interface MyClass (Variant)
@property (nonatomic, strong) NSString *test;
@end
m-ファイル:
#import <extobjc.h>
@implementation MyClass (Variant)
@synthesizeAssociation (MyClass, test);
@end
IOS 9のみでテスト済み例:UIViewプロパティをUINavigationBarに追加(カテゴリ)
UINavigationBar + Helper.h
#import <UIKit/UIKit.h>
@interface UINavigationBar (Helper)
@property (nonatomic, strong) UIView *tkLogoView;
@end
UINavigationBar + Helper.m
#import "UINavigationBar+Helper.h"
#import <objc/runtime.h>
#define kTKLogoViewKey @"tkLogoView"
@implementation UINavigationBar (Helper)
- (void)setTkLogoView:(UIView *)tkLogoView {
objc_setAssociatedObject(self, kTKLogoViewKey, tkLogoView, OBJC_ASSOCIATION_RETAIN_NONATOMIC);
}
- (UIView *)tkLogoView {
return objc_getAssociatedObject(self, kTKLogoViewKey);
}
@end