updating UI from a C function in a thread - objective-c

I am using a library called libmosquitto in an iPhone app.
The library is written in C.
It receives push notifications and therefor runs in a thread.
I want to take the data it receives, and display it in a UITableView, however ( I think) I have to write the callbacks which libmosquitto uses as C functions rather than Objective C methods, so I cannot access 'self' in order to do:
[self performSelectorOnMainThread:#selector(hideActivityViewer) withObject:nil waitUntilDone:NO];
Anyone have problems like this, is there another way I could update the UI?
From inside one of my Objective C methods I call this:
mosquitto_message_callback_set(mosq, my_message_callback);
And my_message_callback is defined as:
void my_message_callback(void *obj, struct mosquitto_message *message)
{
NSLog(#"Do this thing:");
if(message->payloadlen){
const char *payload = (const char *)message->payload;
[array addObject:[NSString stringWithUTF8String: payload]];
//[self performSelectorOnMainThread:#selector(updateTable) withObject:nil waitUntilDone:NO];
//printf("%s %s\n", message->topic, message->payload);
}else{
//printf("%s (null)\n", message->topic);
}
//fflush(stdout);
}
Thanks

Look into Grand Central Dispatch (GCD, aka libdispatch). It's a C library so ought to be able to be called from your C code without issue. You'd want to do something like:
dispatch_async(dispatch_get_main_queue(), ^{
//code you want on the main thread.
});

The function mosquitto_new takes a void * pointer as the second argument, which it will then pass to any callbacks that you have. You can use that to pass self as the thing that should arrive at your callback as void *obj. It's then explicitly safe to cast that to the correct [pointer to] class type since C allows any pointer type to be converted to void * (and back) without any side effects.
So then you'd do something like:
void my_message_callback(void *obj, struct mosquitto_message *message)
{
[(ClassType *)obj
performSelectorOnMainThread:#selector(updateTable)
withObject:nil
waitUntilDone:NO];
}

You can get access to NSApp from everywhere, and I believe that any message not understood by NSApplication will be send to its delagate which is your instance of NSApplicationDelegate. If this does not do it, you could add a class application that returns the object that can run your method if this object is unique.
You can also use
Use grand central dispatch and the function
dispatch_async(dispatch_get_main_queue(), ^{
//some code
})

Related

Constructing or composing my own blocks or imps

Question
Does objective-c have any kind of functionality which allows me to compose my own blocks or IMPs on the fly?
By that I mean let me link together arbitrary code snippets into a single block (and then perform imp_implementationWithBlock) or just get an assembled IMP straight up.
Pseudocode
(IMP) linkExistingBlock:LBExistingBlock With:^{
}
or
(IMP) linkExistingBlock:LBExistingBlock With:LBAnotherBlock
If you have two Blocks, just call them. Further, Blocks are objects, and can be put into NSArrays. Then you can enumerate the array and invoke its contents.
for( dispatch_block_t block in arrayOfBlocks ){
block();
}
or
[arrayOfBlocks enumerateObjectsUsingBlock:^(dispatch_block_t block, NSUInteger idx, BOOL *stop) {
block();
}];
If you have IMPs, those are just function pointers -- they can be put into a C array, or wrapped in NSValues and put into a Cocoa array. You just need to cast them before you try to call them.
For your example method signature:
- (dispatch_block_t)blockLinkingExistingBlock: (dispatch_block_t)firstBlock withBlock: (dispatch_block_t)secondBlock
{
dispatch_block_t linker = ^{ firstBlock(); secondBlock();};
// if compiling with ARC
return linker;
// otherwise
// return [[linker copy] autorelease];
}
There's nothing built in, but you can just create a block that simply executes a series of blocks by calling them.

blocks and async callback, dealloc object - need to nil the block

There is a similar question here, which doesn't explain exactly what I want: Objective C Blocks as Async-callbacks & BAD ACCESS
I have a view controller, which calls a service with an async callback. The callback is done using a block, which references variables on the view controller to populate them.
It looks like so:
- (void) loadData {
__block MyViewController *me = self;
[self.service executeWithCompletion:^(NSArray *result, NSError *error) {
if (!error) {
me.data = result;
}
}];
}
However, if I dealloc the view controller, 'me' is then badly accessed by the callback.
What is the simplest way of making 'me' NULL? If i put it as an iVar, it then brings back the circular reference... i think?
I think I'm missing something obvious....
Thanks
Are you targeting iOS 5.0 or later (or Mac OS X 10.7 or later)? If so, you can use ARC and a __weak variable (instead of a __block one). This will automatically zero out when the referenced object is deallocated. Your code would look like
- (void)loadData {
__weak MyViewController *me = self;
[self.service executeWithCompletion:^(NSArray *result, NSError *error) {
if (!error) {
MyViewController *strongMe = me; // load __weak var into strong
if (strongMe) {
strongMe.data = result;
}
}
}];
}
If you need support for an older OS then you need to find a different solution. One solution is to just go ahead and let the block retain self. If the service is guaranteed to execute the completion block (and then release it), this will only produce a temporary cycle that will break automatically when the completion block is run. Alternatively if you have some way to cancel the service (in a way that guarantees the block cannot be called after the cancellation), you can stick with the __block and just be sure to cancel the service in your -dealloc. There's other alternatives too but they're more complicated.
I did a combination of things above from the suggestions. Including nilling the blocks. Although, my objects are still not getting released immediately. i.e. I'd put a breakpoint on dealloc of MyViewController, and without the __block variable it would get called at a much later point in time (probably due to the async connection) and sometimes not at all.
The code is fairly complex - so I imagine there are other things going on for it to not work as suggested above.
What I have also done, is used Mike Ash's MAZeroingWeakRef, which i guess is the same as using __weak - which #KevinBallard suggested.
Below is how I've implemented it, and it appears to be working. Dealloc is called immediately on disposal of the view controller, which i want. And I can't get it to crash... and with the log comment that i've put in, I can already see that I'm dodging bullets.
- (void) loadData {
__block MAZeroingWeakRef *zeroWeakRef = [[MAZeroingWeakRef alloc] initWithTarget:self];
[zeroWeakRef setCleanupBlock: ^(id target) {
[zeroWeakRef autorelease];
}];
[self.service executeWithCompletion:^(NSArray *result, NSError *error) {
MyViewController *me = [zeroWeakRef target];
if (!me) {
DULog(#"dodged a bullet");
}
if (!error) {
me.data = result;
}
}];
}
Is there a real retain cycle problem that you're trying to avoid? Is there a reason that self should not simply be retained until -executeWithCompletion: completes? Is there any real chance that it won't complete?
So long as it really will eventually complete (even with failure) and so long as it releases the block after invoking it (perhaps by setting a property to nil), then the retain cycle will eventually be broken and all will be well.

ObjC: cast of an indirect pointer to an Objective-C pointer

How can I change the following code to be compatible with ARC:
MyObj* fn = nil;
[self performSelectorOnMainThread:#selector(popSomething:) withObject:(id)&fn waitUntilDone:YES];
Right now, I get the following error:
error: cast of an indirect pointer to an Objective-C pointer to '__strong id' is disallowed with ARC [4]
If you're expecting the main thread to update the string, then a better way to do it would be to use a mutable string and simply pass it to the main thread:
NSMutableString* fn = [NSMutableString string];
[self performSelectorOnMainThread:#selector(queuedFileNamesPop:) withObject:fn waitUntilDone:YES];
Then the main thread can simply update the string.
The type of the argument should be (id *), ie. a pointer to an object, not an object.
But if you just want to return a value from a method that you need to execute on the main thread, a nicer solution is to use blocks and GCD:
__block id poppedFilename;
dispatch_sync(dispatch_get_main_queue(), ^{
poppedFilename = [self popFilename];
});
// do something with the popped file
This executes the method -popFilename on the main thread, and stores the result in poppedFilename. You must be careful not to call this method on the main thread, as it would deadlock. If you aren't sure if you are on the main thread, you could use something like this:
__block id poppedFilename;
if ([NSThread isMainThread]) {
poppedFilename = [self popFilename];
} else {
dispatch_sync(dispatch_get_main_queue(), ^{
poppedFilename = [self popFilename];
});
}

memory leak when using callback

I'm having an issue with memory management when dealing with callbacks and async code in objective c.
I cant seem to find a way to release the instance that the callback is set on.
For example:
MyClass *myArchive = [[MyClass alloc] init] ;
[myArchive callBack:^(RKObjectLoader* objectLoader, id object ) {
NSLog(#"success");
} fail:^(RKObjectLoader* objectLoader, NSError* error) {
NSLog(#"failed");
}];
[myArchive searchArchive:words:paging];
The problem being that I don't know when or how to release the instance *myArchive. Using Instruments within xcode to profile my code I always get a leak here. The function searchArchive performs an async request to a server using restkit. I wont reference the instance from within the callback as I heard this causes a retain cycle and I have done some reading about using __block and other c approaches to avoid retain cycles which is all fine but as it stands now with no actual code happening within the callback how do I release the *myArchive instance. anyone able to explain how I should deal with this within objective-c?
EDIT:
This is where I set the callback in myclass
// Sets internal backs on this object which basically wrap the delegate
//
- (void)callBack: (void (^)(RKObjectLoader* objectLoader, id object))success
fail: (void (^)(RKObjectLoader* objectLoader, NSError* error))fail {
//sanity check
NSAssert(_currentDelegate != self, #"Delegate is another object. Can not set callback");
// store our callback blocks in the instance
_success = [success copy] ;
_fail = [fail copy] ;
}
and then release _success and _fail in dealloc
and within the #interface
#interface myClass : NSObject<RKObjectLoaderDelegate> {
// holds the block callback for "success"
void (^_success)(RKObjectLoader* objectLoader, id object);
// holds the block callback for "fail"
void (^_fail)(RKObjectLoader* objectLoader, NSError* error);
}
I hope this gives more insight into what I'm doing wrong.
EDIT 2:
Ok I'm beginning to see the errors now:
-(void)retrieveGallery{
//create call back for async and deal with the result
[_galleryItems callBack:^(RKObjectLoader* objectLoader, NSArray *objects) {
//success happy days. do a bunch of code here that does not cause leaks
} fail:^(RKObjectLoader* objectLoader, NSError* error) {
//retry the attempt to retrieve gallery data from the server
_retryCount++;
if (_retryCount < _maxRetryCount) {
[self retrieveGallery];
}
}];
//read the collection of gallery items from server
[_galleryItems readGallery];
}
The only actual memory leaks are when the callback catches a fail for what ever reason and then calls the [self retrieveGallery] function from within callback to attempt again. this is what is causing the leak so I'm guessing that is a big no no. How should I attempt the function (retrieveGallery in this case) again.
Memory management isn't really any different because you are using an asynchronous callback. myArchive should be a property of whatever class you are doing this in. You want it to stick around until the task is complete, right?
#property (retain) MyClass *myArchive;
Then..
myArchive = [[MyClass alloc] init];
void (^on_success_callback)(void) = ^(void){
NSLog(#"success");
self.myArchive = nil;
};
You need to make sure you are managing the callbacks properly, i.e. copying them from the stack and releasing them when you are done.
If you have retains and releases in your code you probably aren't using the accessor methods properly.

Handling Callbacks

I have a method in an objective-C class. It has 2 callback functions written in C. The class pointer i.e. self is passed to these functions as void *. In the C functions I create a pointer of type class and assign the void * parameter.
The first callback function executes successfully. But the void * pointer becomes nil in the 2nd callback function. Note that I haven't tweaked pointer in the first callback but still I get nil in 2nd callback.
Any ideas what might be going wrong?
For example:
kr = IOServiceAddMatchingNotification(gNotifyPort, kIOFirstMatchNotification,
matchingDict, RawDeviceAdded, NULL,
&gRawAddedIter);
RawDeviceAdded(NULL, gRawAddedIter, self);
This works fine. But below function receives self as nil.
kr = IOServiceAddMatchingNotification(gNotifyPort, kIOFirstMatchNotification,
matchingDict, BulkTestDeviceAdded, NULL,
&gBulkTestAddedIter);
BulkTestDeviceAdded(NULL, gBulkTestAddedIter, self);
Are your problems specifically with the IOKit callback routines? The problem with the specific example you gave is that the IOServiceMatchingCallback takes only 2 parameters, not 3. You need your RawDeviceAdded() and BulkTestDeviceAdded() callback functions to match the IOServiceMatchingCallback prototype and to accept self as the first parameter (refCon), not the 3rd. Also, you need to pass in self as the second-to-last parameter of IOServiceAddMatchingNotification() to get it passed back to you by the callback.
A common method for handling C callbacks in Objective-C code is just to have a static function that forwards the callback to your instance. So, your example callback code would look like this:
static RawDeviceAdded(void* refcon, io_iterator_t iterator)
{
[(MyClass*)refcon rawDeviceAdded:iterator];
}
#implementation MyClass
- (void)setupCallbacks
{
// ... all preceding setup snipped
kr = IOServiceAddMatchingNotification(gNotifyPort,kIOFirstMatchNotification, matchingDict,RawDeviceAdded,(void*)self,&gRawAddedIter );
// call the callback method once to 'arm' the iterator
[self rawDeviceAdded:gRawAddedIterator];
}
- (void)rawDeviceAdded:(io_iterator_t)iterator
{
// take care of the iterator here, making sure to complete iteration to re-arm it
}
#end
Generally, callbacks in Objective-C are handled by passing a delegate object and a selector to perform on that delegate. For example, this method will call a method on its delegate after logging a message, passing both itself and the message that was logged.
- (void)logMessage:(NSString *)message
delegate:(id)delegate
didLogSelector:(SEL)didLogSelector
{
NSLog(#"%#", message);
if (delegate && didLogSelector && [delegate respondsToSelector:didLogSelector]) {
(void) [delegate performSelector:didLogSelector
withObject:self
withObject:message];
}
}
You might call it in code like this:
- (void)sayHello
{
[logger logMessage:#"Hello, world"
delegate:self
didLogSelector:#selector(messageLogger:didLogMessage:)];
}
- (void)messageLogger:(id)logger
didLogMessage:(NSString *)message
{
NSLog(#"Message logger %# logged message '%#'", logger, message);
}
You can also use objc_msgSend() directly instead, though you need to understand the Objective-C runtime enough to choose which variant to use and how to construct the prototype and function pointer through which to call it. (It's the mechanism by which message sends are actually implemented in Objective-C — what the compiler normally generates calls to in order to represent [] expressions.)
This is what Objective-C's selector is for:
http://developer.apple.com/iphone/library/documentation/Cocoa/Reference/NSInvocationOperation_Class
The API isn't very intuitive, but its fine once you understand it
You might need to do some refactoring as well, now there might be a better way, but when I had this problem my solution was to refactor and use InvoationOperation.