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属性(如intBOOL

传统的做法

传统上你可以定义一个类别属性

 @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_GETCATEGORY_PROPERTY_SETmacros。

由于原始types不是对象,我添加了一个示例macros,允许使用unsigned int作为属性的types。 它通过将整数值包装到一个NSNumber对象中。 所以它的用法与前面的例子类似:

 @interface ... @property unsigned int value; @end @implementation ... CATEGORY_PROPERTY_GET_SET_UINT(value, setValue:) @end 

可以添加更多的macros来支持signed intBOOL等等。

限制

  1. 所有macros默认使用OBJC_ASSOCIATION_RETAIN_NONATOMIC

  2. 重构属性名称时,像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之类的关联对象中使用)