Warning block captures an autoreleasing out-parameter - objective-c

In a third-party lib I use, I am getting the warning
"Block captures an autoreleasing out-parameter"
What is the problem and how can I fix it?
- (BOOL)register:(NSString *)param error:(NSError **)errPtr
{
__block BOOL result = YES;
__block NSError *err = nil;
dispatch_block_t block = ^{ #autoreleasepool {
NSMutableArray *elements = [NSMutableArray array];
/**** Block captures an autoreleasing out-parameter,
which may result in use-after-free bugs ****/
/* on errPtr */
[self registerWithElements:elements error:errPtr];
}};
if (errPtr)
*errPtr = err;
return result;
}

When you have a method with an indirect non-const parameter (T **param) Clang with ARC automatically qualify such a parameter with __autoreleasing (T *__autoreleasing*). This happens because Clang reasonably assumes, that the calling side is not always required to release such an object, so it puts a requirement on the function to assign autoreleasing objects only. Thus this:
- (void)myMethod:(NSObject **)param {
*param = [NSObject new];
}
Turns into this under ARC:
- (void)myMethod:(NSObject *__autoreleasing*)param {
*param = [[NSObject new] autorelease];
}
This in turn imposes special requirements on the arguments for such a method, so in common scenario where you actually just pass some (strongly retained) object to the function:
NSObject *obj;
[self myMethod:&obj];
ARC in fact makes a temporary autoreleasing argument:
NSObject *__strong obj = nil;
NSObject *__autoreleasing tmp = obj;
[self myMethod:&tmp];
obj = [tmp retain];
What is the problem...
If, instead of (indirectly) passing strongly retained pointer, you pass your own indirect pointer, ARC doesn't make any temporary in between:
NSObject *__autoreleasing obj;
NSObject *__autoreleasing *objPtr = &obj;
[self myMethod:objPtr];
It means that the object "returned" by myMethod: doesn't get retained anymore, thus will be destroyed when current autorelease pool is drained. The same is true if you pass a parameter with the same semantic:
- (void)anotherMethod:(NSObject **)param {
[self myMethod:param];
}
Thus if, for any reason, you decide to wrap the invocation of myMethod: with an autorelease block, the code here ends up with a zombie object:
- (void)anotherMethod:(NSObject **)param {
#autoreleasepool {
[self myMethod:param]; // object was created and assigned to a autoreleasing pointer
} // ref. count of the object reached zero. `*param` refers to a released object
}
The same can potentially happen if you wrap the invocation with a block:
- (void)anotherMethod:(NSObject **)param {
void(^block)(void) = ^{
// "Block captures an autoreleasing out-parameter, which may result in use-after-free bugs" warning appears
[self myMethod:param];
};
block();
}
For this specific implementation no problem will happen, and you could just silence the error by explicitly giving the indirect pointer __autoreleasing qualifier (by which you inform Clang that you are well aware of possible consequences):
- (void)anotherMethod:(NSObject *__autoreleasing*)param {
void(^block)(void) = ^{
[self myMethod:param];
};
block();
}
But now you has to be very careful, because block is a first-party object, which can be retained and called from anywhere and there are countless scenarios where additional autorelease pool is spawned. E.g. this code will case the same zombie-object error:
- (void)anotherMethod:(NSObject *__autoreleasing*)param {
void(^block)(void) = ^{
[self myMethod:param];
};
... some code here ...
#autoreleasepool {
block();
}
}
The same if the autorelease pool is right in the block body:
- (void)anotherMethod:(NSObject **)param {
void(^block)(void) = ^{
#autoreleasepool {
[self myMethod:param];
}
};
block();
}
Having that said, Clangs doesn't warn about the error (it's actually obvious in your case, because you wrap the body of your block with an #autoreleasepool block), it just wants you to double check that you are aware of possible problems (as you can see, it's still possible to implement things like that, but you will have hard time to track all the errors if they appear).
how can I fix it?
This depends on your definition of "fix". You either can remove the autorelease pool block from the body of your block and qualify __autoreleasing parameter explicitly (provided it's merely called in the same thread somewhere in the method):
- (BOOL)register:(NSString *)param error:(NSError *__autoreleasing*)errPtr {
....
dispatch_block_t block = ^{
....
[self registerWithElements:elements error:errPtr];
};
block();
....
}
Or you can introduce another "local" variable to capture and pass it inside a block:
- (BOOL)register:(NSString *)param error:(NSError **)errPtr {
....
__block NSError *err;
dispatch_block_t block = ^{
#autoreleasepool {
....
[self registerWithElements:elements error:&err];
}
};
block();
*errPtr = err;
....
}
This again implies that the block is called synchronously in the method, but not necessarily within the same autorelease pool block. If you want to store the block for later use, or call it asynchronously, then you will need another NSError variable with prolonged lifetime to capture inside the block.

