Objective-C:类别中的属性/实例variables
由于我不能在Objective-C的类别中创build一个合成的属性,我不知道如何优化下面的代码:
@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
testing方法在运行时被多次调用,我正在做很多事情来计算结果。 通常使用综合属性,我第一次调用该方法时,将值存储在IVar _test中,下次仅返回该IVar。 我怎样才能优化上面的代码?
@ Iorean的方法可以工作,但你只有一个存储插槽。 所以,如果你想在多个实例上使用它,并让每个实例计算一个不同的值,那么这是行不通的。
幸运的是,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文件
@interface NSObject (LaserUnicorn) @property (nonatomic, strong) LaserUnicorn *laserUnicorn; @end
.M文件
#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)
而不是像这样创build静态指针键:
- (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
给出的答案很好,我的build议只是对它的扩展。 为了避免重复编写类别属性的getter和setter方法,我引入了可以简化其使用的macros。 另外,这些macros可以简化对基本types属性(如int
或BOOL
。
传统的做法
传统上你可以定义一个类别属性
@interface MyClass (Category) @property (strong, nonatomic) NSString *text; @end
然后,您需要使用关联对象和getselect器作为关键字来实现getter和setter方法( 请参阅原始答案 ):
@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
使用macros
现在,使用macros,你会写:
@implementation MyClass (Category) CATEGORY_PROPERTY_GET_SET(NSString*, text, setText:) @end
这些macros定义如下:
#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)
macrosCATEGORY_PROPERTY_GET_SET
为给定属性添加一个getter和setter。 只读或只写属性将分别使用CATEGORY_PROPERTY_GET
和CATEGORY_PROPERTY_SET
macros。
由于原始types不是对象,我添加了一个示例macros,允许使用unsigned int
作为属性的types。 它通过将整数值包装到一个NSNumber
对象中。 所以它的用法与前面的例子类似:
@interface ... @property unsigned int value; @end @implementation ... CATEGORY_PROPERTY_GET_SET_UINT(value, setValue:) @end
可以添加更多的macros来支持signed int
, BOOL
等等。
限制
-
所有macros默认使用
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
更多关于@synthesizeAssociation的信息
仅使用iOS 9进行testing示例:将UIView属性添加到UINavigationBar(Category)
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
另一个可能的解决scheme,也许更容易,不使用Associated Objects
是在类别实现文件中声明一个variables,如下所示:
@interface UIAlertView (UIAlertViewAdditions) - (void)setObject:(id)anObject; - (id)object; @end @implementation UIAlertView (UIAlertViewAdditions) id _object = nil; - (id)object { return _object; } - (void)setObject:(id)anObject { _object = anObject; } @end
这种实现的缺点是对象不能作为一个实例variables,而是作为一个类variables。 此外,不能分配属性属性(例如,在诸如OBJC_ASSOCIATION_RETAIN_NONATOMIC之类的关联对象中使用)