Objective-C Несколько инициализаторов - PullRequest
16 голосов
/ 08 января 2010

У меня простой вопрос о создании нескольких инициализаторов в классе Objective-C. В основном у меня есть класс, который представляет одну строку в моей базе данных (пользователи). В настоящее время у меня есть инициализатор, который инициализирует класс на основе идентификатора пользователя UserID (который также является первичным ключом в базе данных), при передаче идентификатора пользователя класс подключается к веб-службе, анализирует результаты и возвращает инициализированный объект в соответствующую строку в базе данных.

В этой базе данных есть несколько уникальных полей (имя пользователя и адрес электронной почты), я также хотел бы иметь возможность инициализировать мой объект на основе этих значений. Но я не уверен в том, как иметь более одного инициализатора, все, что я прочитал, говорит о том, что я могу иметь несколько инициализаторов, если каждый из них вызывает назначенный инициализатор. Если бы кто-то мог помочь мне с этим, это было бы здорово.

Мой код инициализатора выглядит следующим образом:

- (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;
}

Исходя из комментария Лорана, я попытался реализовать собственное решение, я был бы признателен, если бы вы сообщили мне о любых очевидных затруднениях с этим решением:

Я не совсем уверен, что понимаю, что вы имеете в виду, я пытался реализовать свое собственное решение. Я был бы признателен, если бы вы сообщили мне, что вы думаете:

- (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];
}

Привет

Ответы [ 2 ]

16 голосов
/ 08 января 2010

Определение назначенного инициализатора здесь . Настоятельно необходимо убедиться, что ваши экземпляры согласованы независимо от того, какой инициализатор вы используете. Для полной ссылки см. Руководство по программированию Objective-C : реализация инициализатора хорошо документирована.

Редактировать 2: Исправить опечатку, сообщенную @ NSResponder

Редактировать:

Я думаю, что вызов init после установки члена не является надежным. Члены могут иметь странные значения, которые не пройдут тест на инициализацию.

Лучший способ сделать это - сначала вызвать метод "init" (который установит значения по умолчанию для членов), а затем установить элемент. Таким образом, у вас будет одинаковая структура кода для всех ваших инициализаторов:

- (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;
}
7 голосов
/ 08 января 2010

Способ, которым я склонен делать подобные вещи, заключается в том, чтобы назначенный инициализатор был методом, который принимает большинство параметров, а более простые инициализаторы просто отправляют более подробные сообщения -init. Например:

   // 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;
  }
...