Подтвердить что ты не робот

Objective-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

Тест-метод вызывается несколько раз во время выполнения, и я делаю много всего, чтобы вычислить результат. Обычно с использованием синтезированного свойства я сохраняю значение в IVar _test при первом вызове метода и просто возвращаю этот IVar в следующий раз. Как я могу оптимизировать вышеуказанный код?

4b9b3361

Ответ 1

Метод @Iorean будет работать, но у вас будет только один слот для хранения. Поэтому, если вы хотите использовать это в нескольких экземплярах и каждый экземпляр вычислить отличное значение, это не сработает.

К счастью, среда выполнения 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

Ответ 2

.h файл

@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); 
}

Подробнее см. fooobar.com/questions/81102/...

Ответ 3

Данный ответ отлично работает, и мое предложение - это просто расширение. Чтобы избежать повторного использования методов getter и setter для свойств категории, я ввел макросы, облегчающие их использование. Кроме того, эти макросы облегчают использование свойств примитивного типа, таких как int или BOOL.

Традиционный подход

Традиционно вы определяете свойство категории, например

@interface MyClass (Category)
@property (strong, nonatomic) NSString *text;
@end

Затем вам нужно реализовать метод getter и setter, используя связанный объект и селектор get в качестве ключа (см. оригинальный ответ):

@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

Макросы определяются следующим образом:

#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 как тип свойства. Это делается путем упаковки целочисленного значения в объект NSNumber. Поэтому его использование аналогично предыдущему примеру:

@interface ...
@property unsigned int value;
@end

@implementation ...
CATEGORY_PROPERTY_GET_SET_UINT(value, setValue:)
@end

Дополнительные макросы могут быть добавлены для поддержки signed int, BOOL и т.д.

Ограничения

  • По умолчанию все макросы используют OBJC_ASSOCIATION_RETAIN_NONATOMIC.

  • IDE, такие как код приложения, в настоящее время не распознают имя сеттера при рефакторинге имени свойства. Вам нужно будет переименовать его самостоятельно.

Ответ 4

Просто используйте libextobjc библиотека:

ч файл:

@interface MyClass (Variant)
@property (nonatomic, strong) NSString *test;
@end

м файл:

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

@synthesizeAssociation (MyClass, test);

@end

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

Ответ 5

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

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

Ответ 6

Другим возможным решением, возможно, проще, которое не использует 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)