2013-09-05 2 views
-1

Мне было интересно пару дней, если NSInvocation понадобится NSMethodSignature. Допустим, мы хотим, чтобы написать свой собственный NSInvocation, мои требования будут таким образом:NSInvocation, требующий NSMethodSignature

  1. мне нужен селектор SEL
  2. целевой объект для вызова селектора на
  3. массиве аргументов

Затем я достал IMP из цели и SEL и передал argument в качестве параметров.

Итак, мой вопрос: зачем нам нужен NSMethodSignature для создания и использования NSInvocation?

Примечание: Я знаю, что, имея только SEL и цель, у нас нет аргументов и возвращаемого типа для этого метода, но почему мы заботимся о типах аргументов и возвратах?

ответ

3

Каждый тип C имеет разный размер. (Даже один и тот же тип может иметь разные размеры для разных систем, но на этот раз мы будем игнорировать.) int может иметь 32 или 64 бит, в зависимости от системы. double занимает 64 бит. char представляет 8 бит (но может быть передан как обычный int в зависимости от соглашения о передаче системы). И, наконец, и самое главное, типы struct имеют различные размеры, в зависимости от количества элементов в нем и каждого из их размеров; нет никаких ограничений на то, насколько это может быть. Таким образом, невозможно передавать аргументы одинаково независимо от типа. Поэтому, как вызывающая функция упорядочивает аргументы и как вызываемая функция интерпретирует ее аргументы, должна зависеть от подписи функции. (У вас не может быть массив аргументов типа «агностик», каков будет размер элементов массива?) Когда компиляция нормальных функций компилируется, компилятор знает подпись во время компиляции и может организовать ее правильно в соответствии с соглашением о вызовах. Но NSInvocation предназначен для управления вызовом во время выполнения. Поэтому для работы требуется представление сигнатуры метода.

Есть несколько вещей, которые могут сделать NSInvocation.Каждая из этих вещей требует знания числа и типов (по крайней мере, размеры типов) параметров:

  1. Когда сообщение посылается объекту, который не имеет метод для этого, среда выполнения конструкции объект NSInvocation и передает его в -forwardInvocation:. Объект NSInvocation содержит копию всех аргументов, которые передаются, поскольку их можно сохранить и вызвать позже. Следовательно, среда выполнения должна знать, по крайней мере, насколько велики общие параметры, чтобы скопировать нужный объем данных из регистров и/или стека (в зависимости от того, как аргументы упорядочены в вызывающем соглашении) в NSInvocation объект.
  2. Если у вас есть объект NSInvocation, вы можете запросить значение аргумента i'th, используя -getArgument:atIndex:. Вы также можете установить/изменить значение для i-го аргумента, используя -setArgument:atIndex:. Для этого требуется знать: 1) где в буфере данных запускается i-й параметр; это требует знания того, насколько велики предыдущие параметры, и 2) насколько величен i-й параметр, чтобы он мог скопировать нужный объем данных (если он слишком мало копирует, он будет иметь коррумпированное значение, если он тоже копирует много, скажем, когда вы делаете getArgument, он может перезаписать буфер, который вы ему дали, или когда вы делаете setArgument, перезапишите другие аргументы).
  3. У вас может быть это -retainArguments, что заставляет его сохранять все аргументы типа указателя объекта. Для этого требуется различать типы указателей объектов и другие типы, поэтому информация типа должна содержать не только размер.
  4. Вы можете вызвать NSInvocation, что заставляет его строить и выполнять вызов метода. Это требует, чтобы он знал, по крайней мере, сколько данных нужно копировать из своего буфера в регистры/стек, чтобы поместить все данные, в которых функция будет ожидать. Это требует знания, по крайней мере, комбинированного размера всех параметров и, вероятно, также необходимо знать размеры отдельных параметров, чтобы можно было правильно определить разницу между параметрами на регистрах и параметрами в стеке.
  5. Вы можете получить возвращаемое значение вызова, используя -getReturnValue:; это имеет сходные проблемы с получением аргументов выше.

    • Не упомянутая выше вещь заключается в том, что возвращаемый тип также может иметь большое влияние на вызывающий механизм. В x86 и ARM общие архитектуры для Objective-C, когда тип возврата - тип struct, соглашение о вызове очень отличается - эффективно добавляется дополнительный (первый) параметр перед всеми регулярными параметрами, который является указателем на пространство, в котором должен быть записан результат структуры. Это вместо обычного соглашения о вызове, в котором результат возвращается в регистр. (В PowerPC я считаю, что возвращаемый тип double также обрабатывается специально.) Поэтому знание типа возврата по существу предназначено для построения и вызова NSInvocation.
+0

Итак, вы говорите, что внутри NSInvocation не использует objc_msgSend, он напрямую использует вызовы fptr и копирует args в регистры и т. Д. –

+0

Но почему мы передаем ему 'SEL', цель и' NSMethodSignature ', когда мы знаем, что мы могли бы получить подпись, просто выполнив' [target methodSignatureForSelector: SEL] ', мы могли бы просто передать SEL и target и все равно получить метод sig во время выполнения. что мне здесь не хватает? –

+0

@OmarAbdelhafith: Нет, он использует 'objc_msgSend()'.Но для правильного использования 'objc_msgSend()', вы также должны знать подпись - вы должны притворяться, что 'objc_msgSend' имеет тип указателя функции вызываемой функции. Потому что 'objc_msgSend()' просто батут, который заменяет собой вызванную функцию, оставляя все аргументы в регистрах и стеке неповрежденными. – newacct

1

NSMethodSignature необходим для того, чтобы механизм отправки и отправки сообщений работал правильно для вызовов. NSMethodSignature и NSInvocation были построены как обертка вокруг __builtin_call(), которая является как зависимой от архитектуры, так и крайне консервативной в отношении пространства стека, которое требует данная функция. Следовательно, когда вызываются вызовы, __builtin_call() получает всю необходимую информацию из подписи метода и может изящно потерпеть неудачу, бросая вызов на механизм пересылки, зная, что он тоже получает правильную информацию о том, как стек должен искать re -invocation.

Это означает, что вы не можете сделать примитивное NSInvocation без подписи метода без изменения языка C, чтобы поддерживать преобразование массивов в VARARGS, видя как objc_msgSend(), и его кузены этого не позволят. Даже если вы можете обойти это, вам нужно будет рассчитать размер аргументов и тип возвращаемого значения (не слишком сложно, но если вы ошибаетесь, вы ошибаетесь в течение большого времени) и управляете правильным вызовом __builtin_call() , что потребует глубокого знания архитектуры отправки сообщений или ffi (что, вероятно, в любом случае падает до __builtin_call()).

+0

Но почему мы передаем ему SEL, цель и NSMethodSignature, когда мы знаем, что мы могли бы получить подпись только выполнив [целевой methodSignatureForSelector: SEL], мы могли бы просто передать SEL и цели и все равно получить метод sig во время выполнения. что мне здесь не хватает? –