селектор и PerformSelector

Имам UITableView с няколко секции. Всеки раздел съдържа различен набор от данни: телефонни номера, адреси....

За всеки от тези комплекти имам модел: телефонен номер, адрес. Те са напълно различни, но имат някои общи методи.

В моя UITableView имам масив, съдържащ тези модели/имена на класове:

NSMutableArray *classNames;

В viewDidLoad на моя UITableView правя някои инициализации за всички тези секции:

//section 1: PhoneNumbers
phoneNumbers = [PhoneNumbers getAllIDs];
if (phoneNumbers && (phoneNumbers.count >0)) {
    [classNames addObject:@"PhoneNumber"];
    [dataIDs addObject:phoneNumbers];
}

Правя това отново за всички останали раздели/модели:

 //section 2: Addresses
    addresses = [Address getAllIDs];
    if (addresses && (addresses.count >0)) {
        [classNames addObject:@"Address"];
        [dataIDs addObject:addresses];
    }
    // section 3: .....

Добре дотук за инициализация. Това изглежда добре и работи добре.

След това по-късно в моя cellForRowAtIndexPath извличам действителните данни чрез тези идентификатори

NSInteger section = [indexPath section];                    
NSInteger row = [indexPath row];

NSArray *rows = [dataIDs objectAtIndex:section];        
NSNumber *recordID = [rows objectAtIndex:row]; 

След това разбирам в кой клас трябва да извлечем действителните данни:

Class displayedDataClass = NSClassFromString ([classNames objectAtIndex:section]);

и вземете данните за попълване на клетката.

id displayedRecord = [[displayedDataClass alloc] init];      
[displayedRecord getByID:recordID]; 

След това мога да задам етикетите в клетката си, като използвам:

[cell.someLabel setText:[displayRecord fullDesciption]];

Дотук добре, успешно абстрахирах всичко, cellForRowAtIndexPathне трябва да знае откъде идват нещата, стига тези класове да отговарят на методите за извличане на данните за етикетите (в случая по-горе fullDesciption)

Сега имам нужда от actionButton във всяка клетка, изпълняваща някакво действие. За да съм сигурен, че разбирам концепцията за селектори и performSelection, просто направих бързо и мръсно в действие в моя клас TableView:

- (void) buttonTarget {
    NSLog (@"yes");
}

И в моя cellForRowAtIndexPath метод създадох бутон със следната цел:

button addTarget:self action:@selector(buttonTarget) forControlEvents:UIControlEventTouchUpInside];

Добре, засега добре, нещата работят според очакванията. Но това не е това, което наистина исках. Действието не трябва да се извършва тук, а в действителния клас (PhoneNumber,Address,...).

За да поддържам нещата чисти, направих модел на действие, съдържащ иконата за бутона, описание и селектор:

@interface Action : NSObject

@property (nonatomic, strong) NSString *description;
@property (nonatomic, strong) UIImage *icon;
@property (nonatomic ) SEL selector;

@end

В моя клас PhoneNumber (и подобни класове) действието е зададено на правилния селектор:

Action  *phoneAction = [[Action alloc] init];

phoneAction.description = NSLocalizedString(@"Call", @"Call button description");
phoneAction.icon = [UIImage imageNamed:@"phone"];
phoneAction.selector = @selector(callPhone);

Разбира се callPhone е имплементиран в класа PhoneNumber.

След това в моя TableView получавам действията за тази клетка

action = [displayedRecord action];

След това се опитвам да използвам този селектор в моя бутон:

[button addTarget:displayedRecord action:[action selector] forControlEvents:UIControlEventTouchUpInside];

Но тук нещата се объркват: никога не достигаме до този метод и получавам следната грешка:

[UIDeviceWhiteColor callPhone]: неразпознат селектор, изпратен до екземпляр 0x874af90 2013-12-29 23:23:03.629 thinx[27242:907] * Прекратяване на приложението поради неуловено изключение „NSInvalidArgumentException“, причина: „-[ UIDeviceWhiteColor callPhone]: неразпознат селектор, изпратен до екземпляр 0x874af90'


person Glenn    schedule 29.12.2013    source източник


Отговори (2)


Звучи сякаш имаш зомби. Когато получите действие, изпратено към обект, което няма смисъл, това обикновено означава, че вашият обект се освобождава, преди да можете да изпратите съобщение до него.

Във вашия случай добавяте "displayedRecord" като цел за вашия бутон.

За да работи това, трябва да поддържате силна препратка към извикването displayedRecord за целия живот на вашия обект бутон. Какво притежава вашия обект displayedRecord?

Ако не можете да дебъгвате това, като гледате кода си, можете да използвате инструмента за зомбита, за да се опитате да го разберете.

person Duncan C    schedule 29.12.2013
comment
Това вероятно е отговор в правилната посока!! displayedRecord е дефиниран в cellForRowAtIndexPath (както е написано в моя въпрос). Но тъй като това е локална променлива, вероятно не е достатъчно „силна“. Как да постигна това? - person Glenn; 30.12.2013
comment
Има ли само един обект displayedRecord или създавате по един за всяка клетка? Ако има само един, направете го свойство на вашия изглед контролер. Ако има различен за всяка клетка, създайте персонализиран подклас на UITableViewCell със свойство (от тип id), което да съдържа обекта displayedDataClass за тази клетка. - person Duncan C; 30.12.2013
comment
Благодаря много Дънкан. Това наистина беше РЕШЕНИЕТО. Знам, че има хубава реализация, където функционалността е абстрахирана и поставена където й е мястото! Вече имах персонализиран клас за моя UITableViewCell, така че добавянето на това свойство беше лесно (в началото не работеше, но забравих да направя променливата на екземпляра в моя персонализиран UITableViewCell силна!) - person Glenn; 31.12.2013

Във вашия неразпознат селектор грешка сте изпратили съобщението до обект, наречен UIDeviceWhiteColor. Този клас има ли метод, наречен callPhone? Струва ми се, че displayedRecord не сочи към обекта, който смятате, че е.

person Cameron Lowell Palmer    schedule 29.12.2013
comment
Нямам клас UIDeviceWhiteColor в моя код. Моят клас PhoneNumber има клас callPhone. - person Glenn; 30.12.2013
comment
Проследяването на стека ви казва, че обектът, който се извиква, UIDeviceWhiteColor - частен клас на Apple SDK, получава вашето съобщение. Което означава, че вашата променлива displayedRecord сочи към това по някаква причина. Предполагам, че някъде случайно го присвоявате. Задайте някои точки на прекъсване и проверете типа му клас - person Cameron Lowell Palmer; 30.12.2013
comment
Както посочи друг джентълмен, може да имате зомби. Може да е полезно да влезете в настройките за отстраняване на грешки и да активирате зомбита. - person Cameron Lowell Palmer; 30.12.2013