I’ve worked through the excellent iOS Corona Native tutorial that Scott posted and got a test plugin working for simple cases - ie I can register a function, call it from my Lua code and have it generate an event which get’s picked up by the listener function registered when I call the plugin .init function.
However when I want to take it to the next level I’m hitting an issue. I’m developing a plugin with a simple interface.
plugin.init( listener ) -- initialise the plugin and register the event listener plugin.start() -- start a monitoring process that continually sends events back to the listener plugin.stop() -- stop the monitoring process
To implement the monitoring / recording process in my Objective C code in the start function I use an NSTimer scheduledTimerWithTimeInterval : block to basically create a closure and this appears to work fine - the block repeats fine and will happily NSLog() until I call the stop() function which calls the invalidate method on the above NSTimer instance.
However when I attempt to create a new event from within the block the code stops at the following line
Self \*library = (Self \*)CoronaLuaToUserdata( L, lua\_upvalueindex( 1 ) );
With an EXC_BAD_ACCESS (code=1, address=0x0b) error - as far as I can tell it’s getting a null reference from somewhere but I can’t work out why.
Here’s the code from my plugin
class PluginLibrary { protected: PluginLibrary(); static int Finalizer( lua\_State \*L ); public: typedef PluginLibrary Self; static const char kName[]; static const char kEvent[]; bool Initialize( CoronaLuaRef listener ); CoronaLuaRef GetListener() const { return fListener; } static int Open( lua\_State \*L ); static Self \*ToLibrary( lua\_State \*L ); static int init( lua\_State \*L ); static int start( lua\_State \*L ); static int stop( lua\_State \*L ); static int trigger( lua\_State \*L ); private: static void sendMessage( lua\_State \*L, NSString \*message ); static void sendFrequency( lua\_State \*L, double frequency ); static int updateCounter(); CoronaLuaRef fListener; }; // ---------------------------------------------------------------------------- int fakeFreqValue; bool recording; NSTimer \*heartbeat; // ---------------------------------------------------------------------------- const char PluginLibrary::kName[] = "plugin.tuner"; // This corresponds to the name of the library, e.g. [Lua] require "plugin.library" const char PluginLibrary::kEvent[] = "Tuner"; // This corresponds to the event name, e.g. [Lua] event.name PluginLibrary::PluginLibrary() : fListener( NULL ) { fakeFreqValue = 0; recording = NO; heartbeat = nil; } bool PluginLibrary::Initialize( CoronaLuaRef listener ) { // Can only initialize listener once bool result = ( NULL == fListener ); if ( result ) { fListener = listener; } return result; } int PluginLibrary::Open( lua\_State \*L ) { // Register \_\_gc callback const char kMetatableName[] = \_\_FILE\_\_; // Globally unique string to prevent collision CoronaLuaInitializeGCMetatable( L, kMetatableName, Finalizer ); // Functions in library const luaL\_Reg kVTable[] = { { "init", init }, { "start", start }, { "stop", stop }, { "trigger", trigger }, { NULL, NULL } }; // Set library as upvalue for each library function Self \*library = new Self; CoronaLuaPushUserdata( L, library, kMetatableName ); luaL\_openlib( L, kName, kVTable, 1 ); // leave "library" on top of stack return 1; } int PluginLibrary::Finalizer( lua\_State \*L ) { Self \*library = (Self \*)CoronaLuaToUserdata( L, 1 ); CoronaLuaDeleteRef( L, library-\>GetListener() ); delete library; return 0; } PluginLibrary \*PluginLibrary::ToLibrary( lua\_State \*L ) { // library is pushed as part of the closure Self \*library = (Self \*)CoronaLuaToUserdata( L, lua\_upvalueindex( 1 ) ); return library; } // [Lua] library.init( listener ) int PluginLibrary::init( lua\_State \*L ) { int listenerIndex = 1; // TODO - Request permission to use the microphone... if ( CoronaLuaIsListener( L, listenerIndex, kEvent ) ) { Self \*library = ToLibrary( L ); CoronaLuaRef listener = CoronaLuaNewRef( L, listenerIndex ); library-\>Initialize( listener ); } return 0; } int PluginLibrary::updateCounter() { fakeFreqValue += 1; return fakeFreqValue; } // [Lua] library.start( word ) - start sampling int PluginLibrary::start( lua\_State \*L ) { if (!recording) { // Start sampling the mic and set up a regular heartbeat to send the calculated frequency value via the registered listener... NSString \*message = @"Recording..."; // Fake with a simple counter for now... heartbeat = [NSTimer scheduledTimerWithTimeInterval:1.0 repeats:YES block:^(NSTimer \* \_Nonnull timer) { // NSLog(@"Bing %i",updateCounter()); sendFrequency(L, updateCounter()); }]; recording = YES; sendMessage(L, message); } return 0; } // [Lua] library.off( word ) - stop sampling int PluginLibrary::stop( lua\_State \*L ) { if (recording) { // Stop sampling the mic and stop the heartbeat function... NSString \*message = @"Stopped"; [heartbeat invalidate]; heartbeat = nil; recording = NO; fakeFreqValue = 0; sendMessage(L, message); } return 0; } // [Lua] library.trigger( word ) - force a read and transmit via a despatched event... int PluginLibrary::trigger( lua\_State \*L ) { fakeFreqValue += 1; sendFrequency(L, (double)fakeFreqValue); return 0; } // ---------------------------------------------------------------------------- void PluginLibrary::sendMessage( lua\_State \*L, NSString \*message ) { // Create event and add the data to it Self \*library = ToLibrary(L); CoronaLuaNewEvent(L, kEvent); lua\_pushstring(L, [message UTF8String]); lua\_setfield(L,-2,"message"); CoronaLuaDispatchEvent(L, library-\>GetListener(), 0); } void PluginLibrary::sendFrequency( lua\_State \*L, double value ) { NSLog(@"sendFrequency %f",value); lua\_Number frequency = value; // Create event and add the data to it Self \*library = ToLibrary(L); // \<\<-- This is where it blows up when called from within a block CoronaLuaNewEvent(L, kEvent); lua\_pushnumber(L, frequency); lua\_setfield(L,-2,"frequency"); CoronaLuaDispatchEvent(L, library-\>GetListener(), 0); }
Any help or pointers in the right direction would be gratefully appreciated.