Ответ 1
Причиной аварии было то, что я использовал неправильный performSelector
.
NSObject
определяет несколько версий performSelector
. Тот, который я вызывал, был:
- (id)performSelector:(SEL)aSelector;
Однако метод, который я вызывал, принял параметр id
. Например:
- (void)someMethod:(id)sender;
Теперь ARC - хорошая безопасная система управления памятью, которая пытается обеспечить правильное сохранение параметров во время выполнения метода. Так что хотя мой someMethod:
был пуст, ARC создавал код, который выглядел так:
- (void)someMethod:(id)sender
{
objc_retain(sender);
objc_release(sender);
}
Проблема с этим, однако, заключалась в том, что я вызывал performSelector:
и не поставлял значение для параметра sender
. Таким образом, sender
указывал случайный мусор на стек. Поэтому при вызове objc_retain()
приложение разбилось.
Если я изменил:
MenuItem *item = [[MenuItem alloc] initWithTarget:widget
action:@selector(someMethod:)
object:nil];
к
MenuItem *item = [[MenuItem alloc] initWithTarget:widget
action:@selector(someMethod)
object:nil];
и
- (void)someMethod:(id)sender;
к
- (void)someMethod;
Затем крушение уходит.
Аналогично, я также могу изменить
[self.target performSelector:self.action];
to
[self.target performSelector:self.action withObject:nil];
если я хочу следовать "стандартной" форме методов целевого действия, которые принимают один параметр. Преимущество второй формы performSelector
заключается в том, что если я вызываю метод, который не принимает параметр, он все равно будет работать нормально.