-[NSObject performSelector:withObject:]
предполагается использовать только с методом, который принимает ровно один параметр указателя объекта и возвращает указатель объекта.Ваш метод принимает параметр BOOL
, а не параметр указателя объекта, поэтому его нельзя использовать с -[NSObject performSelector:withObject:]
.
Если вы всегда собираетесь отправлять сообщение handler:
и знаете, что метод имеетпараметр BOOL, вы должны просто вызвать его напрямую:
[self handler:YES];
Если имя метода будет определяться динамически во время выполнения, но вы знаете, что сигнатура метода всегда будет одинаковой (в этом случаеровно один параметр типа BOOL, ничего не возвращающий), вы можете вызвать его с помощью objc_msgSend()
.Вы должны привести objc_msgSend
к соответствующему типу функции для базовой реализующей функции метода перед вызовом (помните, что реализующая функция для методов Objective C имеет первые два параметра: self
и _cmd
, за которыми следуетзаявленные параметры).Это связано с тем, что objc_msgSend
является батутом, который вызывает соответствующую реализующую функцию со всеми регистрами и стеком, используемыми для хранения неповрежденных аргументов, поэтому вы должны вызывать его с соглашением о вызовах для реализующей функции.В вашем случае вы бы сделали:
SEL selector = @selector(handler:); // assume this is computed dynamically at runtime
((void (*)(id, SEL, BOOL))objc_msgSend)(self, selector, YES);
Кстати, если вы посмотрите на исходный код для -[NSObject performSelector:withObject:]
, вы увидите, что они делают то же самое -они знают, что сигнатура метода должна быть одним параметром типа id
и типом возврата id
, поэтому они приводят objc_msgSend
к id (*)(id, SEL, id)
и затем вызывают его.
В редких случаяхВ случае, когда сигнатура метода также будет динамически изменяться и неизвестна во время компиляции, вам придется использовать NSInvocation
.
Давайте рассмотрим, что произошло в вашем случае, когда вы использовали -[NSObject performSelector:withObject:]
сметод неправильной подписи.Внутри они вызывают objc_msgSend()
, что эквивалентно вызову базовой реализующей функции с указателем функции типа id (*)(id, SEL, id)
.Однако реализующая функция вашего метода на самом деле имеет тип void (id, SEL, BOOL)
.Таким образом, вы вызываете функцию, используя указатель на функцию другого типа.Согласно стандарту C (стандарт C99, раздел 6.5.2.2, параграф 9):
Если функция определена с типом, который не совместим с типом (выражения), на который указываетвыражение, которое обозначает вызываемую функцию, поведение не определено.
Итак, в основном вы видите неопределенное поведение .Неопределенное поведение означает, что все может случиться.Как вы видите, он может вернуть одну вещь в одной системе, а другую - в другой, или может привести к сбою всей программы.Вы не можете полагаться на какое-либо конкретное поведение.