2010-01-08 20 views
15

J'ai une question simple sur la création de plusieurs initialisers dans une classe objective-c. Fondamentalement, j'ai une classe qui représente une seule ligne dans ma base de données (utilisateurs). J'ai actuellement un initialiseur qui initialise la classe en fonction de l'utilisateur UserID (qui est aussi la clé primaire dans la base de données), une fois l'ID utilisateur passé la classe les connectera à un webservice analyser les résultats et retourner un objet initialisé à la ligne correspondante dans la base de données.Initialiseurs multiples Objective-C

Dans cette base de données sont un certain nombre de champs uniques (nom d'utilisateur et adresse e-mail), je voudrais également être en mesure d'initialiser mon objet sur la base de ces valeurs. Mais je ne suis pas sûr de savoir comment avoir plus d'un initiateur, tout ce que j'ai lu indique que je suis libre d'avoir plusieurs initialiseurs, à condition que chacun appelle l'initialiseur désigné. Si quelqu'un pouvait m'aider avec ça, ce serait génial.

Mon code initialiseur est comme suit:

- (id) initWithUserID:(NSInteger) candidate { 
    self = [super init]; 
    if(self) { 
     // Load User Data Here 
     NSString *soapMessage = [NSString stringWithFormat: 
           @"<?xml version=\"1.0\" encoding=\"utf-8\"?>\n" 
           "<soap:Envelope xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\" xmlns:xsd=\"http://www.w3.org/2001/XMLSchema\" xmlns:soap=\"http://schemas.xmlsoap.org/soap/envelope/\">\n" 
           "<soap:Body>\n" 
           "<GetByUserID xmlns=\"http://tempuri.org/\">\n" 
           "<UserID>%d</UserID>\n" 
           "</GetByUserID>\n" 
           "</soap:Body>\n" 
           "</soap:Envelope>\n", candidate 
           ]; 
     NSLog(@"%@",soapMessage); 

     // Build Our Request 
     NSURL *url = [NSURL URLWithString:@"http://photoswapper.mick-walker.co.uk/UsersService.asmx"]; 
     NSMutableURLRequest *theRequest = [NSMutableURLRequest requestWithURL:url]; 
     NSString *msgLength = [NSString stringWithFormat:@"%d", [soapMessage length]]; 

     [theRequest addValue: @"text/xml; charset=utf-8" forHTTPHeaderField:@"Content-Type"]; 
     [theRequest addValue: @"http://tempuri.org/GetByUserID" forHTTPHeaderField:@"SOAPAction"]; 
     [theRequest addValue: msgLength forHTTPHeaderField:@"Content-Length"]; 
     [theRequest setHTTPMethod:@"POST"]; 
     [theRequest setHTTPBody: [soapMessage dataUsingEncoding:NSUTF8StringEncoding]]; 

     NSError *WSerror; 
     NSURLResponse *WSresponse; 

     webData = [NSURLConnection sendSynchronousRequest:theRequest returningResponse:&WSresponse error:&WSerror]; 

     xmlParser = [[NSXMLParser alloc] initWithData: webData]; 
     [xmlParser setDelegate: self]; 
     [xmlParser setShouldResolveExternalEntities: YES]; 
     [xmlParser parse]; 
    } 
    return self; 
} 

Suite du commentaire de Laurent, j'ai essayé de mettre en œuvre ma propre solution, je vous serais reconnaissant si vous pouviez me faire part de tout est évident gotcha avec cette solution:

Je ne suis pas totalement sûr que je comprends que vous voulez dire, j'ai essayé de mettre en œuvre ma propre solution. Je vous serais reconnaissant si vous pouviez me faire savoir ce que vous pensez:

- (id) init { 
    self = [super init]; 
    if(self){ 
     // For simplicity I am going to assume that the 3 possible 
     // initialation vectors are mutually exclusive. 
     // i.e if userName is used, then userID and emailAddress 
     // will always be nil 
     if(self.userName != nil){ 
      // Initialise object based on username 
     } 
     if(self.emailAddress != nil){ 
      // Initialise object based on emailAddress 
     } 
     if(self.userID != 0){ // UserID is an NSInteger Type 
      // Initialise object based on userID 
     } 
    } 
    return self; 
} 
- (id) initWithUserID:(NSInteger) candidate { 
    self.userID = candidate; 
    return [self init]; 
} 
- (id) initWithEmailAddress:(NSString *) candidate { 
    self.emailAddress = candidate; 
    return [self init]; 
} 
- (id) initWithUserName:(NSString *) candidate { 
    self.userName = candidate; 
    return [self init]; 
} 

Cordialement

+2

AVERTISSEMENT - échec d'application probable: vous devez vous initialiser en utilisant l'initialiseur désigné AVANT de définir self.userID ou similaire. Ceci parce que 1) la mémoire d'objet peut être réaffectée à un nouvel emplacement par l'appel de [super init] dans [self init], perdant votre variable initialisée et 2) userID peut être écrasé par l'appel de [self init]. Faites ceci: if (self = [self init]) {self.userID = candidat} renvoient le self; –

Répondre

16

définition de l'initialiseur désigné est here. Il est impératif de s'assurer que vos instances sont cohérentes quel que soit l'initialiseur utilisé. Pour une référence complète, voir le Objective-C Programming Guide: l'implémentation de l'initialiseur est bien documentée.

Edit 2: typo Fix rapporté par @NSResponder

Edit:

Je pense que l'appel d'initialisation après avoir réglé le membre n'est pas fiable. Les membres peuvent avoir des valeurs bizarres qui échoueront au test d'initialisation.

Une meilleure façon de le faire est d'appeler d'abord la méthode "init" (qui va définir les valeurs par défaut pour les membres), puis de définir le membre. De cette façon, vous avez la même structure de code pour tous vos initializers:

- (id) init { 
    self = [super init]; 
    if(self){ 
     self.userID = 0; 
     self.userName = nil; 
     self.emailAddress = nil; 
    } 
    return self; 
} 

- (id) initWithUserID:(NSInteger) candidate { 
    self = [self init]; 
    if(self){ 
     self.userID = candidate; 
    } 
    return self; 
} 
+0

J'ai édité mon article original, pourriez-vous me dire ce que vous pensez de cette solution. Merci beaucoup Mick –

+0

Voir mes modifications pour une autre proposition. –

+0

Puis-je poser une question?Quel est l'initialiseur désigné dans la classe? Si la classe est sous-classée, init ne peut pas être l'initialiseur désigné qui doit être appelé par l'initialiseur de la sous-classe, car à ce moment-là, l'identifiant utilisateur, l'adresse email et la valeur userName ne sont pas encore affectés. – yehnan

7

La façon dont je tends à faire ce genre de chose est l'initialiseur désigné pour être la méthode qui prend la plupart des paramètres, et les initialiseurs plus simples envoyez simplement les messages -init plus détaillés. Par exemple:

// simple initializer 
- (id) init 
    { 
    return [self initWithWidth: 1.0]; 
    } 

    // not so simple initializer 
- (id) initWithWidth:(float) aWidth 
    { 
    return [self initWithWidth:aWidth andColor:nil]; 
    } 

    // designated initializer. This is the one that subclasses should override. 
- (id) initWithWidth: (float) aWidth andColor: (NSColor *) aColor 
    { 
    if (self = [super init]) 
    { 
    self.width = aWidth; 
    self.color = aColor ? aColor : [[self class] defaultColor]; 
    } 
    return self; 
    }