EXC_BAD_ACCESS from setting pass-by-writeback error within enumerateObjectsUsingBlock - ios

The following code causes an EXC_BAD_ACCESS upon attempting to set *error.
- (void)triggerEXC_BAD_ACCESS
{
NSError *error = nil;
[self doSetErrorInBlock:&error];
}
- (void)doSetErrorInBlock:(NSError * __autoreleasing *)error
{
[#[#(0)] enumerateObjectsUsingBlock:^(id _Nonnull obj, NSUInteger idx, BOOL * _Nonnull stop) {
*error = [NSError errorWithDomain:#"some.domain" code:100 userInfo:nil]; // <--- causes EXC_BAD_ACCESS
}];
}
However, I'm not sure why the EXC_BAD_ACCESS is occurring.
Replacing the enumerateObjectsUsingBlock: call with the following function, which tries to reproduce the function signature of enumerateObjectsUsingBlock:, will let the function triggerEXC_BAD_ACCESS run without error:
- (void)doSetErrorInBlock:(NSError * __autoreleasing *)error
{
[self runABlock:^(id someObject, NSUInteger idx, BOOL *anotherWriteback) {
*error = [NSError errorWithDomain:#"some.domain" code:100 userInfo:nil]; // <--- No crash here
}];
}
- (void)runABlock:(void (NS_NOESCAPE ^)(id obj, NSUInteger idx, BOOL *stop))block
{
BOOL anotherWriteback = NO;
block(#"Some string", 0, &anotherWriteback);
}
Not sure if I'm missing anything about how ARC works here, or if it's specific to the version of Xcode that I'm using (Xcode 12.2).

I couldn't reproduce a crash in -doSetErrorInBlock:, but I can reproduce a crash in -triggerEXC_BAD_ACCESS with "-[NSError retain]: message sent to deallocated instance" in debug node (I am not sure if it's due to NSZombie or some other debug options).
The reason for it is that *error in -doSetErrorInBlock: has type NSError * __autoreleasing, and the implementation of -[NSArray enumerateObjectsUsingBlock:] (which is closed-source but it's possible to examine the assembly) happens to internally have an autorelease pool around the execution of the block. An object pointer which is __autoreleasing means that we don't retain it, and we assume it is alive by being retained by some autorelease pool. That means it is bad to assign something to an __autoreleasing variable inside an autorelease pool, and then try to access it after the autorelease pool ended, because the end of the autorelease pool could have deallocated it, so you can be left with a dangling pointer. This section of the ARC spec says:
It is undefined behavior if a non-null pointer is assigned to an
__autoreleasing object while an autorelease pool is in scope and
then that object is read after the autorelease pool’s scope is left.
The reason why the crash message says it is trying to retain it, is because of what happens when you try to pass a "pointer to __strong" (e.g. &error in -triggerEXC_BAD_ACCESS) to a parameter of type "pointer to __autoreleasing" (e.g. the parameter of -doSetErrorInBlock:). As you can see from this section of the ARC spec, a "pass-by-writeback" process happens, where they create a temporary variable of type __autoreleasing, assign the value of the __strong variable to it, make the call, and then assign the value of the __autoreleasing variable back to the __strong variable, so your triggerEXC_BAD_ACCESS method is really kind of like this:
NSError *error = nil;
NSError * __autoreleasing temporary = error;
[self doSetErrorInBlock:&temporary];
error = temporary;
The last step of assigning the value back to the __strong variable performs a retain, and that's when it encounters the deallocated instance.
I could reproduce the same crash in your second example if I change the -runABlock: to:
- (void)runABlock:(void (NS_NOESCAPE ^)(id obj, NSUInteger idx, BOOL *stop))block
{
BOOL anotherWriteback = NO;
#autoreleasepool {
block(#"Some string", 0, &anotherWriteback);
}
}
You shouldn't really use __autoreleasing in a new method that you write. __strong is much better because the strong reference makes sure you don't accidentally have dangling references and problems like that. The main reason why __autoreleasing exists is because back in manual reference counting days, there were no explicit ownership qualifiers, and the "convention" was that retain counts were not transferred into or out of methods, and so objects returned from a method (including objects returned by pointer using an out-parameter) would be autoreleased rather than retained. (And those methods would be responsible for ensuring that the object is still valid when the method returns.) And since your program can be used on different OS versions, they cannot change the behavior of APIs in new OS versions, so they are stuck with this "pointer to __autoreleasing" type. However, in a method that you yourself write in ARC (which does have explicit ownership qualifiers), which is only going to be called by your own ARC code, by all means use __strong. If you wrote your method using __strong, it would not crash (by default pointer-to-object-pointers are interpreted as __autoreleasing, so you have to specify __strong explicitly):
- (void)doSetErrorInBlock:(NSError * __strong *)error
{
[#[#(0)] enumerateObjectsUsingBlock:^(id _Nonnull obj, NSUInteger idx, BOOL * _Nonnull stop) {
*error = [NSError errorWithDomain:#"some.domain" code:100 userInfo:nil];
}];
}
If you for some reason insist on taking a parameter of type NSError * __autoreleasing *, and want to do the same thing you were doing but safely, you should be using a __strong variable for the block, and only assign it into the __autoreleasing at the end:
- (void)doSetErrorInBlock:(NSError * __autoreleasing *)error
{
__block NSError *result;
[#[#(0)] enumerateObjectsUsingBlock:^(id _Nonnull obj, NSUInteger idx, BOOL * _Nonnull stop) {
result = [NSError errorWithDomain:#"some.domain" code:100 userInfo:nil];
}];
*error = result;
}

Related

Pass by Reference in Callbacks

I am facing a problem. I am passing an object to another class by its reference & setting the value in that object. Now when I access this variable in callback handler then It is nil.
My sample code is:
Class A:
__block NSString *getListJobId = nil;
ClassB *bobject = [[ClassB alloc]init];
[bobject getItemsWithJobId:&getListJobId onSuccess:^(NSArray *response) {
NSLog(#"job id %#",getListJobId); //It is nil, It should be **shiv**
} onFailure:^(NSError *error) {
}];
Class B:
.h
- (void)getItemsWithJobId:(NSString **)jobId onSuccess:(void (^)(NSArray *))completedBlock onFailure:(void (^)(NSError *))failureBlock;
.m
- (void)getItemsWithJobId:(NSString **)jobId onSuccess:(void (^)(NSArray *))completedBlock onFailure:(void (^)(NSError *))failureBlock
{
*jobId = #"shiv";
completedBlock([NSArray new]);
}
I am getting this jobId nil in class A in callback response. How can I get this value from class B to class A.
I will appreciate your help.
You should not pass by reference to get an updated value in the method, because the getListJobId at ClassA and ClassB do not point same address.
An Obj-C block capture the value of variables outside of its enclosing scope.
See "Blocks Can Capture Values from the Enclosing Scope" section.
https://developer.apple.com/library/ios/documentation/Cocoa/Conceptual/ProgrammingWithObjectiveC/WorkingwithBlocks/WorkingwithBlocks.html
Instead of passing by reference, we can get the updated value from arguments of the block and update getListJobId in the block.
Class A:
__block NSString *getListJobId = nil;
ClassB *bobject = [[ClassB alloc] init];
[bobject getItemsWithJobId:getListJobId onSuccess:^(NSArray *response, NSString *updatedJobId) {
getListJobId = updatedJobId;
NSLog(#"job id %#", getListJobId); // job id **shiv**
} onFailure:^(NSError *error) {
}];
Class B: .h
- (void)getItemsWithJobId:(NSString *)jobId onSuccess:(void (^)(NSArray *, NSString *))completedBlock onFailure:(void (^)(NSError *))failureBlock;
.m
- (void)getItemsWithJobId:(NSString *)jobId onSuccess:(void (^)(NSArray *, NSString *))completedBlock onFailure:(void (^)(NSError *))failureBlock
{
NSString *updatedJobId = #"**shiv**";
completedBlock([NSArray new], updatedJobId);
}
Taking the address of a __block variable does not always do what you expect.
In the current implementation, __block variables are initially allocated on the stack, and then "moved" to the heap upon any of the blocks that use it being moved to the heap (which is caused by the block being copied).
Therefore, the address of a __block variable changes over its lifetime. If you take the address of it, and it moves, then you will no longer be pointing to the version of the variable that everyone else is using.
Here, what is happening is that you take the address of the __block variable getListJobId while it is still on the stack. It is still on the stack at that point because it is caused to be moved to the heap by the copying of any block that uses it, but no block has been created at the point yet.
Then, a block that uses getListJobId gets copied somewhere and getListJobId gets moved to the heap. Exactly where this happens is not very clear, because ARC is allowed to insert copies of blocks in various places. Plus, the code you are showing here does not seem like your real code, because there would be no point to calling a "completion block" at the end of a method synchronously (in that case you would just return and let the caller perform the operations they want when completed). Rather, your real code probably performs an asynchronous operation, at the end of which the completion handler is called. dispatch_async and related asynchronous functions copy the blocks passed to them (which in turn copy any blocks captured, and so on).
I am guessing that in your real code, both the *jobId = #"shiv"; line and the calling of the completion block happen in the asynchronous operation. What is happening is that the creation of the asynchronous operation copies the block and causes getListJobId to be moved to the heap. So inside the asynchronous operation, getListJobId refers to the heap version of the variable. However, the *jobId = #"shiv"; writes to the stack version of the variable, because jobId is a pointer taken from the address of the variable when it was still on the stack. So you are writing to and reading from different variables.
Furthermore, what you are doing in *jobId = #"shiv"; is very dangerous, because by the time of the asynchronous operation, the stack frame of the original function call no longer exists. And writing to a variable on the stack after the stack frame is gone is undefined behavior, and you may be overwriting other unknown variables in memory. You are lucky it didn't crash.

Why using a point to point type(NSProgress * __autoreleasing *) rather than just a point type (NSProgress * __autoreleasing) in AFNetworking?

In AFNetworking I find this function:
- (NSURLSessionUploadTask *)uploadTaskWithRequest:(NSURLRequest *)request
fromFile:(NSURL *)fileURL
progress:(NSProgress * __autoreleasing *)progress
completionHandler:(void (^)(NSURLResponse *response, id responseObject, NSError *error))completionHandler;
Here the progress type is NSProgress * __autoreleasing *.
I do not why here a point to point type is used rather than just a point type. The usage of progress parameter is as follows in this function:
if (progress) {
*progress = delegate.uploadProgress;
}
In my mind, if declare:
NSProgress *progress = nil;
passing:
progress:(NSProgress * __autoreleasing *)progress
and use it as:
*progress = delegate.uploadProgress;
is just the same as passing
progress:(__autoreleasing NSProgress *)progress
and use it as:
progress = delegate.uploadProgress;
Could any one help explain why a point to point type is used here?
The purpose of that parameter is to let the method pass back a pointer to an NSProgress object. To do that, the method needs to assign into the caller's variable.
Functions receive a copy of the passed value. If the parameter were just __autoreleasing NSProgress*, then the function would receive a copy of the passed pointer. Both the caller and the method would have variables containing a pointer to an NSProgress object, but they would be separate variables. When the method assigned to its variable using progress = delegate.uploadProgress; it would only change its copy. The assignment would not affect the caller's variable.
When the parameter is NSProgress * __autoreleasing * and the caller passes &callersProgress, the function receives a copy of a pointer to the caller's variable. When the method uses *progress (as in *progress = delegate.uploadProgress;), it dereferences that pointer. That yields a reference to the caller's variable. So, the method is assigning to the caller's variable, not just a local variable.

Is the following iOS code safe? (__autoreleasing semantics)

-(NSData *)jsonRepresentation:(NSError **error)error {
NSDictionary *dict = [self getDictRepresentation];
return [NSJSONSerialization dataWithJSONObject:dict options:nil error:error];
}
// Some other place...
NSError *__autoreleasing error = nil;
NSData *json = [obj jsonRepresentation:&error];
Do autoreleasing semantics safely convey error up the stack to my second code block?
As per the clang ARC spec, a method argument of the form NSError ** (or rather, a pointer to any obj-c object) is implicitly assumed to be NSError * __autoreleasing *. This means that the error will be autoreleased in the method.
As for the call site, if you call it with an __autoreleasing variable, as you have, then everything is just fine. In fact, I recommend this pattern. However, it will still work if you call it with a __strong variable instead. In that case, the compiler will generate an unnamed __autoreleasing temporary, pass the address of that to the method, and then upon return, it will assign the temporary into the __strong. So if you have
NSError *foo;
[bar callMethodWithError:&foo];
the compiler will treat this the same as
NSError *foo;
NSError * __autoreleasing tmp = foo;
[bar callMethodWithError:&tmp];
foo = tmp;
It's actually slightly more complicated than that if the out-param is marked out, but this is generally how it works.
Yes, it's correct, but you should specify this modifier in the method declaration, and not in the declaration of the variable. Even the official documentation of Apple mentions this very situation explicitly:
__autoreleasing is used to denote arguments that are passed by reference (id *) and are autoreleased on return.

ARC: returning allocated object from a method called by performSelector, is it OK?

I'm not sure if i cause a leak here, is it ok to return allocated NSError back to
the calling method by perform selector?
Is it OK to create the NSMutableArray and store it in the same object i got for the callback? and later pass it to the delegate?
The code works fine, but because i'm new to arc i have the fear of doing something wrong.
(i'm using perform selector because my selector is dynamic. just for the example i wrote it statically).
AFHTTPRequestOperation *operation = [self.client HTTPRequestOperationWithRequest:request
success:^(AFHTTPRequestOperation *operation, id responseObject) {
//-----------------Callback--------------------
#pragma clang diagnostic ignored "-Warc-performSelector-leaks"
SEL callback = #selector(getOperationCallback:);
NSError *error = [self performSelector:callback withObject:operation];
//------------------Delegate Call---------------
if(operation.delegate)
[operation.delegate onFinish:operation.requestIdentifier error:error
data:operation.parsedObject];
} failure:^(AFHTTPRequestOperation *operation, NSError *error) {
//------------------Delegate Call---------------
if(operation.delegate)
[operation.delegate onFinish:operation.requestIdentifier error:error data:nil];
}];
- (NSError *)getOperationCallback:(AFHTTPRequestOperation *)operation{
NSArray *rawJson = (NSArray *)operation.jsonObject;
NSError *error;
NSMutableArray *array = [[NSMutableArray alloc] init];
for(id json in rawJson){
MyObject *object = [[MyObject alloc] initWithJson:json];
if(object){
[array addObject:object];
}else{
error = [NSError errorWithDomain:#"myErrors" code:1000 userInfo:nil];
break;
}
}
operation.parsedObject = array;
return error;
}
Generally, performSelector: in ARC could only cause a leak, if the selector you pass to it starts with alloc, new, retain, copy, or mutableCopy.
is it ok to return allocated NSError back to the calling method by perform selector?
Is it OK to create the NSMutableArray and store it in the same object
i got for the callback?
and later pass it to the delegate?
Answer to all questions are OK, nothing wrong with anything. All objects are created in autorelease way.
As long as the return value of the method your are invoking via performSelector:withObject: is an object, it's perfectly ok to do that.
It won't leak since ARC will take care of releasing array and error is autoreleased.
In general, steer clear of performSelector:. The reason being that ARC cannot help you. Even if you think your app works and you've tested that it does, it might break later down the line when you change something.
Sure, if the selector you're calling does not start with alloc, new, copy, mutableCopy, etc then it won't be a problem. But there's cases, such as using __attribute__((ns_returns_retained)) that make it non-obvious that a method might return something retained. In any case, having code that ARC cannot help you out with, is a bad thing.
There's always a way to make it such that you don't have to use performSelector:. Why not make use of a callback block for example?

How to create an NSInvocation object using a method that takes a pointer to an object as an argument

I would like to create an NSInvocation object using a method that takes a pointer to an NSError object as an argument. An example of this would be the method -
- (BOOL)writeToFile:(NSString *)path options:(NSDataWritingOptions)mask error:(NSError **)errorPtr
I undersand that I would set my invocation up like this
NSData *myData = [[NSData alloc] init];
SEL writeToFileSelector = #selector(writeToFile:options:error:);
NSMethodSignature *signature = [NSData instanceMethodSignatureForSelector:writeToFileSelector];
NSInvocation *invocation = [NSInvocation invocationWithMethodSignature:signature];
[invocation setTarget:myData];
[invocation setSelector:writeToFileSelector];
NSString *string = [NSString stringWithFormat:#"long cat"];
NSDataWritingOptions *dataOptions;
*dataOptions = NSDataWritingFileProtectionComplete;
[invocation setArgument:&string atIndex:2];
[invocation setArgument:&dataOptions atIndex:3];
For writeToFile:Options:Error: the last argument is expecting to receive a pointer instead of an object. As a result doing the following does not work -
NSError *err = nil;
[invocation setArgument:&err atIndex:4];
It seems logical that the solution might be to create a pointer to a pointer, but this causes a compiler warning. I am not sure how to execute that properly and not create a memory management problem.
You create the argument just the same as any other argument you'd pass to the method.
As you point out, the method signature wants an NSError ** for its last argument (index 4). So, you will need to declare one, but there's a bit of a gotcha.
NSError **errorPointer
Gives you a variable that points to an NSError variable. But, since you haven't told it to point to any variable, it points to nil. Therefore when you fire the invocation the selector won't be able to change the variable your error pointer points to. In other words, it would be like calling [myData writeToFile:string options:dataOptions error:NULL].
So, you'll want to also declare an NSError variable, and assign its address as the variable your errorPointer should point to:
NSError *error;
NSError **errorPointer = &error;
Now you can pass in the errorPointer as an argument, and you'll be able to inspect it later if there was a problem when you invoked the method. Check out this post on NSInvocation for a little more help (hat tip to Mark Dalrymple for pointing out the blog post)
It is important to also realize that the scope should be considered for the arguments you create and pass into your invocation. Take a look at a similar question I asked here.
The accepted answer by edelaney05 was great, but I think it needs a slight tweak for ARC.
(I can't add comments yet, so creating a new answer to document what I found)
As is, I got the compile error:
"Pointer to non-const type 'NSError *' with no explicit ownership"
I researched this and found that I needed:
NSError * __autoreleasing error = nil;
NSError * __autoreleasing *errorPointer = &error;
References that led me to this answer:
NSInvocation & NSError - __autoreleasing & memory crasher
Automatic Reference Counting: Pointer to non-const type 'NSError *' with no explicit ownership
http://developer.apple.com/library/ios/#releasenotes/ObjectiveC/RN-TransitioningToARC/Introduction/Introduction.html
"__autoreleasing is used to denote arguments that are passed by reference (id *) and are autoreleased on return."
The parameter type is NSError **, which you get from taking the address of an NSError * that you want the error to be written to. To set an argument in an NSInvocation, you need to pass the address of a value of the argument to setArgument:, so you need to put your NSError ** in a variable (I call it errPointer here), and take the address of that (which will be an NSError ***) to pass to setArgument:. You don't need the errPointer variable afterwards.
NSError *err = nil;
NSError **errPointer = &err;
[invocation setArgument:&errPointer atIndex:4];

Resources