Инструмент основания OS X Service, Сборка мусора, MacRuby: почему мой NSRunLoop не будет зацикливаться в acceptInputForMode: beforeDate :? - PullRequest
1 голос
/ 19 августа 2010

Я пишу OS X Service с 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…? Это на самом деле не работает. Программа выходит сразу. Я полагаю, цикл запускается один раз, а затем завершается. Anyhoo, факт в том, что он определенно не ждет 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), кажется, мне нужно вручную запустить GC, вызвав 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: kludge вместо 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 доступна здесь ( загрузка ) с задачей Rake, которая будет собирать и устанавливать ее в ~/Library/Services. Затем вам нужно включить его флажок в разделе «Службы» на панели настроек клавиатуры (один раз).

(или git clone git://gist.github.com/537075.git)

В сторону : Интересно, что я попытался вызвать NSLog внутри строки MacRuby, и она подняла NoMethodError. Что дает?

Ответы [ 2 ]

0 голосов
/ 03 января 2011

acceptInputForMode: beforeDate: цикл запускается только один раз. Как только любой вход (кроме таймера) обрабатывается, он завершается. runUntilDate: однако продолжает выполнение цикла, пока не будет достигнута дата.

0 голосов
/ 22 ноября 2010

Это немного странно, но есть обходной путь:

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

puts "Starting…"
NSRegisterServicesProvider(KCUpcase.new, "Upcase")
puts "Registered…"
later = NSDate.dateWithTimeIntervalSinceNow(5)
NSRunLoop.currentRunLoop.runUntilDate later
puts "Done"

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

...