Метод Objective-C с переменными аргументами — пропущен первый аргумент?

#objective-c #foundation #variadic-functions

#objective-c #foundation #переменные функции

Вопрос:

Я слежу за программированием Стивена Кочана на Objective-C, 6-е издание, и мне нужна помощь с этим конкретным методом. В принципе, у меня есть класс, AddressBook , который имеет две переменные экземпляра: NSMutableArray вызываемый book и NSString вызываемый bookName . Вполне понятно.

book содержит AddressCard s, и я пытался реализовать метод, который принимает несколько переменных типа id и добавляет их все в массив book . Вот оно:

 -(void) addCards:(id)firstCard, ... NS_REQUIRES_NIL_TERMINATION
{
    va_list argumentList;
    va_start(argumentList, firstCard);

    id theArgument = firstCard;
    while ((theArgument = va_arg(argumentList, id)))
    {
        [self addCard:theArgument];
    }
    va_end(argumentList);
}
 

addCard Метод представляет собой настраиваемую реализацию NSMutableArray addObject метода. Проблема в том, что первый аргумент, который я предоставляю addCards , не добавляется book . Вот main.m и сопутствующий вывод:

 int main(int argc, const char * argv[])
{
    @autoreleasepool
    {
        AddressCard *card1 = [[AddressCard alloc] initWithName:@"Joseph Brown" andEmail:@"jbrown@yahoo.com"];
        AddressCard *card2 = [[AddressCard alloc] initWithName:@"Thomas Walter" andEmail:@"t.walter@gmail.com"];
        AddressCard *card3 = [[AddressCard alloc] initWithName:@"Jonathan Green" andEmail:@"jon_green@gmail.com"];
        AddressCard *card4 = [[AddressCard alloc] initWithName:@"Elizabeth White" andEmail:@"elizwhite@live.com"];

        AddressBook *myBook = [[AddressBook alloc] initWithName:@"My Address Book"];

        [myBook addCards:card1, card2, card3, card4, nil];

        NSLog(@"Lookup: Joseph Brown");

        if ([myBook lookup:@"joseph Brown"] == nil)
        {                                   
            NSLog(@"Not Found!");
        }

        else
        {                                   
            [[myBook lookup:@"Joseph Brown"] print];
        }
        [myBook list];
    }
    return 0;
}
 

Вывод:

 2014-06-25 11:43:25.997 AddressBook[19454:303] Lookup: Joseph Brown
2014-06-25 11:43:25.999 AddressBook[19454:303] Not Found!
2014-06-25 11:43:25.999 AddressBook[19454:303] ======== Contents of My Address Book ========
2014-06-25 11:43:26.000 AddressBook[19454:303] Thomas Walter           t.walter@gmail.com              
2014-06-25 11:43:26.000 AddressBook[19454:303] Jonathan Green          jon_green@gmail.com             
2014-06-25 11:43:26.000 AddressBook[19454:303] Elizabeth White         elizwhite@live.com              
2014-06-25 11:43:26.001 AddressBook[19454:303] =============================================
Program ended with exit code: 0
 

lookup делает именно это: ищет AddressCard указанное в аргументе для него; print печатает AddressCard в удобном формате; list также выполняет его тезку: перечисляет содержимое book . Теперь, как видно из выходных данных, Joseph Brown , хотя и добавляется в AddressBook as card1 , на самом деле не добавляется, что дважды подтверждается выводом. Что не так?

Комментарии:

1. Рассмотрите возможность использования массива вместо этого. Мне было очень сложно отлаживать код с переменными аргументами. Требуется три символа, чтобы обернуть ваш список карточек в массив.

2. Достаточно справедливо; Я сначала подумал об этом. Но разве элементы уже не входят в массив? Я решил не делать этого, потому что это было неэффективно; зачем инициализировать массив, чтобы поместить его элементы в другой массив? Если бы мне действительно нужно было, я мог бы использовать arrayWithArray: или arrayWithObjects: для этого, я полагаю. Но почему именно, помимо простоты отладки?

3. Более чистый код, НАМНОГО более чистый код. Кроме того, вам нужно всего три символа, чтобы обернуть ваши карты в массив, и вам не нужно беспокоиться о завершении nil. [myBook addCards:@[card1, card2, card3, card4]];

Ответ №1:

Альтернативное решение с использованием NSArray

 AddressCard *card1 = [[AddressCard alloc] initWithName:@"Joseph Brown" andEmail:@"jbrown@yahoo.com"];
AddressCard *card2 = [[AddressCard alloc] initWithName:@"Thomas Walter" andEmail:@"t.walter@gmail.com"];
AddressCard *card3 = [[AddressCard alloc] initWithName:@"Jonathan Green" andEmail:@"jon_green@gmail.com"];
AddressCard *card4 = [[AddressCard alloc] initWithName:@"Elizabeth White" andEmail:@"elizwhite@live.com"];

AddressBook *myBook = [[AddressBook alloc] initWithName:@"My Address Book"];
[myBook addCards:@[card1, card2, card3, card4]];
 

 -(void)addCards:(NSArray *)cards {
    for (AddressCard *card in cards) {
        [self addCard:card];
    }
}
 

Комментарии:

1. Спасибо за предложение. Я совершенно забыл о быстром перечислении и литералах Obj-C. Для еще большего сокращения кода, я полагаю, можно использовать arrayWithArray: ?

2. Я имел в виду, что вместо перечисления через массив карточек вы могли бы просто сделать [self.book arrayWithArray:cards]; .

3. Это не так, как работает arrayWithArray… Это статический метод, который возвращает копию массива. Вы не поделились структурой класса AddressBook, поэтому я не могу это прокомментировать.

4. Возможно, вы думали arrayByAddingObjectsFromArray: .

Ответ №2:

В вашем коде

 id theArgument = firstCard;
while ((theArgument = va_arg(argumentList, id)))
 

вы ничего не делали с. firstCard второй аргумент переопределяет его

 -(void) addCards:(id)firstCard, ... NS_REQUIRES_NIL_TERMINATION
{
    va_list argumentList;
    va_start(argumentList, firstCard);

    id theArgument = firstCard;
    while (theArgument)
    {
        [self addCard:theArgument];
        theArgument = va_arg(argumentList, id);
    }
    va_end(argumentList);
}
 

Комментарии:

1. Ах, да, я должен был это понять. Большое спасибо.