Цель-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


на

576   6  

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;
@end

m-file:

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

@synthesizeAssociation (MyClass, test);

@end

подробнее о @synthesizeAssociation

тестируется только с iOS 9 Пример: добавление свойства UIView в UINavigationBar (Category)

UINavigationBar+Помощник.h

#import <UIKit/UIKit.h>

@interface UINavigationBar (Helper)
@property (nonatomic, strong) UIView *tkLogoView;
@end

UINavigationBar+Помощник.м

#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

    Ничего не найдено.