2012-09-03 14 views
10

würde Ich mag eine Eigenschaft auf UITableView in einer Klasse-Erweiterung hinzuzufügen:eine Eigenschaft in iOS Klassenerweiterung definieren

@interface UITableViewController() 

@property NSString *entityString; 

@end 

Dann importiere ich die Verlängerung und dann verwende ich entityString Eigenschaft in einer Unterklasse von UITableViewController:

@implementation CustomerTableViewController 

- (void)viewDidLoad { 
    self.entityString = @"Customer"; 
    ... 
    [super viewDidLoad]; 
} 
... 

Apple documentation sagt:

the compiler will automatically synthesize the relevant accessor methods (...) inside the primary class implementation.

Aber wenn ich zu ex versuchen Ich bekomme diesen Fehler:

-[CustomerTableViewController setEntityString:]: unrecognized selector sent to instance 0x737b670

Was mache ich falsch? Vielleicht kann die Eigenschaft nicht auf Unterklassen zugreifen?

Antwort

5

A Klassenerweiterung verwendet wird, zusätzliche Schnittstelle zu deklarieren - Methoden und Eigenschaften - deren Umsetzung Vertrag innerhalb der Klasse der primären @implementaiton erfüllt werden.

Genau aus diesem Grund können Sie über eine Klassenerweiterung keinen Speicher hinzufügen - Sie fügen Ivars hinzu. Eine Klassenerweiterung ist eine Schnittstelle, nicht mehr und nicht weniger. @synthesize ist, was Speicher für Deklarationen erstellt, aber @synthesize eines @property kann nur in der @implementation der Klasse (ob explizit oder als ein Standardverhalten des Compilers) angezeigt werden.

Da Sie die Framework-Klasse nicht neu kompilieren können, können Sie keine Ivars hinzufügen.

@ prashat Antwort ist eine Möglichkeit, Speicher zu einer vorhandenen Klasse hinzuzufügen. Es ist jedoch im Allgemeinen unerwünscht, diesen Weg zu gehen; hanging night aus Rahmenklassen willy-nilly ist ein Zeichen für schlechtes Design und wird Ihre Anwendung im Laufe der Zeit deutlich schwieriger zu pflegen.

Viel besser, um Ihr Design erneut zu besuchen, zu verstehen, warum Sie derzeit an ein Objekt, das es nicht direkt enthalten kann, Status anfügen und diese Anforderung weg refaktorieren.

+0

Kann nicht Ivars zur Klassenerweiterung hinzugefügt werden oder hängt das vom verwendeten Compiler ab? – tiguero

+3

Sie können Ivars und Eigenschaften in Klassenerweiterungen deklarieren, aber der Speicher für solche wird nicht erstellt, es sei denn, die Erweiterung wird vom Compiler vor der Kompilierung der @implementation der Klasse erkannt. – bbum

+0

Danke für die Klärung - ich habe nicht bemerkt, dass alazaro tatsächlich eine Klassenerweiterung für eine Framework-Klasse verwendet hat – tiguero

4

Die docs Zustand:

Class extensions are like anonymous categories, except that the methods they declare must be implemented in the main @implementation block for the corresponding class.

Wenn Sie @property verwenden, es entspricht in etwa zu Accessormethoden erklärt. Das bedeutet, dass Sie nur so etwas tun können, wenn Sie auch der Autor des Hauptblocks @implementation der Klasse sind, was Sie mit UITableViewController nicht tun.

Ihre einzige Option ist hier Kategorien, die keine Instanzvariablen hinzufügen können.

The docs link, und notieren Sie die allerletzte Zeile dieser Seite:

The implementation of the setValue: method must appear within the main @implementation block for the class (you cannot implement it in a category). If this is not the case, the compiler emits a warning that it cannot find a method definition for setValue:.

+0

Ich denke, das ist nicht mein Fall, ich benutze kein ivar + Accessoren. Der Compiler beschwert sich jedoch nicht über meinen Code. Der Fehler liegt in der Laufzeit. – alazaro

+3

ein '@ Eigentum' ** ist ** ein ivar plus accessors. –

+0

Ja, ich weiß, aber der Compiler erledigt die Arbeit für Sie. Ich dachte, es könnten auch Accessoren in der Hauptklasse hinzugefügt werden, egal ob es mir gehört oder nicht. – alazaro

12

Versuchen Sie stattdessen eine Kategorie mit assoziativen Referenzen verwenden. Es ist viel sauberer und funktioniert bei allen Instanzen von UIButton.

UIButton+Property.h 

#import <Foundation/Foundation.h> 

@interface UIButton(Property) 

@property (nonatomic, retain) NSObject *property; 

@end 


UIButton+Property.m 

#import "UIButton+Property.h" 
#import <objc/runtime.h> 

@implementation UIButton(Property) 

static char UIB_PROPERTY_KEY; 

@dynamic property; 

-(void)setProperty:(NSObject *)property 
{ 
    objc_setAssociatedObject(self, &UIB_PROPERTY_KEY, property, OBJC_ASSOCIATION_RETAIN_NONATOMIC); 
} 

-(NSObject*)property 
{ 
    return (NSObject*)objc_getAssociatedObject(self, &UIB_PROPERTY_KEY); 
} 

@end 

// Beispiel für die Verwendung

#import "UIButton+Property.h" 


UIButton *button1 = [UIButton buttonWithType:UIButtonTypeRoundedRect]; 
button1.property = @"HELLO"; 
NSLog(@"Property %@", button1.property); 
button1.property = nil; 
NSLog(@"Property %@", button1.property); 
+0

Wie sind assoziative Referenzen sauberer als Klassenerweiterungen? – vikingosegundo

+4

Sie sind sauberer, weil sie funktionieren (die Klassenerweiterung nicht, weil die Erweiterung nicht vorhanden war, als die @ Implementierung für die Klasse kompiliert wurde), aber das macht sie nicht sauber. Das Hinzufügen von Status zu vorhandenen Framework-Klassen ist ein Zeichen für schlechtes Design und hohe Fragilität. Während Prashants Antwort funktioniert, ist das normalerweise ein Zeichen, dass Sie Ihre Architektur überdenken sollten. – bbum

+0

Es gibt keine Notwendigkeit, '@ dynamic' in diesem Code zu verwenden, BTW. – bbum