web-dev-qa-db-de.com

Ziel-C: Eigenschafts- / Instanzvariable in Kategorie

Da ich in Objective-C keine synthetisierte Eigenschaft in einer Kategorie erstellen kann, kann ich den folgenden Code nicht optimieren:

@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

Die Test-Methode wird zur Laufzeit mehrmals aufgerufen und ich mache eine Menge Sachen, um das Ergebnis zu berechnen. Normalerweise speichere ich mithilfe einer synthetisierten Eigenschaft den Wert in einem IVar _test, wenn die Methode zum ersten Mal aufgerufen wird, und gebe diesen IVar beim nächsten Mal zurück. Wie kann ich den obigen Code optimieren?

117
dhrm

@ Loreans Methode wird funktionieren (Hinweis: Antwort ist jetzt gelöscht), aber Sie hätten nur einen einzigen Speicherplatz. Wenn Sie dies also für mehrere Instanzen verwenden möchten und für jede Instanz einen eigenen Wert berechnen möchten, funktioniert dies nicht.

Glücklicherweise hat die Objective-C-Laufzeit das Ding Associated Objects , das genau das kann, was Sie wollen:

#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
122
Dave DeLong

. h-Datei

@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

Genau wie eine normale Eigenschaft - zugänglich mit Punktnotation

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

Einfachere Syntax

Alternativ können Sie @selector(nameOfGetter) verwenden, anstatt einen statischen Zeigerschlüssel wie folgt zu erstellen:

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

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

Weitere Informationen finden Sie unter https://stackoverflow.com/a/16020927/202451

169
hfossli

Die gegebene Antwort funktioniert sehr gut und mein Vorschlag ist nur eine Erweiterung, die das Schreiben von zu viel Code vermeidet.

Um zu vermeiden, dass Get- und Setter-Methoden für Kategorieeigenschaften wiederholt geschrieben werden, werden in dieser Antwort Makros eingeführt. Darüber hinaus vereinfachen diese Makros die Verwendung primitiver Typeneigenschaften wie int oder BOOL.

Traditioneller Ansatz ohne Makros

Traditionell definieren Sie eine Kategorieeigenschaft wie

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

Dann müssen Sie eine Get- und Setter-Methode implementieren, indem Sie ein zugehöriges Objekt und den Get-Selektor als Schlüssel verwenden ( siehe ursprüngliche Antwort ):

#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

Mein vorgeschlagener Ansatz

Mit einem Makro schreiben Sie stattdessen:

@implementation MyClass (Category)

CATEGORY_PROPERTY_GET_SET(NSString*, text, setText:)

@end

Die Makros sind wie folgt definiert:

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

Das Makro CATEGORY_PROPERTY_GET_SET Fügt einen Getter und Setter für die angegebene Eigenschaft hinzu. Bei schreibgeschützten oder schreibgeschützten Eigenschaften wird das Makro CATEGORY_PROPERTY_GET Bzw. CATEGORY_PROPERTY_SET Verwendet.

Primitive Typen brauchen etwas mehr Aufmerksamkeit

Da primitive Typen keine Objekte sind, enthalten die obigen Makros ein Beispiel für die Verwendung von unsigned int Als Typ der Eigenschaft. Dazu wird der ganzzahlige Wert in ein NSNumber -Objekt eingeschlossen. Die Verwendung ist also analog zum vorherigen Beispiel:

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

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

Nach diesem Muster können Sie einfach weitere Makros hinzufügen, um auch signed int, BOOL usw. zu unterstützen.

Einschränkungen

  1. Alle Makros verwenden standardmäßig OBJC_ASSOCIATION_RETAIN_NONATOMIC.

  2. IDEs wie App Code erkennen derzeit den Namen des Setters nicht, wenn der Name der Eigenschaft überarbeitet wird. Sie müssten es selbst umbenennen.

31
Lars Blumberg

Verwenden Sie einfach libextobjc library:

h-Datei:

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

m-Datei:

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

@synthesizeAssociation (MyClass, test);

@end

Mehr über @synthesizeAssociation

7
Mansurov Ruslan

Nur mit iOS 9 getestet Beispiel: Hinzufügen einer UIView-Eigenschaft zu UINavigationBar (Kategorie)

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
Rikco