Related

Assigning a variable inside of a block requires weak reference?

So I have the following code:
[serviceHandler getHomeConfigurationData:^(NSDictionary *data){
if (data) {
NSLog(#"The Data: %#", data);
homeConfigData = data;
}
} failure:^(NSError *error, BaseServiceHandler *context){
homeConfigData = Nil;
NSLog(#"Error: %#", error);
}];
in here, homeConfigData is an instance variable in my ViewController. This entire method is done on a background thread. It's possible that the ViewController is deallocated once the completion block returns / is fired. So I'm worried if I'll get an issue when I do the line homeConfigData = data;
How would I handle this?
It's possible that the ViewController is deallocated once the
completion block returns / is fired.
Actually, as it is written now, it is not possible that the ViewController is deallocated by the time the block runs, because both of those blocks retain self as self is used in both blocks (because the instance variable homeConfigData is used, which implicitly means self->homeConfigData).
Like this:
YourClass *__weak weakSelf = self;
[serviceHandler getHomeConfigurationData:^(NSDictionary *data){
if (data) {
NSLog(#"The Data: %#", data);
weakSelf.homeConfigData = data;
}
} failure:^(NSError *error, BaseServiceHandler *context){
weakSelf.homeConfigData = Nil;
NSLog(#"Error: %#", error);
}];
If all of the strong pointers to self become nil, the instance will be deallocated and all the weak pointers, i.e. weakSelf, will become nil. Sending the setHomeConfigData selector to nil will be a noop.

Clarifications needed for a crash using NSArray, blocks and Manual Reference Counting

I need some clarifications on a crash I'm encountering using NSArray, blocks and Manual Reference Counting. My goal is to store blocks on a collection (NSArray in this case) in order to reuse them in the future.
I've setup a small sample to replicate the issue. In particular, I have a class Item that looks like the following:
#import <Foundation/Foundation.h>
typedef void(^MyBlock)();
#interface Item : NSObject
- (instancetype)initWithBlocks:(NSArray*)blocks;
#end
#import "Item.h"
#interface Item ()
#property (nonatomic, strong) NSArray *blocks;
#end
#implementation Item
- (instancetype)initWithBlocks:(NSArray*)blocks
{
self = [super init];
if (self) {
NSMutableArray *temp = [NSMutableArray array];
for (MyBlock block in blocks) {
[temp addObject:[[block copy] autorelease]];
}
_blocks = [temp copy];
}
return self;
}
The usage is described below (I'm using in the app delegate).
- (void)applicationDidFinishLaunching:(NSNotification *)aNotification {
__block typeof(self) weakSelf = self;
MyBlock myBlock1 = ^() {
[weakSelf doSomething1];
};
MyBlock myBlock2 = ^() {
[weakSelf doSomething1];
};
NSArray *blocks = #[myBlock1, myBlock2];
// As MartinR suggested the code crashes even
// if the following line is commented
Item *item = [[Item alloc] initWithBlocks:blocks];
}
If I run the app, it crashes with an EXC_BAD_INSTRUCTION (note that I've already enabled All Exceptions breakpoints). In particular, the app stops in the main.
int main(int argc, const char * argv[]) {
return NSApplicationMain(argc, argv);
}
Note: As suggested by Ken Thomases, if you use bt command on llvm console, you are to see the back trace. In this case it shows the following:
-[__NSArrayI dealloc]
If I comment the [weakSelf doSomethingX]; it works without crashes (it does not mean that is correct).
Modifying the code a little bit like the following, all runs ok.
// Item does not do anymore the copy/autorelease dance
// since used in the declaration of the blocks
- (instancetype)initWithBlocks:(NSArray*)blocks
{
self = [super init];
if (self) {
_blocks = [blocks retain];
}
return self;
}
and
__block typeof(self) weakSelf = self;
MyBlock myBlock1 = [[^() {
[weakSelf doSomething1];
} copy] autorelease];
MyBlock myBlock2 = [[^() {
[weakSelf doSomething1];
} copy] autorelease];
NSArray *blocks = #[myBlock1, myBlock2];
Item *item = [[Item alloc] initWithBlocks:blocks];
What is the point here? I think I'm missing something but I don't know what.
Update 1
Ok. I'll try to recap my thoughts based on the comments with #Martin R and #Ken Thomases.
A block, by default, is created on stack if a copy message is not sent to it (ARC does this for us) in order to move it on the heap. So, the situation in this case is the following. I create an autorelease array and I add two blocks where retain is called in a implicit manner. When the applicationDidFinishLaunching method finishes is execution, the blocks, since created on the stack (they are automatic variables) disappear. In a later moment, the array called blocks will be released since has been marked as autorelease. So, it will crash since it will send a release object to blocks that do not exist anymore.
So, my question is the following: What does it mean to send a retain message to a block that is on the stack? Why the array is the source of the crash (see the back trace)? In other words, since a block is on the stack, will it bump the retain count of it? And when it goes out of scope? In addiction, why if I comment the [weakSelf doSomething1] line the code works without problems? Not very clear to me this part.
You are sticking an object from the stack into an autoreleased array. BOOM ensues.
Consider:
typedef void(^MyBlock)();
int main(int argc, char *argv[]) {
#autoreleasepool {
NSObject *o = [NSObject new];
MyBlock myBlock1 = ^() {
[o doSomething1];
};
NSLog(#"o %p", o);
NSLog(#"b %p", myBlock1);
NSLog(#"b retain %p", [myBlock1 retain]);
NSLog(#"b copy %p", [myBlock1 copy]);
NSLog(#"s %p", ^{});
sleep(1000000);
}
}
Compiled/run as -i386 (because the #s are smaller and more obvious):
a.out[11729:555819] o 0x7b6510f0
a.out[11729:555819] b 0xbff2dc30
a.out[11729:555819] b retain 0xbff2dc30
a.out[11729:555819] b copy 0x7b6511a0
a.out[11748:572916] s 0x67048
Since the object is at 0x7b, we can assume that is the heap. 0xb is really high memory and, thus, the stack.
The retain doesn't cause a copy (because doing so would have invariably led to leaks) and retain on a stack based object is meaningless.
If you change the [o doSomething1]; to [nil doSomething1]; then that becomes a static block and that lives in readonly mapped memory (readonly-executable pages from the mach-o's TEXT segment) and, thus, there is no allocation to deallocate and retain/release/autorelease are no-ops.
As you can see, the static block ended up around 0x67048 (this number may change from run to run, btw, for a variety of reasons. Low in memory.
In fact, because of the sleep(), we can run vmmap against the a.out process and see:
==== Writable regions for process 11772
REGION TYPE START - END [ VSIZE] PRT/MAX SHRMOD REGION DETAIL
__DATA 00067000-00068000 [ 4K] rw-/rwx SM=ZER /tmp/a.out
That is, the static block was in the first 4K segment of mapped writable regions from the mach-o file. Note that this doesn't mean the code is in that writable region (SECURITY HOLE if it were). The code is in the TEXT segment mapped into the readable regions.

Objective-c block lifetime ARC

I'm confused about the lifetime of a block under ARC. I've written a unit test to demonstrate what is confusing me.
- (void)testBlock {
NSObject *testObject = [[NSObject alloc] init];
CompletionBlock testBlock = ^{ NSLog(#"%#", testObject); };
XCTAssertNotNil(testObject, #"testObject should not be nil");
__weak NSObject *weakTestObject = testObject;
#autoreleasepool {
testObject = nil;
}
XCTAssertNotNil(weakTestObject, #"testObject should be retained by testBlock");
#autoreleasepool {
testBlock = nil;
}
//THIS IS THE FAILING TEST CASE
XCTAssertNil(weakTestObject, #"testObject should have been released when testBlock was released");
}
I'm guessing that this behavior has something to do with how blocks are stored on the stack/heap.
Update!
Setting the block to nil doesn't release the block as I expected because it is on the stack and will not be released until it goes out of scope. Forcing the block to go out of scope fixes my test. Updated code below.
- (void)testBlock {
NSObject *testObject = [[NSObject alloc] init];
__weak NSObject *weakTestObject = testObject;
#autoreleasepool {
CompletionBlock testBlock = ^{ NSLog(#"%#", testObject); };
XCTAssertNotNil(testBlock, #"testBlock should not be nil");
XCTAssertNotNil(testObject, #"testObject should not be nil");
testObject = nil;
XCTAssertNotNil(weakTestObject, #"testObject should be retained by testBlock");
//testBlock goes out of scope here
}
XCTAssertNil(weakTestObject, #"testObject should have been released when testBlock was released");
}
Blocks are created on stack, and only become destroyed when scope exits, pretty much like C++ stack-allocated objects with destructors. These blocks are exempt from reference counting and will ignore retain/release messages. Only after being copied (via Block_copy() function or copy message) they become normal heap-allocated objects that can be retained and released.
In your example, the assert shall start working if you wrap all the code before it in extra curly brackets { }, so that the assert executes after block variable's scope ends.

Manual retain with ARC

Before ARC I had the following code that retains the delegate while an async operation is in progress:
- (void)startAsyncWork
{
[_delegate retain];
// calls executeAsyncWork asynchronously
}
- (void)executeAsyncWork
{
// when finished, calls stopAsyncWork
}
- (void)stopAsyncWork
{
[_delegate release];
}
What is the equivalent to this pattern with ARC?
I have occasionally needed to manually retain and release things (sometimes just for debugging) and came up with the following macros:
#define AntiARCRetain(...) void *retainedThing = (__bridge_retained void *)__VA_ARGS__; retainedThing = retainedThing
#define AntiARCRelease(...) void *retainedThing = (__bridge void *) __VA_ARGS__; id unretainedThing = (__bridge_transfer id)retainedThing; unretainedThing = nil
This works by using the __bridge_retained and __bridge_transfer to cast things to and from (void *) which causes things to be retained, or to create a strong reference without calling retain.
Have fun, but be careful!
Why not just assign your delegate object to a strong ivar for the duration of the asynchronous task?
Or have a local variable in executeAsyncWork
- (void)executeAsyncWork
{
id localCopy = _delegate;
if (localCopy != nil) // since this method is async, the delegate might have gone
{
// do work on local copy
}
}
Something like this:
- (void)startAsyncWork
{
id<YourProtocol> delegate = _delegate;
dispatch_async(/* some queue */, ^{
// do work
[delegate doSomething];
}
}
The block will retain the delegate as long as needed...

Objective-C Singletons and LLVM/clang leak warnings

I'm using the singleton pattern in several places in an application, and I'm getting memory leak errors from clang when analyzing the code.
static MyClass *_sharedMyClass;
+ (MyClass *)sharedMyClass {
#synchronized(self) {
if (_sharedMyClass == nil)
[[self alloc] init];
}
return _sharedMyClass;
}
// clang error: Object allocated on line 5 is no longer referenced after this point and has a retain count of +1 (object leaked)
I'm using these settings for scan-build:
scan-build -v -v -v -V -k xcodebuild
I'm fairly certain that the code in the singleton is just fine - after all, it's the same code referenced here on Stack Overflow as well as in Apple's documentation - but I would like to get the memory leak warning sorted out so my scan-build returns success.
I may be being exceptionally dense, but surely your line 5
[[self alloc] init];
allocates an object of the containing class type, and promptly throws it away? Do you not want
_sharedMyClass = [[self alloc] init];
?
Apple has since updated their recommended singleton code to pass the static analyzer:
+ (MyGizmoClass*)sharedManager
{
if (sharedGizmoManager == nil) {
sharedGizmoManager = [[super allocWithZone:NULL] init];
}
return sharedGizmoManager;
}
+ (id)allocWithZone:(NSZone *)zone
{
return [[self sharedManager] retain];
}
Now +sharedManager calls super's -allocWithZone: and assigns the return of -init, and the singleton's -allocWithZone: just returns a retained sharedInstance.
Edit:
Why the retain in +allocWithZone:?
+allocWithZone: is overridden because someone using MyGizmoClass could circumvent the singleton by calling [[MyGizmoClass alloc] init] instead of [MyGizmoClass sharedManager]. It's retained because +alloc is expected to always return an object with a retain count of +1.
Every call to +alloc should be balanced with a -release or -autorelease, so without the retain in +allocWithZone:, the shared instance could potentially be deallocated out from under other users.
You may be interested in a simple, one-method, GCD-based singleton implementation (and thus 10.6+ only) posted on Mike Ash's site:
+ (id)sharedFoo
{
static dispatch_once_t pred;
static Foo *foo = nil;
dispatch_once(&pred, ^{ foo = [[self alloc] init]; });
return foo;
}
You are referencing self in a class method! Big no-no! Secondly, you are calling [[self alloc] init] and just throwing away the instance. You should assign the singleton reference in the class method, and not in init like I am guessing you are doing. Next, there is no real guarantee that _sharedMyClass will be initialized to zero. You should explicitly initialize it to nil.
static MyClass *_sharedMyClass = nil;
+ (MyClass *)sharedMyClass {
#synchronized(self) {
if (_sharedMyClass == nil)
_sharedMyClass = [[MyClass alloc] init];
}
return _sharedMyClass;
}
You also probably had this in there too...
+ (id)allocWithZone:(NSZone *)zone {
#synchronized(self) {
if (sharedInstance == nil) {
sharedInstance = [super allocWithZone:zone];
return sharedInstance; // assignment and return on first allocation
}
}
return nil; // on subsequent allocation attempts return nil
}
The reason you weren't storing it in init is because you were storing it in the method that alloc called. This is the pattern Apple has in their examples. If you save the value in your init as well, all is fine and the warning goes away. I'd leave the allocWithZone implementation alone.