NSDictionary of Blocks as argument cause overflow - objective-c

I understand blocks are objective c objects and can be put in NSDictionary directly without Block_copy when using ARC. But I got EXC_BAD_ACCESS error with this code:
- (void)viewDidLoad
{
[super viewDidLoad];
[self method1:^(BOOL result){
NSLog(#"method1WithBlock finished %d", result);
}];
}
- (void) method1:(void (^)(BOOL))finish{
NSDictionary *dict = [NSDictionary dictionaryWithObjectsAndKeys:^(NSData *rcb){
finish(YES);
}, #"success",
^(NSError *error){
finish(NO);
}, #"failure", nil];
[self method2:dict];
}
- (void) method2:(NSDictionary *)dict{
void (^success)(NSData *rcb) = [dict objectForKey:#"success"];
success(nil);
}
If I change method1: to this, no error raised.
- (void) method1:(void (^)(BOOL))finish{
void (^success)(NSData *) = ^(NSData *rcb){
finish(YES);
};
void (^failure)(NSError *error) = ^(NSError *error){
finish(NO);
};
NSDictionary *dict = [NSDictionary dictionaryWithObjectsAndKeys:success, #"success",
failure, #"failure", nil];
[self method2:dict];
}
Can anybody explain why I have to use automatic variables to store the blocks before putting them to dictionary ?
I am using iOS SDK 6.1.

According to the "Transitioning to ARC Release Notes", you have to copy a block stored
in a dictionary (emphasis mine):
Blocks “just work” when you pass blocks up the stack in ARC mode, such
as in a return. You don’t have to call Block Copy any more.
You still need to use [^{} copy] when passing “down” the stack into
arrayWithObjects: and other methods that do a retain.
The second method works "by chance" because success and failure are a
__NSGlobalBlock__ and not a "stack based block" that needs to be copied to the heap.

I understand blocks are objective c objects and can be put in NSDictionary directly without Block_copy when using ARC.
No, they're not common objects. When you create a block it is on the stack, and it doesn't matter of what is it's retain count, when you exit form the function it will be popped from the stack. Copy it to make it stay alive.

You must copy blocks before passing them to a method when 1) the block will be stored for longer than the duration of the call, and 2) the parameter you are passing it to is a normal object pointer type (i.e. id or NSObject *) instead of a block type.
This is the case for your call. dictionaryWithObjectsAndKeys: stores the argument in the resulting dictionary, and it simply expects normal object pointer arguments (of type id), and does not know whether you are passing blocks or not.
The reason for the second condition I said is because if the method parameter already takes a block type (e.g. for any completion handler parameters), then that method is already aware of the special memory management requirements of blocks, and therefore will take responsibility for copying the block if it needs to store it. In that case the caller doesn't need to worry about it. However, in your case, you are passing it to a general method that doesn't know it's getting a block, and thus doesn't know to copy it. So the caller must do it.
Can anybody explain why I have to use automatic variables to store the
blocks before putting them to dictionary ?
About this, your second example happens to work because recent versions of the ARC compiler is super conservative about blocks and inserts copies whenever you assign it to a block type variable. However, this is not guaranteed by the ARC specification, and is not guaranteed to work in the future, or in another compiler. You should not rely on this behavior.

Related

Why need to copy the objective-c handler before cast it to void* type?

I want to simulate the NSAlert member function which only exist on 10.9.
- (void)beginSheetModalForWindow:(NSWindow *)sheetWindow completionHandler:(void (^)(NSModalResponse returnCode))handler NS_AVAILABLE_MAC(10_9);
The code is as following:
-(void)compatibleBeginSheetModalForWindow: (NSWindow *)sheetWindow
completionHandler: (void (^)(NSInteger returnCode))handler
{
void *handlerValue = (__bridge void*) [handler copy];
[self beginSheetModalForWindow: sheetWindow
modalDelegate: self
didEndSelector: #selector(blockBasedAlertDidEnd:returnCode:contextInfo:)
contextInfo: handlerValue];
}
-(void)blockBasedAlertDidEnd: (NSAlert *)alert
returnCode: (NSInteger)returnCode
contextInfo: (void *)contextInfo
{
void(^handler)(NSInteger) = (__bridge typeof(handler)) contextInfo;
handler(returnCode);
[handler release];
}
I need to copy the handler before cast it to void*, else it will crash, if I change it to following line:
void *handlerValue = (__bridge void*) handler;
And by check the handler in blockBasedAlertDidEnd:returnCode:contextInfo:, it is an incorrect value.
Even I call [handler retain] before cast to void* it doesn't work.
It is quite confuse to me, so why I need to copy it?
This is because blocks are created on the stack and not the heap. So when the stack pointer moves on the memory in that stack frame is returned and the block is lost. Thats why you always should copy blocks and not retain them. When you run copy on a block the new copy is allocated on the heap and therefor don't get removed.
Peter Segerblom covered the basics of it. In the current implementation, there are 3 kinds of blocks. Blocks that don't capture local variables are global blocks; there is just one instance that has static lifetime. Blocks that do capture local variables start out as objects on the stack; and copying it will return a block on the heap. Copying heap or global blocks will simply return the same instance.
Basically, a block passed into your function could be a stack block or not. A stack block is only valid for the current function call. Since it could be a stack block, you must use a copy of it if you want to store it somewhere that outlives the current function call. This is the contract for functions that take a block parameter.
Now, if all your function does with this block is pass it to another function, do you need to pass a copy? If this function parameter is of block type, then no, because if this function needs to keep the block around for later, then it is responsible for copying it (according to the contract above). So you don't need to worry about it. But, if you pass it to a function that is not of block type (e.g. -[NSMutableArray addObject:]), then that function does not know to potentially copy it (it doesn't even know it's a block). In that case, you will have to pass a copy if that function keeps the object around for later. This is the case with the function you are passing to here.

Why does Objective-C block still work without copying it to the heap?

I have a simple method in my class:
- (void)getFormWithBlock:(DataCenterResultBlock)block {
[SomeClass doSomeLongOperationWithParam:someParam
completionBlock:^(NSData *data, NSURLResponse *response) {
//Success
block(aVar, YES);
} errorBlock:^(NSError *error) {
//Failed
block(nil, NO);
}];
}
I read that you should copy blocks to the heap if you are doing something asynchronously because they are allocated on stack and once the call tree rewinds it will be gone.
But here, I am not copying it to heap but still I get no crash. Why?
Thanks
Block variables are copied to the heap automatically by ARC compilers:
7.5. Blocks
...
__block variables of retainable object owner type are moved off the stack by initializing the heap copy with the result of moving from the stack copy.
EDIT I think I misunderstood the question: you asked about block objects themselves, not block variables. The answer in this case is slightly different, but it boils down to the same this: ARC does the correct thing automatically.
ARC knows that block literals must be copied if they're used after the current scope returns. Non-ARC code needs to explicitly copy and autorelease returned blocks:
return [[^{
DoSomethingMagical();
} copy] autorelease];
With ARC, this simply becomes:
return ^{ DoSomethingMagical(); };
(from here)
[SomeClass doSomeLongOperationWithParam:completionBlock:errorBlock:] should be copying the completion and error blocks.
If you look at the implementation of that method, it is probably doing the right thing and copying the block that you passed in. ARC or no ARC, that method should copy those blocks.

passing objects from no arc compiled class to arc enabled class?

I ran into this problem while trying to fix a memory leak with the facebook-ios-sdk. How do i handle this situation when passing objects from no arc compiled classe to arc enabled classe?
This is the code inside the non arc compiled Facebook library: (i removed the unnecessary stuff which is not related to the problem) as you can see, result object is not autoreleased or released.
- (void)handleResponseData:(NSData *)data {
NSError* error = nil;
id result = [self parseJsonResponse:data error:&error];
self.error = error;
// Call the defined delegate wich is my AppDelegate didLoad method wich is arc enabled
[_delegate request:self didLoad:result];
}
- (id)parseJsonResponse:(NSData *)data error:(NSError **)error {
SBJSON *jsonParser = [[SBJSON alloc] init];
//gets the object wich leaks or gets overreleased
id result = [jsonParser objectWithString:responseString];
[jsonParser release];
return result;
}
Now if i try to add autorelease to the result object, i am facing a NSZombie when my arc code in my AppDelegate try's to release the object. However if i leave the code like this i'm facing memory leaks whit the result object which gets not released.
am i missing something basic? i can't get my head around this?
Thanx for any advice! Chris
The result returned from -parseJsonResponse:... is autoreleased already (see note at bottom).
Since the name of the -parseJson method doesn't begin with new, alloc, copy or mutableCopy, the compiler assumes that it returns an object with a +0 reference count, meaning it needs to be retained by the calling code if it is to be kept around, and doesn't need to be released if it's not being kept around. That's a long winded way of saying that it should neither leak nor cause a crash in your ARC code as written in your question.
Passing objects between ARC code and manual reference counting code doesn't require any special handling. You just need to make sure that methods' names match their memory management semantics in the non-ARC code. It certainly seems like you've done that in this case, although as you say, you didn't post your complete code.
Note: Presumably, objectWithString: returns an autoreleased object. If it doesn't it, it should (because it doesn't start with alloc, new, copy, mutableCopy).

Why does the ARC migrator say that NSInvocation's -setArgument: is not safe unless the argument is __unsafe_unretained?

I was migrating a block of code to automatic reference counting (ARC), and had the ARC migrator throw the error
NSInvocation's setArgument is not safe to be used with an object with
ownership other than __unsafe_unretained
on code where I had allocated an object using something like
NSDecimalNumber *testNumber1 = [[NSDecimalNumber alloc] initWithString:#"1.0"];
then set it as an NSInvocation argument using
[theInvocation setArgument:&testNumber1 atIndex:2];
Why is it preventing you from doing this? It seems just as bad to use __unsafe_unretained objects as arguments. For example, the following code causes a crash under ARC:
NSDecimalNumber *testNumber1 = [[NSDecimalNumber alloc] initWithString:#"1.0"];
NSMutableArray *testArray = [[NSMutableArray alloc] init];
__unsafe_unretained NSDecimalNumber *tempNumber = testNumber1;
NSLog(#"Array count before invocation: %ld", [testArray count]);
// [testArray addObject:testNumber1];
SEL theSelector = #selector(addObject:);
NSMethodSignature *sig = [testArray methodSignatureForSelector:theSelector];
NSInvocation *theInvocation = [NSInvocation invocationWithMethodSignature:sig];
[theInvocation setTarget:testArray];
[theInvocation setSelector:theSelector];
[theInvocation setArgument:&tempNumber atIndex:2];
// [theInvocation retainArguments];
// Let's say we don't use this invocation until after the original pointer is gone
testNumber1 = nil;
[theInvocation invoke];
theInvocation = nil;
NSLog(#"Array count after invocation: %ld", [testArray count]);
testArray = nil;
due to the overrelease of testNumber1, because the temporary __unsafe_unretained tempNumber variable is not holding on to it after the original pointer is set to nil (simulating a case where the invocation is used after the original reference to an argument has gone away). If the -retainArguments line is uncommented (causing the NSInvocation to hold on to the argument), this code does not crash.
The exact same crash happens if I use testNumber1 directly as an argument to -setArgument:, and it's also fixed if you use -retainArguments. Why, then, does the ARC migrator say that using a strongly held pointer as an argument to NSInvocation's -setArgument: is unsafe, unless you use something that is __unsafe_unretained?
This is a complete guess, but might it be something to do with the argument being passed in by reference as a void*?
In the case you've mentioned, this doesn't really seem a problem, but if you were to call, eg. getArgument:atIndex: then the compiler wouldn't have any way of knowing whether the returned argument needed to be retained.
From NSInvocation.h:
- (void)getArgument:(void *)argumentLocation atIndex:(NSInteger)idx;
- (void)setArgument:(void *)argumentLocation atIndex:(NSInteger)idx;
Given that the compiler doesn't know whether the method will return by reference or not (these two method declarations have identical types and attributes), perhaps the migrator is being (sensibly) cautious and telling you to avoid void pointers to strong pointers?
Eg:
NSDecimalNumber* val;
[anInvocation getArgument:&val atIndex:2];
anInvocation = nil;
NSLog(#"%#", val); // kaboom!
__unsafe_unretained NSDecimalNumber* tempVal;
[anInvocation getArgument:&tempVal atIndex:2];
NSDecimalNumber* val = tempVal;
anInvocation = nil;
NSLog(#"%#", val); // fine
An NSInvocation by default does not retain or copy given arguments for efficiency, so each object passed as argument must still live when the invocation is invoked. That means the pointers passed to -setArgument:atIndex: are handled as __unsafe_unretained.
The two lines of MRR code you posted got away with this: testNumber1 was never released. That would have lead to a memory leak, but would have worked. In ARC though, testNumber1 will be released anywhere between its last use and the end of the block in which it is defined, so it will be deallocated. By migrating to ARC, the code may crash, so the ARC migration tool prevents you from migrating:
NSInvocation's setArgument is not safe to be used with an object with
ownership other than __unsafe_unretained
Simply passing the pointer as __unsafe_unretained won't fix the problem, you have to make sure that the argument is still around when the invocation gets called. One way to do this is call -retainArguments as you did (or even better: directly after creating the NSInvocation). Then the invocation retains all its arguments, and so it keeps everything needed for being invoked around. That may be not as efficient, but it's definitely preferable to a crash ;)
Why is it preventing you from doing this? It seems just as bad to use __unsafe_unretained objects as arguments.
The error message could be improved but the migrator is not saying that __unsafe_unretained objects are safe to be used with NSInvocation (there's nothing safe with __unsafe_unretained, it is in the name). The purpose of the error is to get your attention that passing strong/weak objects to that API is not safe, your code can blow up at runtime, and you should check the code to make sure it won't.
By using __unsafe_unretained you are basically introducing explicit unsafe points in your code where you are taking control and responsibility of what happens. It is good hygiene to make these unsafe points visible in the code when dealing with NSInvocation, instead of being under the illusion that ARC will correctly handle things with that API.
Throwing in my complete guess here.
This is likely directly related to retainArguments existing at all on the invocation. In general all methods describe how they will handle any arguments sent to them with annotations directly in the parameter. That can't work in the NSInvocation case because the runtime doesn't know what the invocation will do with the parameter. ARC's purpose is to do its best to guarantee no leaks, without these annotations it is on the programmer to verify there isn't a leak. By forcing you to use __unsafe_unretained its forcing you to do this.
I would chalk this up to one of the quirks with ARC (others include some things not supporting weak references).
The important thing here is the standard behaviour of NSInvocation:
By default, arguments are not retained and C string arguments are not being copied. Therefore under ARC your code can behave as follows:
// Creating the testNumber
NSDecimalNumber *testNumber1 = [[NSDecimalNumber alloc] initWithString:#"1.0"];
// Set the number as argument
[theInvocation setArgument:&testNumber1 atIndex:2];
// At this point ARC can/will deallocate testNumber1,
// since NSInvocation does not retain the argument
// and we don't reference testNumber1 anymore
// Calling the retainArguments method happens too late.
[theInvocation retainArguments];
// This will most likely result in a bad access since the invocation references an invalid pointer or nil
[theInvocation invoke];
Therefore the migrator tells you:
At this point you have to explicitly ensure that your object is being retained long enough. Therefore create an unsafe_unretained variable (where you have to keep in mind that ARC won't manage it for you).
According to Apple Doc NSInvocation:
This class does not retain the arguments for the contained invocation by default. If those objects might disappear between the time you create your instance of NSInvocation and the time you use it, you should explicitly retain the objects yourself or invoke the retainArguments method to have the invocation object retain them itself.

Block gets released whilst in NSDictionary (ARC)

I'm trying to retain a reference to a Block that's been passed in to my class by a method, to call at a later time. I'm having trouble, however, maintaining a reference to it.
The obvious way, I thought, was to add it to an ivar collection, all of which are supposed to maintain strong references to their contents. But when I try to pull it back out, it's nil.
The code is pretty simple:
typedef void (^DataControllerCallback)(id rslt);
#interface DataController : NSObject {
NSMutableArray* queue;
}
- (void) addBlock:(DataControllerCallback)callback;
- (void) functionToBeCalledLater;
#end
#implementation DataController
- (id) init {
self = [super init];
if (self != nil) {
queue = [NSMutableArray new];
}
return self;
}
- (void) addBlock:(DataControllerCallback)callback {
NSDictionary* toAdd = [NSDictionary dictionaryWithObjectsAndKeys:
[callback copy], #"callback",
#"some other data", #"data", nil];
[queue addObject:toAdd];
}
- (void) functionToBeCalledLater {
NSDictionary* dict = [queue lastObject];
NSLog(#"%#", [dict objectForKey:#"data"]; //works
DataControllerCallback callback = [dict objectForKey:#"callback"]; //this is nil
callback(#"an arguemnt"); //EXC_BAD_ACCESS
}
What's happening?
Update: I've tried it with [callback copy] and just callback inserting into the dictionary, neither works.
Update 2: If I just stick my block into an NSMutableSet, as long as I call copy, I'm fine. It works great. But if it's in an NSDictionary, it doesn't.
I've actually tested it by putting a breakpoint right after the NSDict is created and the callback never gets inserted. The description reads clearly "1 key-value pair", not two.
I'm currently getting around this with a specialised class that just acts as a container. The callback property is declared as strong; I don't even need to use copy.
The question still stands, though: why is this happening? Why won't an NSDictionary store a Block? Does it have something to do with the fact that I'm targeting iOS 4.3 and thus ARC must be built in as a static library?
Update 3: Ladies and gentleman: I am an idiot.
The code I presented here was obviously a simplified version of the actual code; most particularly, it was leaving some key/value pairs out of the dictionary.
If you're storing a value in an NSDictionary using [NSDictionary dictionaryWithObjectsAndKeys:], you had better be damn sure one of those values isn't nil.
One of them was.
ICYMI, it was causing an early termination of the argument list. I had a userInfo-type argument being passed into one of the "add to queue" methods, and you could, of course, pass in "nil". Then when I constructed the dictionary, chucking in that argument caused the constructor to think I had terminated the argument list. #"callback" was the last value in the dictionary constructor and it was never being stored.
Contrary to popular mis-conception, ARC does not automatically de-stackify Blocks passed as arguments to methods. It only de-stackify's automatically when a block is returned from a method/function.
I.e. this....
[dict setObject: ^{;} forKey: #"boom"];
... will crash if dict survives beyond the scope and you attempt to use the block (actually, it won't in this case because that is a static block, but that is a compiler detail that you can't rely on).
This is documented here:
How do blocks work in ARC?
Blocks “just work” when you pass blocks up the stack in ARC mode, such
as in a return. You don’t have to call Block Copy any more. You
still need to use [^{} copy] when passing “down” the stack into
arrayWithObjects: and other methods that do a retain.
The return value behavior could be automated because it is always correct to return a heap based block (and always an error to return a stack based block). In the case of a block-as-an-argument, it is impossible to automate the behavior in a way that would be both very efficient and always correct.
The analyzer likely should have warned about this use. If it didn't, file a bug.
(I derped a stack when I meant a heap. Sorry about that.)
The compiler doesn't automate blocks-as-parameters for a few reasons:
unnecessarily copying a block to the heap can be a significant performance penalty
multiple-copies of a block can multiply that performance penalty significantly.
I.e.:
doSomethingSynchronous(aBlock);
doSomethingSynchronous(aBlock);
doSomethingSynchronous(aBlock);
doSomethingSynchronous(aBlock);
If that were to imply four Block_copy() operations and aBlock contained a significant quantity of captured state, that'd be a huge potential hit.
• There are only so many hours in the day and automating the handling of parameters is rife with non-obvious edge cases. If this were handled automatically in the future, it could be done without breaking existing code and, thus, maybe it will be done in the future.
I.e. the compiler could generate:
aBlock = [aBlock copy];
doSomethingSynchronous(aBlock);
doSomethingSynchronous(aBlock);
doSomethingSynchronous(aBlock);
doSomethingSynchronous(aBlock);
[aBlock release];
Not only would this fix the problem of a block-as-param, but it would also only produce one copy of the block across all potential uses.
The question still stands, though: why is this happening? Why won't an
NSDictionary store a Block? Does it have something to do with the fact
that I'm targeting iOS 4.3 and thus ARC must be built in as a static
library?
Something bizarre is going on, then. Coincidentally, I've been using blocks-as-values in an ARC based application in the last week and it is working fine.
Do you have a minimal example handy?