Сценарии Какао: перехватывать любые вызовы первого сообщения объекта

Я пишу свою собственную не-ObjC-среду на основе сценариев Какао (подумайте о написании сценариев Mac-приложения на C, C++ или Java или, в моем случае, Xojo).

Мне нравится иметь возможность перехватывать любой вызов метода Object-first вместо того, чтобы добавлять фактический метод в класс ObjC (я не могу, потому что фреймворк не будет знать, какое сообщение код приложения может обработать заранее, поэтому Вместо этого мне придется получать и передавать любые командные сообщения, как только они поступят из механизма сценариев).

Например, любые методы получения и установки свойств могут быть перехвачены посредством реализации

-valueForUndefinedKey:
-setValue:forUndefinedKey:

а также все методы NSScriptKeyValueCoding протокол.

Я ищу аналогичный способ перехвата NSCommandScript сообщения, отправленные методу, указанному в этих элементах sdef:

<responds-to command="reload">
    <cocoa method="reloadList:"/>
</responds-to>

Таким образом, вместо реализации reloadList: добавляя его в методы класса, мне интересно, есть ли общий способ перехвата всех таких вызовов.

Я обнаружил, что метод класса

+ (BOOL)resolveInstanceMethod:(SEL)sel

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

Я надеюсь, что есть кое-что, что позволяет мне сказать, что этот селектор связан с NSScriptCommand, прежде чем пересылать его дальше.

2 ответа

После установки точки останова в назначенном методе обработки команд я увидел следующую трассировку стека:

#0  0x00000001000197db in -[SKTRectangle rotate:]
#1  0x00007fff8ee0b7bc in __invoking___ ()
#2  0x00007fff8ee0b612 in -[NSInvocation invoke] ()
#3  0x00007fff8eeab5c6 in -[NSInvocation invokeWithTarget:] ()
#4  0x00007fff8b82cbde in -[NSScriptCommand _sendToRemainingReceivers] ()
#5  0x00007fff8b82cf39 in -[NSScriptCommand executeCommand] ()

Это показывает, что NSScriptCommand, по-видимому, не использует какой-либо настраиваемый специальный механизм пересылки, а использует NSInvocation для вызова метода.

Такие вызовы могут быть перехвачены следующим образом:

- (NSMethodSignature *)methodSignatureForSelector:(SEL)aSelector {
    // Look for signatures with exactly one ":"
    if ([[NSStringFromSelector(aSelector) componentsSeparatedByString:@":"] count] == 2) {
        return [NSMethodSignature signatureWithObjCTypes:"@:@@"];
    } else {
        return [super methodSignatureForSelector:aSelector];
    }
}

- (void)forwardInvocation:(NSInvocation *)anInvocation {
    id arg; // The first and only argument is expected to be of type NSScriptCommand*
    [anInvocation getArgument:&arg atIndex:2];
    if ([arg isKindOfClass:[NSScriptCommand class]]) {
        NSLog(@"executing the command...");
        // when finished, set the return value (which shall be an NSObject)
        id result = nil;
        [anInvocation setReturnValue:&result];
    } else {
        // oops - we cannot handle this
        [super forwardInvocation:anInvocation];
    }
}

Эта форма перехвата работает лучше, чем использование resolveInstanceMethod: потому что он вызывается не так часто, а только для определенных целей, таких как выполнение NSScriptCommand.

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

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

Если это не приложение на основе Какао, то вам, вероятно, лучше забыть об использовании сценариев Какао, так как оно тесно связано с остальной частью архитектуры Какао, устанавливайте свои собственные обработчики AE напрямую, используя NSAppleEventManager и напишите свой собственный клей View-Controller между ними и тем, во что вы в конечном итоге внедрите свою модель. См. также: Scriptable (AppleScript) в приложении Mac Carbon

ETA: Если подумать, возможно, вы захотите покопаться в Интернете и посмотреть, сможете ли вы углубиться в какие-либо старые C++ AEOM-фреймворки, так как ISTR были один или два назад в дни до OS X. Может потребовать некоторого обновления, и может быть, а может и не быть хорошим (но тогда CS тоже довольно дурацкий), но это было бы гораздо проще, чем начинать с нуля, как разработка и реализация хорошего, надежного, идиоматического (или даже упрощенного) Среда AEOM - это гигантская PITA, даже когда вы знаете, что делаете (и вряд ли кто-то знает).

Другие вопросы по тегам