6 Stimmen

Verstecke Instanzvariable aus der Kopfzeile in Objective C

Ich bin auf eine Bibliothek gestoßen, die in Objective C geschrieben ist (ich habe nur die Header-Datei und die .a-Binärdatei). In der Header-Datei sieht es so aus:

@interface MyClass : MySuperClass 
{ 
    //hier steht nichts
}

@property (nonatomic, retain) MyObject anObject;
- (void)someMethod;

Wie kann ich dasselbe erreichen? Wenn ich versuche, eine Eigenschaft ohne die entsprechende Instanzvariable innerhalb der {} des Interfaces zu deklarieren, wird mir der Compiler einen Fehler melden. Letztendlich möchte ich die interne Struktur meiner Klasse in der .a verbergen und nur die notwendigen Methoden in der Headerdatei freigeben. Wie deklariere ich Instanzvariablen in der .m? Kategorien erlauben es mir nicht, Instanzvariablen hinzuzufügen, nur Methoden.

14voto

bbum Punkte 161596

Für 64-Bit-Anwendungen und iPhone-Anwendungen (jedoch nicht im Simulator) kann die Eigenschaftssynthese auch den Speicher für eine Instanzvariable synthetisieren.

D.h. das funktioniert:

@interface MyClass : MySuperClass 
{ 
    // hier ist nichts
}

@property (nonatomic, retain) MyObject *anObject;
@end

@implementation MyClass
@synthesize anObject;
@end

Wenn Sie für 32-Bit Mac OS X oder den iPhone Simulator kompilieren, wird der Compiler einen Fehler ausgeben.

13voto

Alvaro Polo Punkte 131

Sie können das gleiche Idiom verwenden, das in Cocoa-Klassen verwendet wird. Wenn Sie sich die NSString-Klassen-Schnittstelle in NSString.h ansehen, werden Sie feststellen, dass keine Instanzvariable deklariert ist. Wenn Sie tiefer in den GNUstep-Quellcode eintauchen, werden Sie den Trick finden.

Betrachten Sie den folgenden Code.

MyClass.h

@interface MyClass : NSObject

// Ihre Methoden hier
- (void) doSomething;

@end

MyClass.m

@interface MyClassImpl : MyClass {
   // Ihre privaten und versteckten Instanzvariablen hier
}
@end

@implementation MyClass

+ (id) allocWithZone:(NSZone *)zone
{
   return NSAllocateObject([MyClassImpl class], 0, zone);
}

// Ihre Methoden hier
- (void) doSomething {
  // Diese Methode gilt als rein virtuell und kann nicht aufgerufen werden
  [self doesNotRecognizeSelector: _cmd];          
}

@end

@implementation MyClassImpl

// Ihre Methoden hier
- (void) doSomething {
  // Eine tatsächliche Implementierung von doSomething
}

@end

Wie Sie sehen können, besteht der Trick darin, allocWithZone: in Ihrer Klasse zu überladen. Dieser Code wird standardmäßig von alloc bereitgestellt von NSObject aufgerufen, sodass Sie sich keine Gedanken darüber machen müssen, welche Zuweisungsmethode verwendet werden sollte (beide sind gültig). In einem solchen allocWithZone: können Sie die Foundation-Funktion NSAllocateObject() verwenden, um Speicher zu zuweisen und isa für ein MyClassImpl-Objekt anstelle von MyClass zu initialisieren. Danach handelt es sich für den Benutzer transparent um ein MyClassImpl-Objekt.

Natürlich muss die tatsächliche Implementierung Ihrer Klasse von MyClassImpl bereitgestellt werden. Die Methoden für MyClass müssen so implementiert werden, dass das Empfangen einer Nachricht als Fehler betrachtet wird.

7voto

user757885 Punkte 71

Sie können eine Klassenerweiterung verwenden. Eine Klassenerweiterung ist ähnlich wie eine Kategorie, aber ohne Namen. In der Apple-Dokumentation werden nur private Methoden definiert, aber tatsächlich können Sie auch Ihre internen Variablen deklarieren.

MyClass.h

@class PublicClass;

// Öffentliche Schnittstelle 
@interface MyClass : NSObject

@property (nonatomic, retain) PublicClass *publicVar;
@property (nonatomic, retain) PublicClass *publicVarDiffInternal;

- (void)publicMethod;

@end

MyClass.m

#import "PublicClass.h"
#import "InternalClass.h"

// Private Schnittstelle
@interface MyClass ( /* Klassenerweiterung */ ) 
{
@private
    // Interne Variable nur intern verwendet
    NSInteger defaultSize;

    // Interne Variable nur intern als private Eigenschaft verwendet
    InternalClass *internalVar;  

@private 
    // Interne Variable als öffentliche Eigenschaft freigegeben 
    PublicClass *publicVar;

    // Interne Variable als öffentliche Eigenschaft mit einem anderen Namen freigegeben
    PublicClass *myFooVar;
}

@property (nonatomic, retain) InternalClass *internalVar;

- (void)privateMethod;

@end

// Vollständige Implementierung von MyClass
@implementation MyClass

@synthesize internalVar;
@synthesize publicVar;
@synthesize publicVarDiffInternal = myFooVar

- (void)privateMethod 
{
}

- (void)publicMethod 
{
}

- (id)init
{
   if ((self = [super init]))
     {
       defaultSize = 512;
       self.internalVar = nil;
       self.publicVar = nil;
       self.publicVarDiffInternal = nil; //myFooVar initialisieren
     }

   return self;
}
@end

Sie können MyClass.h jedem geben, der nur Ihre öffentliche API und öffentlichen Eigenschaften benötigt. In MyClass.m deklarieren Sie Ihre Member-Variablen als privat und öffentlich sowie Ihre privaten Methoden in Ihrer Klassenerweiterung.

Auf diese Weise ist es einfach, öffentliche Schnittstellen freizulegen und Implementierungsdetails zu verbergen. Ich habe es in meinem Projekt ohne Probleme verwendet.

2voto

Graham Asher Punkte 1476

Entsprechend der Dokumentation, die ich mir angesehen habe, gibt es kein Problem. Alles, was Sie tun müssen, um Instanzvariablen zu verstecken, ist, sie am Anfang des @implementation-Abschnitts zu deklarieren, innerhalb von {...}. Allerdings bin ich relativ neu in Objective C und es besteht die Möglichkeit, dass ich etwas missverstanden habe - ich vermute, dass sich die Sprache geändert hat. Ich habe dieses System tatsächlich ausprobiert, mit XCode 4.2, Code für das iPad erstellt, und es scheint gut zu funktionieren.

Eine meiner Quellen für diese Idee ist die Apple-Entwicklerdokumentation unter http://developer.apple.com/library/ios/#documentation/cocoa/conceptual/objectivec/Chapters/ocDefiningClasses.html, die dieses Muster enthält:

@implementation Klassenname

{

// Deklaration von Instanzvariablen.

}

// Methodendefinitionen.

@end

1voto

kennytm Punkte 488916

Nein, Sie können das nicht. Aber Sie können dies tun, wenn Sie @property nicht verwenden:

.h

@interface X : Y {
  struct X_Impl* impl;
}
-(int)getValue;
@end

.m

struct X_Impl {
  int value;
};
...
@implementation X
-(void)getValue {
  return impl->value * impl->value;
}
@end

CodeJaeger.com

CodeJaeger ist eine Gemeinschaft für Programmierer, die täglich Hilfe erhalten..
Wir haben viele Inhalte, und Sie können auch Ihre eigenen Fragen stellen oder die Fragen anderer Leute lösen.

Powered by:

X