[MacRuby-devel] Foundation tool OS X Service, Garbage Collection, MacRuby: why my NSRunLoop won’t loop in acceptInputForMode:beforeDate:?

Caio Chassot dev at caiochassot.com
Thu Aug 19 01:56:05 PDT 2010


(repost from http://stackoverflow.com/questions/3519436 under recommendation from @lrz)

I suspect this is not strictly a MacRuby issue since I can more or less cause it in pure Objective-C with GC. I observe the same NSRunLoop issue in Nu, in addition to crashing. Anyhoo:


I'm writing an OS X Service with MacRuby. It upcases the selected text. It mostly works, but… well, here's all of it:

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

It's just a Foundation tool, not part of an Application.

Now, see the `NSRunLoop…` line? That doesn't really work. The program exits imediately. I suppose the loop runs once and then exits. Anyhoo, the fact is that it's definititely not waiting 10s for input. So, here's what I did instead:

   NSRunLoop.currentRunLoop.runUntilDate NSDate.dateWithTimeIntervalSinceNow(60.0)


And that works, but naturally the program sticks around for 60s, and it's a kludge. So I implemented the whole thing in Objective C (Including KCUpcase, which is not shown). And… it works. With manual memory management. Once I switch to GC (`-fobjc-gc-only`), it exits imediately same as the MacRuby version.

   #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;
   }

But, alas, the fix is easy: because this is a Foundation tool (not an NSApplication), it seems I have to start GC manually by calling `objc_startCollectorThread`. Here:


   #import <objc/objc-auto.h>
   // ...
   NSLog(@"Starting…");
   objc_startCollectorThread();
   NSRegisterServicesProvider([[KCUpcase alloc] init], @"KCUpcase");
   // ...


Ok, but what's up with MacRuby then? Let's throw it into the mix:

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

And, again, it's not waiting in the loop. And, again, ussing the `runUntilDate:` kludge instead of `acceptInputForMode:beforeDate:` works:

   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;

**So, I suppose I'm missing something terribly obvious. Please enlighten me.**

---

And by the way, the full MacRuby version of the project is available (see below) with a Rake task that'll build and install it in `~/Library/Services`. Then you need to enable its checkbox in Services in the Keyboard Preference Pane (once).

* view: http://gist.github.com/537075
* download: http://gist.github.com/gists/537075/download
* clone: git clone git://gist.github.com/537075.git

**Aside**: Interestingly, I tried calling `NSLog` inside the MacRuby string, and it raised `NoMethodError`. What gives?



More information about the MacRuby-devel mailing list