0

I'm implementing callback routines for external static C++ library to be used in Objective-C project. Now I have trouble moving data between callback and normal routines. As you can see below my "msgStore" is defined as part of MyMessage class and can be used within class routines such as init(). However attempting same from callback routine, which is NOT part of the MyMessage class, fails.

@interface MyMessage : NSObject {
    NSMutableArray *msgStore;
}
@property (nonatomic, retain) IBOutlet NSMutableArray *msgStore;

// Callback functions declarations
void aCBack (const std::string text);

@implementation MyMessage    
@synthesize msgStore;

- (id)init
{
    if ((self = [super init])) { }

    if (msgStore == nil) {
        msgStore = [NSMutableArray arrayWithCapacity:100];        
    }
    return self;
}

void aCBack (const std::string text)
{
    NSString *msg = [[NSString alloc] initWithCString:(const char *)text.c_str() encoding:NSUTF8StringEncoding];
    [msgStore insertObject:msg atIndex:0];
}

The last code line gives error message 'msgStore' was not declared in this scope. I'm guessing it's because aCBack is a plain C function and thus does not have automatic "self" pointer?

Any ideas how to save data received in callback for use inside Obj-C class?

JOM
  • 8,139
  • 6
  • 78
  • 111
  • I can do Obj-C stuff inside callback, so could I somehow create or request MyMessage object pointer? Not creating new object, but pointer to already existing one? Well, one solution might be to create msgStore as a singleton, I guess. Hoping there would be something easier... – JOM Dec 03 '09 at 10:52

3 Answers3

1

You cannot call msgStore from the function because it is not in the scope of the function.

There are a few ways to get to it in the function.

One is to use a singleton class. If you plan on only using one message store, then you can make that class a singleton. That means you can get the object instance of that class by calling a class method, which you can do from any scope. See also: What should my Objective-C singleton look like?

MyMessage * myMsg = [MyMessage sharedMessage]; // this will get you a pointer to the shared instance

Another way is, if the callback function allows, you can also pass it as a void * data argument, then cast it to a MyMessage in the function. See also Alex Deem's answer.

PS. You create the array with [NSArray arrayWithCapacity:], which you might want to make [[NSArray arrayWithCapacity:] retain] or just [[NSArray alloc] initWithCapacity:], so the object won't vannish on the next autoreleasepool housekeeping round.

Community
  • 1
  • 1
nash
  • 2,181
  • 15
  • 16
  • Thanx for both tips! Singleton really seems to be solution for this problem (did some reading in between) and especial thanx for the housekeeping! However my Quick But Dirty solution was something else, no time for proper singleton... More below. But your answer is the right one! – JOM Dec 04 '09 at 05:54
0

The simple solution is to pass a pointer to the MyMessage object as an argument of the callback function. Something like:

void aCBack( MyMessage * message, const std::string text)
{
    NSString *msg = [[NSString alloc] initWithCString:(const char *)text.c_str() encoding:NSUTF8StringEncoding];
    [message.msgStore insertObject:msg atIndex:0];
}
Alex Deem
  • 4,717
  • 1
  • 21
  • 24
  • aCBack is declared outside of my reach. I have to give aCBack function pointer to library, so that it can call me back. Thus cannot add MyMessage object as additional argument. That would make external library dependent on my implementation, which is not allowed... – JOM Dec 03 '09 at 10:46
  • Instead of using a MyMessage * you can use a void * and type-cast it as appropriate. Without something in the callback indicating which object the callback is relating to you really have no idea. Imagine the case when you have more than one MyMessage objects waiting for callbacks at the same time. When the callback comes, how do you know which one it is for? – Alex Deem Dec 03 '09 at 10:54
  • Have to check that, thanx! Btw the example is greatly simplified.. I have 5 callbacks for different purposes, but just one "object" to handle them. This object will send notification(s) when something happens and those (objects) who are interested can request more info. Yep, planning to refactor, but now just got to get it working as a demo. – JOM Dec 03 '09 at 11:09
0

Let's be clear: Singleton is the answer (at least one good choice). However deadline is hanging on my neck and I just got to get something more or less working today. Here's what I use, for here and now. Please note this is NOT a "singleton":

@interface MyMessage : NSObject {
    NSMutableArray *msgStore;
}
@property (nonatomic, retain) NSMutableArray *msgStore;
- (void)myStoreMessage: (NSString *) msg;
@end

// Callback C/C++ function declarations
void aCBack (const std::string text);
MyMessage *myObserver = nil;

void aCBack (const std::string text)
{
    NSString *msg = [[NSString alloc] initWithCString:(const char *)text.c_str() encoding:NSUTF8StringEncoding];
    [myObserver myStoreMessage:[NSString stringWithString:msg]];
    [msg release];
    // TODO: fix memory leak
}

@implementation MyMessage    
@synthesize msgStore;

- (id)init
{
    if ((self = [super init])) { }

    if (msgStore == nil) {
        msgStore = [[NSMutableArray arrayWithCapacity:100] retain];
    }
    myObserver = self;
    return self;
}

- (void) myStoreMessage: (NSString *)msg
{
    [self.msgStore insertObject:msg atIndex:0];
}
@end

What can I say, it seems to work. There is a memory leak with msg, which I haven't figured out, but otherwise this is what I'll be using for the demo. When there is time afterwards (yeah, sure) I'll implement a proper Singleton.

JOM
  • 8,139
  • 6
  • 78
  • 111
  • Using a global variable works too :-p Quick and dirty! The memory leak here is that you don't have a dealloc in `MyMessage`. You should deallocate msgStore. Also, since you use a global, it would be nice to set the `myObserver` to Nil in your dealloc method. Also, in the init method everything but `return self` should be between the accolades of (self = [super init]). – nash Dec 04 '09 at 06:41
  • The whole file is about 200 lines, had to cut short: dealloc is there, but added myObserver = nil; Thanx for init fix, too! The memory leak is really with msg in aCBack routine... Callback is called from different thread than MyMessage object, which might have something to do with it. Most likely I just need to define local NSAutoReleasePool within callback. – JOM Dec 04 '09 at 07:47