Цель-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
на
6 ответов:
метод @ lorean будет работать (Примечание: ответ удален), но у вас будет только один слот для хранения. Поэтому, если вы хотите использовать это на нескольких экземплярах и каждый экземпляр вычисляет отдельное значение, это не сработает.
к счастью, среда выполнения Objective-C имеет эту вещь под названием Связанные Объекты это может сделать именно то, что вы хотите:
#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.м-файл
#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
данный ответ Отлично работает, и мое предложение-это просто расширение к нему, которое позволяет избежать написания слишком много шаблонного кода.
чтобы избежать многократного написания методов getter и setter для свойств категории, этот ответ вводит макросы. Кроме того, эти макросы упрощают использование свойств примитивного типа, таких как
intилиBOOL.традиционный подход без макросов
традиционно вы определяете категорию свойство, как
@interface MyClass (Category) @property (strong, nonatomic) NSString *text; @endзатем вам нужно реализовать метод 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как тип свойства. Это делается путем обертывания целочисленного значения в
просто использовать libextobjc библиотека:
h-file:
@interface MyClass (Variant) @property (nonatomic, strong) NSString *test; @endm-file:
#import <extobjc.h> @implementation MyClass (Variant) @synthesizeAssociation (MyClass, test); @end
тестируется только с iOS 9 Пример: добавление свойства UIView в UINavigationBar (Category)
UINavigationBar+Помощник.h
#import <UIKit/UIKit.h> @interface UINavigationBar (Helper) @property (nonatomic, strong) UIView *tkLogoView; @endUINavigationBar+Помощник.м
#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
другое возможное решение, возможно, проще, которое не использует
Associated Objectsобъявляет переменную в файле реализации категории следующим образом:@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недостатком такого рода реализации является то, что объект не функционирует как переменная экземпляра, а скорее как переменная класса. Кроме того, атрибуты свойств не могут быть назначены(например, используемые в связанных объектах, таких как OBJC_ASSOCIATION_RETAIN_NONATOMIC)
Comments