ScriptingBridge - как это работает «за кадром» - PullRequest
1 голос
/ 25 мая 2020

Контекст: я работаю над Pharo / Smalltalk -> Objective- C bridge

Сценарий: В следующем фрагменте Objective- C ScriptingBridge:

iTunesApplication *iTunes = [SBApplication applicationWithBundleIdentifier:@"com.apple.iTunes"];

iTunesTrack *currentTrack = iTunes.currentTrack; //[1]
// This low level way works too
//iTunesTrack *currentTrack = [iTunes propertyWithCode: 'pTrk']; //[2]

[iTunes playpause]; //[3]

Проблема: мост использует class_getInstanceMethod, чтобы определить, понимает ли объект сообщение / селектор, но возвращает NULL для сообщений сценария, таких как playpause

Вопрос №1 Почему class_getInstanceMethod возвращает NULL для сообщений сценария вроде playpause? Тот же вопрос для class_copyMethodList? Что особенного в сообщениях сценариев, что они не действуют как другие сообщения Obj- C (кроме случаев, когда они это делают!)?

Вопрос № 2 [РЕШЕНО - см. Ответ @ Matt]

Где, согласно документам , в «динамически определяемом подклассе для приложения iTunes» SB помещает «специфичные для приложения c методы, которые автоматически обрабатывают отправку событий Apple. "? И, учитывая, что class_getInstanceMethod не может найти такое поведение (см. Ниже), какой надежный способ для моста проверить это (т.е. существует ли такой метод / сообщение)?

The Objective- C Runtime API сообщает о смешанных результатах. С одной стороны, у класса iTunesApplication, похоже, нет никаких методов (или свойств в этом отношении):

  • class_copyMethodList([iTunes class]... возвращает нулевые методы
  • class_getInstanceMethod, которые мост, используемый для поиска и выполнения методов, терпит неудачу.

С другой стороны, #playpause можно запросить и отправить через другие части API:

  • respondsToSelector: -> ИСТИНА
  • methodSignatureForSelector: возвращает подпись
  • и performSelector: фактически отправляет сообщение

Как ни странно, methodForSelector:@"playpause" успешно возвращает IMP в Obj- C, но вылетает при отправке с другой стороны моста.

Вопрос № 3 [решено]

Как можно имитировать / реплицировать [3 ]?

@Willeke ответил в комментариях: [iTunes sendEvent:'hook' id:'PlPs' parameters:0]

1 Ответ

3 голосов
/ 03 июня 2020

Если SB не использует сообщения Objective- C, что означают документы, когда «подклассы SBApplication реализуют специфичные для приложения c методы, которые автоматически обрабатывают отправку событий Apple»? Почему iTunes RespondsToSelector: @ "playpause" работает, т.е. возвращает true? А как работает [iTunes playpause]? Et c, et c ..

Это работает, потому что самая первая вещь, которую вы делаете в приложении-мосте сценариев, генерирует заголовок. В Catalina это делается следующим образом:

sdp -f h --basename iTunes /System/Applications/Music.app/Contents/Resources/com.apple.Music.sdef

Это читает словарь iTunes (sdef) и генерирует заголовок для группы аналогичных классов Objective- C. Теперь у вас есть файл iTunes.h , который вы включаете в проект приложения и импортируете в свой код. Он содержит следующую строку:

- (void) playpause;  // toggle the playing/paused state of the current track

Итак, теперь playpause явно объявлен как допустимая команда, которую вы можете отправить объекту iTunesApplication. Затем, когда вы действительно запускаете свое приложение, вы говорите

iTunesApplication* tunes = (iTunesApplication*)[SBApplication applicationWithBundleIdentifier:@"com.apple.music"];

. Это заставляет ваше приложение взаимодействовать с iTunes (Musi c) и снова получать словарь (sdef) , генерируя реализацию для методов, объявленных в заголовке. Реализация команды playpause точно такая, как в sdef, а именно: отправить событие hookPlPs в iTunes.

Это объясняет, почему вам разрешено скажем playpause и что происходит , когда вы это говорите.

Это то, что AppleScript есть - это приложение, предоставляющее список вещей, которые вы можете сказать к нему, используя события Apple, вместе с sh -подобными английскими терминами, которые ссылаются на эти события Apple.

Итак, если вы хотите написать мост, вы должны сделать то же самое: вам необходимо предоставить способ прочесывать ресурс sdef целевого приложения и преобразовывать эту информацию в способ, позволяющий давать соответствующие команды на вашем языке, каким бы он ни был.

...