web-dev-qa-db-ja.com

Objective-C:カテゴリのプロパティ/インスタンス変数

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を返します。上記のコードを最適化するにはどうすればよいですか?

117
dhrm

@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
122
Dave DeLong

。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 を参照してください

169
hfossli

与えられた答えはうまく機能し、私の提案は、単なる定型コードの記述を避けるための単なる拡張です。

カテゴリプロパティのゲッターメソッドとセッターメソッドを繰り返し記述することを避けるために、この回答ではマクロを紹介します。さらに、これらのマクロは、intBOOLなどのプリミティブ型プロパティの使用を容易にします。

マクロを使用しない従来のアプローチ

従来、次のようなカテゴリプロパティを定義します。

@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 intBOOLなどをサポートするマクロを追加できます。

制限事項

  1. すべてのマクロはデフォルトでOBJC_ASSOCIATION_RETAIN_NONATOMICを使用しています。

  2. App CodeなどのIDEは、現在、プロパティの名前をリファクタリングするときにセッターの名前を認識しません。自分で名前を変更する必要があります。

31
Lars Blumberg

libextobjc ライブラリを使用するだけです:

h-ファイル:

@interface MyClass (Variant)
@property (nonatomic, strong) NSString *test;
@end

m-ファイル:

#import <extobjc.h>
@implementation MyClass (Variant)

@synthesizeAssociation (MyClass, test);

@end

@ synthesizeAssociationの詳細

7
Mansurov Ruslan

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
3
Rikco