Можно ли использовать pyobjc с привилегированным вспомогательным инструментом XPC и API XPCInterface? - PullRequest
0 голосов
/ 11 сентября 2018

Я полагаю, что ответ на этот вопрос - «нет», но я предлагаю его сообществу на тот случай, если кто-то добился большего успеха, чем я.

У меня есть привилегированный вспомогательный инструмент, который клиентское приложение Какао использует с NSXPCConnection и NSXPCInterface. Сам интерфейс включает метод, который предоставляет код возврата через блок обработчика завершения.

В Objective-C код клиента выглядит следующим образом:

NSXPCConnection * xpcConn = [NSXPCConnection alloc] 
    initWithMachServiceName:kSvcName 
    options:NSXPCConnectionPrivileged];

// MyProtocol defines an instance method runCommand:(NSString*) withReply:^(int result)
NSXPCInterface * mySvcIF = [NSXPCInterface interfaceWithProtocol:@protocol(MyProtocol)];

xpcConn.remoteObjectInterface = mySvcIF;
[xpcConn resume];
if (nil == xpcConn.remoteObjectProxy) {
    NSLog(@"ERROR - remote interface is nil, can't communicate with service");
}

[[xpcConn remoteObjectProxy] runCommand:nsstrCmd withReply:^(int result) {
    NSLog(@"service result is: %d", result);
    if (result != 0) {
        self.svcResult = result;
        self.svcCommandComplete = YES;
    }
}];

У меня также есть Mac-приложение pyobjc / py2app, которое должно использовать функциональность этого вспомогательного инструмента. У меня есть инструмент, встроенный в комплект приложений pyobjc, подписанный и авторизированный через SMJobBless, но похоже, что есть несколько проблем, которые делают использование этого API неподдерживаемым:

1) Соединение вызова runCommand: withReply: ^, похоже, не поддерживается - если я правильно понимаю, блоки поддерживаются только для вызовов метода платформы NS *, а не для «пользовательских» (то есть определяемых пользователем) методов? Обратите внимание, я мог бы сделать версию метода без кода возврата, если бы это была единственная проблема с блокировкой, но попытка не сработала, потому что ...

2) Чтобы использовать API способом Objective-C, мне нужно создать ссылку @selector на runCommand: на самом деле он не имеет никакой реализации функции python - он должен быть просто функциональным объектом, который определяет подпись для функции, которая будет предоставлена ​​динамически созданным remoteProxy. Я не определяю реализацию remoteProxy в Python. Кажется, это не поддерживается - я не смог заставить объявление селектора без функции python работать через objc.selector ().

3) Я не уверен, что даже если бы я мог заставить 2) работать, эта конструкция формального протокола работала бы так, как это ожидается в качестве параметра для interfaceWithProtocol: из python - он должен стать собственным обычаем @protocol, который NSXPCInterface может использовать в своем заводском методе для создания remoteProxy.

Спасибо за любые советы, если вы выяснили, как это сделать в pyobjc, или за окончательное подтверждение того, что этот материал просто невозможен, исходя из ваших знаний об этом.

1 Ответ

0 голосов
/ 30 декабря 2018

Первые два подвопроса просты для ответа: API можно вызывать с блоками, даже в библиотеках, которые не являются фреймворками Apple. Это требует дополнительной работы в коде Python, потому что среда выполнения Objective C не предоставляет достаточно информации, чтобы полностью автоматически делать правильные вещи.

Для этого конкретного примера вы можете сделать что-то вроде этого:

objc.registerMetaDataForSelector(b'NSObject', b'runCommand:withReply:', {
'arguments': {
    3: {
       'callable': {
          'retval': {'type': b'@'}, 
          'arguments': {
              0: {'type': b'^v'}, 
              1: {'type': b'i'}, 
          },
       },
    }
  }
})

Здесь регистрируется дополнительная информация для метода "- [NSObject runCommand: withReply:]". Аргументом блока является номер 3: отсчет начинается с 0, и первые два аргумента методов Objective-C - это «self» и «_sel» (последний не предоставляется Python).

Обычно вы используете реальный класс, в котором реализован метод, но я ожидаю, что это скрытый класс, который может даже генерироваться динамически. Простая регистрация метаданных в NSObject должна быть безопасной, если нет конфликта с другими классами.

Также возможно создание протоколов в Python:

MyProtocol = objc.formal_protocol('MyProtocol', (), [
    objc.selector(None, b"runCommand:withReply:", signature=b"v@:@@?"),
])

И создание интерфейса XPC с:

mySvcIF = Foundation.NSXPCInterface.interfaceWithProtocol_(MyProtocol)

Последний шаг, к сожалению, не работает, потому что NSXPCInterface вызывает исключение: NSInvalidArgumentException - NSXPCInterface: Unable to get extended method signature from Protocol data (MyProtocol / runCommand:withReply:). Use of clang is required for NSXPCInterface..

Я подал проблему для этого в трекере PyObjC: https://bitbucket.org/ronaldoussoren/pyobjc/issues/256/enable-using-xpcinterface-with-protocols.

Обходной путь для этой проблемы - создать расширение Python, которое содержит определение протокола, а также неиспользуемую функцию, которая использует протокол (см., Например, https://bitbucket.org/ronaldoussoren/pyobjc/src/default/pyobjc-framework-Cocoa/Modules/_AppKit_protocols.m для последней части). После импорта расширения вы можете использовать objc.protocolNamed("MyProtocol") для доступа к протоколу, который будет ссылаться на полный объект протокола, созданный clang, и должен работать с NSXPCInterface.

P.S. Я редко обращаюсь к stackoverflow, часто легче привлечь мое внимание, отправив письмо по адресу pyobjc-dev@lists.sourceforge.net (список рассылки PyObjC).

...