Я пишу службу OS X с MacRuby. Он увеличивает выделенный текст. В основном это работает, но… ну, вот и все:
#!/usr/local/bin/macruby
# encoding: UTF-8
framework 'Foundation'
framework 'AppKit'
class KCUpcase
def upcase(pasteboard, userData: s_userdata, error: s_error)
incoming_string = pasteboard.stringForType "public.utf8-plain-text"
outgoing_string = incoming_string.upcase
pasteboard.clearContents
pasteboard.setString(outgoing_string, forType: "public.utf8-plain-text")
end
end
NSLog "Starting…"
NSRegisterServicesProvider(KCUpcase.new, "Upcase")
NSLog "Registered…"
NSRunLoop.currentRunLoop\
.acceptInputForMode(NSDefaultRunLoopMode,
beforeDate:NSDate.dateWithTimeIntervalSinceNow(10.0))
NSLog "Done."
Это просто инструмент Foundation, а не часть приложения.
Теперь видите строку NSRunLoop…
? Это действительно не работает. Программа немедленно завершает работу. Я полагаю, что цикл выполняется один раз, а затем выходит. Во всяком случае, дело в том, что он определенно не ждет ввода 10 секунд. Итак, вот что я сделал вместо этого:
NSRunLoop.currentRunLoop.runUntilDate NSDate.dateWithTimeIntervalSinceNow(60.0)
И это работает, но, естественно, программа зависает на 60 секунд, и это бесполезно. Поэтому я реализовал все это на Objective C (включая KCUpcase, который не показан). И… это работает. С ручным управлением памятью. Как только я переключаюсь на GC (-fobjc-gc-only
), он сразу же закрывается, как и версия MacRuby.
#import <Foundation/Foundation.h>
#import "KCUpcase.h"
int main (int argc, const char * argv[]) {
NSLog(@"Starting…");
NSRegisterServicesProvider([[KCUpcase alloc] init], @"KCUpcase");
NSLog(@"Registered…");
[[NSRunLoop currentRunLoop]
acceptInputForMode:NSDefaultRunLoopMode
beforeDate:[NSDate dateWithTimeIntervalSinceNow:10.0]];
NSLog(@"Done.");
return 0;
}
Но, увы, это легко исправить: поскольку это инструмент Foundation (а не NSApplication), кажется, мне придется запускать сборщик мусора вручную, вызвав objc_startCollectorThread
. Здесь:
#import <objc/objc-auto.h>
// ...
NSLog(@"Starting…");
objc_startCollectorThread();
NSRegisterServicesProvider([[KCUpcase alloc] init], @"KCUpcase");
// ...
Хорошо, но что тогда с MacRuby? Давайте добавим это в смесь:
#import <MacRuby/MacRuby.h>
// ...
NSLog(@"Starting…");
objc_startCollectorThread(); // This magic stops working once we add MacRuby
[[MacRuby sharedRuntime] evaluateString: @"$stderr.puts 'hi from macruby'"];
NSRegisterServicesProvider([[KCUpcase alloc] init], @"KCUpcase");
// ...
И, опять же, это не ожидание в цикле. И опять же, использование кладжа runUntilDate:
вместо acceptInputForMode:beforeDate:
работает:
NSLog(@"Starting…");
[[MacRuby sharedRuntime] evaluateString: @"$stderr.puts 'hi from macruby'"];
NSRegisterServicesProvider([[KCUpcase alloc] init], @"KCUpcase");
NSLog(@"Registered…");
// Hmmm…
[[NSRunLoop currentRunLoop]
runUntilDate:[NSDate dateWithTimeIntervalSinceNow:10.0]];
NSLog(@"Done.");
return 0;
Полагаю, я упускаю что-то ужасно очевидное. Пожалуйста, просветите меня.
Кстати, полная версия проекта для MacRuby доступна здесь (download) с заданием Rake, которое создаст и установит его в ~/Library/Services
. Затем вам нужно включить его флажок в разделе «Службы» на панели настроек клавиатуры (один раз).
(or git clone git://gist.github.com/537075.git
)
Кроме того: интересно, я попытался вызвать NSLog
внутри строки MacRuby, и он поднял NoMethodError
. Что дает?