2010-08-19 5 views
1

Я пишу OS X Service с MacRuby. Он отображает выделенный текст. В основном это работает, но ... ну, вот все это:Инструмент Foundation OS X Service, Garbage Collection, MacRuby: почему мой NSRunLoop не будет зацикливаться на acceptInputForMode: beforeDate :?

#!/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"); 
// ... 

И, опять же, это не ждет в цикле. И, опять же, Ussing в 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 проекта доступна here (download) с Rake задачи, которая будет строить и установить его в ~/Library/Services. Затем вам нужно включить его флажок в «Службы» в панели предпочтений клавиатуры (один раз).

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

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

+0

Работает ли версия Objective-C с сборкой мусора, если вы поддерживаете ссылку на объект службы в основном. т. е. вы используете локальную переменную? – JeremyP

+0

Я получаю те же результаты, что и выше: выходы при использовании acceptInputForMode ..., отлично работают для runUntilDate. – kch

+0

Я бы сказал, что по какой-то причине цикл цикла не считает, что у него есть какие-либо источники ввода. – JeremyP

ответ

0

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

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, но все же, надеюсь, это поможет.

+0

Интересно, что с текущим MacRuby ночью, проблема, похоже, ушла. – kch

+0

Думаю, у нас был билет по этому вопросу, и Лоран исправил это. –

0

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

 Смежные вопросы

  • Нет связанных вопросов^_^