The given answer works great and my proposal is just an extension to it that avoids writing too much boilerplate code.
In order to avoid writing repeatedly getter and setter methods for category properties this answer introduces macros. Additionally these macros ease the use of primitive type properties such as int
or BOOL
.
Traditional approach without macros
Traditionally you define a category property like
@interface MyClass (Category)
@property (strong, nonatomic) NSString *text;
@end
Then you need to implement a getter and setter method using an associated object and the get selector as the key (see original answer):
#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
My suggested approach
Now, using a macro you will write instead:
@implementation MyClass (Category)
CATEGORY_PROPERTY_GET_SET(NSString*, text, setText:)
@end
The macros are defined as following:
#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)
The macro CATEGORY_PROPERTY_GET_SET
adds a getter and setter for the given property. Read-only or write-only properties will use the CATEGORY_PROPERTY_GET
and CATEGORY_PROPERTY_SET
macro respectively.
Primitive types need a little more attention
As primitive types are no objects the above macros contain an example for using unsigned int
as the property's type. It does so by wrapping the integer value into a NSNumber
object. So its usage is analog to the previous example:
@interface ...
@property unsigned int value;
@end
@implementation ...
CATEGORY_PROPERTY_GET_SET_UINT(value, setValue:)
@end
Following this pattern, you can simply add more macros to also support signed int
, BOOL
, etc...
Limitations
All macros are using OBJC_ASSOCIATION_RETAIN_NONATOMIC
by default.
IDEs like App Code do currently not recognize the setter's name when refactoring the property's name. You would need to rename it by yourself.