Objective-C: zmienna właściwości / instancji w kategorii

Ponieważ nie mogę utworzyć syntetyzowanej właściwości w kategorii w Objective-C, Nie wiem, jak zoptymalizować następujący kod:

@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

metoda testowa jest wywoływana wiele razy w trybie runtime i robię wiele rzeczy, aby obliczyć wynik. Zwykle używając syntetyzowanej właściwości przechowuję wartość w Ivar _test za pierwszym razem, gdy metoda jest wywoływana, i po prostu zwracam ten IVar następnym razem. Jak zoptymalizować powyższy kod?

Author: hfossli, 2012-01-04

6 answers

Metoda@lorean będzie działać (Uwaga: odpowiedź jest teraz usuwana), ale masz tylko jedno miejsce do przechowywania. Więc jeśli chcesz użyć tego na wielu instancjach i mieć każde wystąpienie obliczyć odrębną wartość, to nie będzie działać.

Na szczęście, Runtime Objective - C ma coś o nazwie powiązane obiekty , które mogą robić dokładnie to, co chcesz:

#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
 117
Author: Dave DeLong,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-12-28 07:30:40

.H-file

@interface NSObject (LaserUnicorn)

@property (nonatomic, strong) LaserUnicorn *laserUnicorn;

@end

.m-file

#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

Podobnie jak normalna właściwość-dostępna z notacją kropkową

NSObject *myObject = [NSObject new];
myObject.laserUnicorn = [LaserUnicorn new];
NSLog(@"Laser unicorn: %@", myObject.laserUnicorn);

Łatwiejsza składnia

Alternatywnie możesz użyć @selector(nameOfGetter) zamiast tworzyć statyczny klawisz wskaźnika w ten sposób:

- (LaserUnicorn *)laserUnicorn {
    return objc_getAssociatedObject(self, @selector(laserUnicorn));
}

- (void)setLaserUnicorn:(LaserUnicorn *)unicorn {
    objc_setAssociatedObject(self, @selector(laserUnicorn), unicorn, OBJC_ASSOCIATION_RETAIN_NONATOMIC); 
}

Po Więcej szczegółów zobacz https://stackoverflow.com/a/16020927/202451

 156
Author: hfossli,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-05-23 11:54:44

Podana odpowiedź działa świetnie i moja propozycja jest tylko jej rozszerzeniem, które pozwala uniknąć pisania zbyt dużej ilości kodu kotła.

Aby uniknąć wielokrotnego pisania metod getter i setter dla właściwości kategorii, ta odpowiedź wprowadza makra. Dodatkowo makra te ułatwiają korzystanie z prymitywnych właściwości typu, takich jak int lub BOOL.

Podejście tradycyjne bez makr

Tradycyjnie definiujesz właściwość kategorii jak

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

Wtedy musisz zaimplementować metodę getter i setter używając skojarzonego obiektu i selektora get jako klucza (Zobacz oryginalną odpowiedź):

#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

Moje sugerowane podejście

Teraz używając makra napiszesz zamiast tego:

@implementation MyClass (Category)

CATEGORY_PROPERTY_GET_SET(NSString*, text, setText:)

@end

Makra są zdefiniowane w następujący sposób:

#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)

Makro CATEGORY_PROPERTY_GET_SET dodaje getter i setter dla podanej właściwości. Właściwości tylko do odczytu lub tylko do zapisu będą używać makra CATEGORY_PROPERTY_GET i CATEGORY_PROPERTY_SET odpowiednio.

Prymitywne typy potrzebują trochę więcej uwagi

Jako że typy prymitywne nie są obiektami, powyższe makra zawierają przykład użycia unsigned int jako typu właściwości. Robi to, owijając wartość całkowitą w obiekt NSNumber. Tak więc jego użycie jest analogiczne do poprzedniego przykładu:

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

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

Podążając za tym wzorcem, możesz po prostu dodać więcej makr, aby również obsługiwać signed int, BOOL, itd...

Ograniczenia

  1. Wszystkie makra domyślnie używają OBJC_ASSOCIATION_RETAIN_NONATOMIC.

  2. IDE takie jak kod aplikacji obecnie nie rozpoznają nazwy settera podczas refaktoryzacji nazwy właściwości. Sam musisz zmienić nazwę.

 28
Author: Lars Blumberg,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2018-01-25 19:24:46

Po prostu użyj libextobjc biblioteka:

H-plik:

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

M-file:

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

@synthesizeAssociation (MyClass, test);

@end

Więcej o @ synthesizeAssociation

 7
Author: Mansurov Ruslan,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2015-01-05 14:57:09

Testowane tylko z iOS 9 Przykład: dodanie właściwości UIView do 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
 3
Author: Rikco,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2016-04-05 13:39:48

Innym możliwym rozwiązaniem, być może łatwiejszym, które nie używa {[1] } jest zadeklarowanie zmiennej w pliku implementacji kategorii w następujący sposób:

@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

Minusem tego typu implementacji jest to, że obiekt nie działa jako zmienna instancji, ale raczej jako zmienna klasy. Nie można również przypisać atrybutów właściwości (takich jak używane w Powiązanych obiektach, takich jak OBJC_ASSOCIATION_RETAIN_NONATOMIC)

 -2
Author: kernix,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2014-02-25 12:04:02