Can't retain block retrieved from within another block - objective-c

I am trying to write a unit test for a method which itself creates and passes a block to another object (so it can be called later). This method is using socket.io-objc to send a request to a server. It passes a callback block to socketio's sendEvent:withData:andAcknowledge that will be invoked when it receives a response from the server). Here is the method i want to test:
typedef void(^MyResponseCallback)(NSDictionary * response);
...
-(void) sendCommand:(NSDictionary*)dict withCallback:(MyResponseCallback)callback andTimeoutAfter:(NSTimeInterval)delay
{
__block BOOL didTimeout = FALSE;
void (^timeoutBlock)() = nil;
// create the block to invoke if request times out
if (delay > 0)
{
timeoutBlock = ^
{
// set the flag so if we do get a response we can suppress it
didTimeout = TRUE;
// invoke original callback with no response argument (to indicate timeout)
callback(nil);
};
}
// create a callback/ack to be invoked when we get a response
SocketIOCallback cb = ^(id argsData)
{
// if the callback was invoked after the UI timed out, ignore the response. otherwise, invoke
// original callback
if (!didTimeout)
{
if (timeoutBlock != nil)
{
// cancel the timeout timer
[NSObject cancelPreviousPerformRequestsWithTarget:self selector:#selector(onRequestTimeout:) object:timeoutBlock];
}
// invoke original callback
NSDictionary * responseDict = argsData;
callback(responseDict);
}
};
// send the event to the server
[_socketIO sendEvent:#"message" withData:dict andAcknowledge:cb];
if (timeoutBlock != nil)
{
// if a timeout value was specified, set up timeout now
[self performSelector:#selector(onRequestTimeout:) withObject:timeoutBlock afterDelay:delay];
}
}
-(void) onRequestTimeout:(id)arg
{
if (nil != arg)
{
// arg is a block, execute it
void (^callback)() = (void (^)())arg;
callback();
}
}
This all appears to be working fine when running for real. My problem comes in when I run my unit test (which uses SenTestingKit and OCMock):
-(void)testSendRequestWithNoTimeout
{
NSDictionary * cmd = [[NSDictionary alloc] initWithObjectsAndKeys:
#"TheCommand", #"msg", nil];
__block BOOL callbackInvoked = FALSE;
__block SocketIOCallback socketIoCallback = nil;
MyResponseCallback requestCallback = ^(NSDictionary * response)
{
STAssertNotNil(response, #"Response dictionary is invalid");
callbackInvoked = TRUE;
};
// expect controller to emit the message
[[[_mockSocket expect] andDo:^(NSInvocation * invocation) {
SocketIOCallback temp;
[invocation getArgument:&temp atIndex:4];
// THIS ISN'T WORKING AS I'D EXPECT
socketIoCallback = [temp copy];
STAssertNotNil(socketIoCallback, #"No callback was passed to socket.io sendEvent method");
}] sendEvent:#"message" withData:cmd andAcknowledge:OCMOCK_ANY];
// send command to dio
[_ioController sendCommand:cmd withCallback:requestCallback];
// make sure callback not invoked yet
STAssertFalse(callbackInvoked, #"Response callback was invoked before receiving a response");
// fake a response coming back
socketIoCallback([[NSDictionary alloc] initWithObjectsAndKeys:#"msg", #"response", nil]);
// make sure the original callback was invoked as a result
STAssertTrue(callbackInvoked, #"Original requester did not get callback when msg recvd");
}
To simulate a response, I need to capture (and retain) the block that is created by the method i'm testing and is passed to sendEvent. By passing an 'andDo' argument to my expectation, I am able to access the block i'm looking for. However, it seems like it is not being retained. So, when sendEvent unwinds and I go to invoke the callback, all the values that should have been captured in the block show up as null. The result is that the test crashes when I invoke socketIoCallback and it goes to access the 'callback' value that was originally captured as part of the block (and is now nil).
I am using ARC and so I expect that "__block SocketIOCallback socketIoCallback" will retain values. I've tried to "-copy" the block into this variable but still it does not seem to retain past the end of sendCommand. What can I do to force this block to retain long enough for me to simulate a response?
Note: I've tried calling [invocation retainArguments] which works but then crashes somewhere in objc_release when cleaning up after the test is complete.

I was finally able to reproduce the problem and I suspect that the error is in this code section:
SocketIOCallback temp;
[invocation getArgument:&temp atIndex:4];
Extracting the block this way does not work correctly. I'm not exactly sure why but it may have to do with some of the magic ARC does in the background. If you change the code to the following it should work as you'd expect:
void *pointer;
[invocation getArgument:&pointer atIndex:4];
SocketIOCallback temp = (__brigde SocketIOCallback)pointer;

#aLevelOfIndirection is correct in that it is due to call to getArgument:atIndex:.
To understand why, remember that SocketIOCallback is an object pointer type (it is a typedef for a block pointer type), which is implicitly __strong in ARC. So &temp is type SocketIOCallback __strong *, i.e. it is a "pointer to strong". When you pass a "pointer to strong" to a function, the contract is that if the function replaces the value pointed to by the pointer (which is __strong), it must 1) release the existing value, and 2) retain the new value.
However, NSInvocation's getArgument:atIndex: does not know anything about the type of the thing pointed to. It takes a void * parameter, and simply copies the desired value binary-wise into the location pointed to by the pointer. So in simple terms, it does a pre-ARC non-retained assignment into temp. It does not retain the value assigned.
However, since temp is a strong reference, ARC will assume it was retained, and release it at the end of its scope. This is an over-release, and thus will crash.
Ideally, the compiler should disallow conversions between "pointer to strong" and void *, so this doesn't accidentally happen.
The solution is to not pass a "pointer to strong" to getArgument:atIndex:. You can pass either a void * as aLevelOfIndirection showed:
void *pointer;
[invocation getArgument:&pointer atIndex:4];
SocketIOCallback temp = (__brigde SocketIOCallback)pointer;
or a "pointer to unretained":
SocketIOCallback __unsafe_unretained pointer;
[invocation getArgument:&pointer atIndex:4];
SocketIOCallback temp = pointer;
and then assign back into a strong reference afterwards. (or in the latter case, you could use the unretained reference directly if you are careful)

Related

Why NSInvocation return value creates a zombie?

I am trying to build a JavaScript to Native communication. For that purpose I need to execute dynamically a method on some class when JavaScript calls it.
I have a problem with NSInvocation getting the return value. When the getReturnValue is used the app crashes due to zombie. The zombie is indicated to be coming from the invocation called method's return value.
If I comment out the [invocation getReturnValue:&result]; line the app doesn't break.
The test method I am currently calling returns and (NSString *)
If I make the invoked selector method implementation return a literal string like #"firstsecond") the app doesn't break as well.
Why does it need a reference to it any way when the invocation method has already been executed and a string is returned. Isn't the returned string copied to the id result.
- (void)userContentController:(nonnull WKUserContentController *)userContentController didReceiveScriptMessage:(nonnull WKScriptMessage *)message {
if ([#"Native_iOS_Handler" isEqualToString: message.name]) {
NSArray *arguments = [message.body valueForKey:#"arguments"];
NSNumber *callbackID = [message.body valueForKey:#"callbackID"];
NSString *APIName = [message.body valueForKey:#"APIName"];
NSString *methodName = [message.body valueForKey:#"methodName"];
id classAPI = [self.exposedAPIs objectForKey:APIName];
SEL methodToRun = [classAPI getSelectorForJSMethod:methodName];
NSMethodSignature *methodSignature = [classAPI methodSignatureForSelector:methodToRun];
NSInvocation *invocation = [NSInvocation invocationWithMethodSignature:methodSignature];
[invocation setTarget:classAPI];
[invocation setSelector:methodToRun];
id result;
[invocation invoke];
[invocation getReturnValue:&result];
NSLog(#"%#", result);// output: firstsecond
}
}
//the selector in this case is this
-(NSString*)getFoo{
// Why is this a zombie????
return [NSString stringWithFormat:#"%#%#", #"first", #"second"];
// This works:
//return #"fristsecond"
}
Although the selector in Instruments is different the result is the same. From this picture I understand what I have told you. I have no experience whit Instruments.
You fell victim of ARC not being aware of the way NSInvocation works modifying the result indirectly through another pointer. It's a known problem described here.
What happens is the resulting object indirectly becomes equal to result but ARC is not aware of it and will never retain it.
Without going into too much details NSString is a class cluster. What it effectively means is the implementation underneath changes based on how the string is created and used. Details of it are hidden while interacting with it in obj-c and Apple put a lot of effort to make it seamless to iOS developers. Your case is somewhat special.
Typically you will be getting:
__NSCFConstantString (e.g. #"constant") - string constant for app lifetime , for your case it happens to work, but you should never rely on that
NSTaggedPointerString (e.g. [[#"a"] mutableCopy] copy]) - an optimised short string with internal lookup table.
__NSCFString (e.g. [#"longString" mutableCopy] copy]) long string CoreFoundation representation.
At any time NSString may change implementation underneath so you should never make assumptions about it. Case 3 will immediately go out of scope after returning and get deallocated in next run loop, case 1 will never get deallocated, case 2 (?), but for sure will survive the next run loop.
So basially ARC isn't clever enough to associate the potentially deallocated object with the id result and you run into your problems.
How to fix it?
Use one of these:
1.
void *tempResult;
[invocation getReturnValue:&tempResult];
id result = (__bridge id) tempResult;
2.
__unsafe_unretained id tempResult;
[invocation getReturnValue:&tempResult];
result = tempResult;
Edit
As noted by #newacct in his comment getReturnValue: doesn't do registration of weak pointers hence it's inappropriate in this case. The pointer would not get zeroed when the object gets deallocated.
3.
__weak id tempResult;
[invocation getReturnValue:&tempResult];
result = tempResult;

How to invoke an Objective-C block obtained at runtime?

I'm trying to write a mock of HKHealthStore. In the stubbed executeQuery: I need to call the result handler block of a HKSampleQuery instance passed to it. The block is private so I need to get it at runtime. This is what I have so far:
- (void)executeQuery:(HKQuery *)query {
NSAssert([query isKindOfClass:HKSampleQuery.class], #"Mock executeQuery: not implemented yet for other query types than HKSampleQuery");
HKSampleQuery *sampleQuery = (HKSampleQuery *)query;
NSMutableArray<HKObject *> *queryResults = [NSMutableArray new];
for (HKObject *o in self.storedObjects) {
if ([sampleQuery.predicate evaluateWithObject:o]) {
[queryResults addObject:o];
}
}
SEL selector = NSSelectorFromString(#"resultHandler");
Method m = class_getInstanceMethod(HKSampleQuery.class, selector);
IMP imp = method_getImplementation(m);
typedef void(*resultHandler_t)(id, SEL, void(^)(HKQuery*, NSArray*, NSError*));
resultHandler_t f = (resultHandler_t)imp;
// here, I need to invoke the result handler block with sampleQuery, queryResults and nil as arguments
}
Note the selector name is "resultHandler" even though the parameter of the initializer of HKSampleQuery is called "resultsHandler".
Is there any way to invoke the block with appropriate arguments?
You're not doing what you think you are doing. You are getting the implementation of the method resultHandler (the getter method of the property resultHandler). The block you want is the value of the property resultHandler, which is the return value of running the getter method. In other words, you need to run the getter and get the result, not get the getter itself.
Simplest way to call the method and get the return value (since in this case the return value is a regular object pointer type) would be
typedef void (^resultHandler_t)(HKSampleQuery *query, NSArray *results, NSError *error);
resultHandler_t f = [sampleQuery performSelector:#selector(resultHandler)];
f(sampleQuery, queryResults, nil);
Alternately, if you declare (but not implement) the instance method or property resultHandler in a dummy category of HKSampleQuery, you can then access the property directly like resultHandler_t f = sampleQuery.resultHandler;

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.

Is there a SELF pointer for blocks?

I'd like to recursively call a block from within itself. In an obj-c object, we get to use "self", is there something like this to refer to a block instance from inside itself?
Fun story! Blocks actually are Objective-C objects. That said, there is no exposed API to get the self pointer of blocks.
However, if you declare blocks before using them, you can use them recursively. In a non-garbage-collected environment, you would do something like this:
__weak __block int (^block_self)(int);
int (^fibonacci)(int) = [^(int n) {
if (n < 2) { return 1; }
return block_self(n - 1) + block_self(n - 2);
} copy];
block_self = fibonacci;
It is necessary to apply the __block modifier to block_self, because otherwise, the block_self reference inside fibonacci would refer to it before it is assigned (crashing your program on the first recursive call). The __weak is to ensure that the block doesn't capture a strong reference to itself, which would cause a memory leak.
The following recursive block code will compile and run using ARC, GC, or manual memory management, without crashing, leaking, or issuing warnings (analyzer or regular):
typedef void (^CountdownBlock)(int currentValue);
- (CountdownBlock) makeRecursiveBlock
{
CountdownBlock aBlock;
__block __unsafe_unretained CountdownBlock aBlock_recursive;
aBlock_recursive = aBlock = [^(int currentValue)
{
if(currentValue >= 0)
{
NSLog(#"Current value = %d", currentValue);
aBlock_recursive(currentValue-1);
}
} copy];
#if !__has_feature(objc_arc)
[aBlock autorelease];
#endif
return aBlock;
}
- (void) callRecursiveBlock
{
CountdownBlock aBlock = [self makeRecursiveBlock];
// You don't need to dispatch; I'm doing this to demonstrate
// calling from beyond the current autorelease pool.
dispatch_async(dispatch_get_main_queue(), ^
{
aBlock(10);
});
}
Important considerations:
You must copy the block onto the heap manually or else it will try to access a nonexistent stack when you call it from another context (ARC usually does this for you, but not in all cases. Better to play it safe).
You need TWO references: One to hold the strong reference to the block, and one to hold a weak reference for the recursive block to call (technically, this is only needed for ARC).
You must use the __block qualifier so that the block doesn't capture the as-yet unassigned value of the block reference.
If you're doing manual memory management, you'll need to autorelease the copied block yourself.
You have to declare the block variable as __block:
typedef void (^MyBlock)(id);
__block MyBlock block = ^(id param) {
NSLog(#"%#", param);
block(param);
};
There is no self for blocks (yet). You can build one like this (assuming ARC):
__block void (__weak ^blockSelf)(void);
void (^block)(void) = [^{
// Use blockSelf here
} copy];
blockSelf = block;
// Use block here
The __block is needed so we can set blockSelf to the block after creating the block. The __weak is needed because otherwise the block would hold a strong reference to itself, which would cause a strong reference cycle and therefore a memory leak. The copy is needed to make sure that the block is copied to the heap. That may be unnecessary with newer compiler versions, but it won't do any harm.

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.