Manual retain with ARC - objective-c

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...

Related

Warning block captures an autoreleasing out-parameter

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.

ARC, between block and C callback

I've a c function which takes a callback function as one parameter, I'm going to write an Obj-C wrapper to a standard C API. I would like to replace C callbacks by blocks.
Let's imagine a C API:
void audioStopFunction(void (*callback)(void *), void *udata);
The Obj-C wrapper looks like this:
- (void)myAudioStopFunction:(dispatch_block_t)block
{
void *udata = (__bridge void *)block;
audioStopFunction(my_callback, udata);
}
void my_callback(void *udata)
{
dispatch_block_t block = (__bridge_transfer dispatch_block_t)udata;
block();
}
Now, I have some questions:
In myAudioStopFunction function, do i need to copy the block like below:
void *udata = (__bridge void *)[block copy];
In my_callback function, should i use __bridge instead of __bridge_transfer? And also, do i need to call Block_release after block()?
Will the code cause memory leak? if yes, then what's the correct way?
As the C callback is managed by the wrapper class it's most easy to let the class manage the ownership of the block. (I'm calling the block completionBlock, as this seems to be a bit more in line with Cocoa naming conventions.)
#interface AudioCallbackWrapper
#property (nonatomic) dispatch_block_t completionBlock;
#end
static void my_callback(void *udata)
{
dispatch_block_t block = (__bridge dispatch_block_t)udata;
if (block != nil)
block();
}
#implementation AudioCallbackWrapper
- (void)setCompletionBlock:(dispatch_block_t)completionBlock
{
_completionBlock = [completionBlock copy];
audioStopFunction(my_callback, (__bridge void *)_completionBlock);
}
- (void)dealloc
{
// remove the C callback before completionBlock is released
self.completionBlock = nil;
}
#end
Since the block's lifetime is managed by the enclosing wrapper, the C code never has to transfer ownership. So the code only contains __bridge casts to and from a void *.
Will the code cause memory leak? if yes, then what's the correct way?
Your original code would release the block every time the callback fires. The block pointer would dangle after the first callback.

Objective-C : Synchronizing code

I have a simple class with two ivars, a NSMutableArray and a BOOL. Objects of this class are able to shuffle elements in the array when they are sent the startShuffling message. They do so until they receive the stopShuffling message.
To make it work, the startShuffling method set the boolean to YES, and then dispatch the block of code that shuffles (while(self.isShuffling) { //... } on a concurrent queue. The stopShuffling set the boolean to NO, so that the shuffling process will terminate on the next loop turn.
Here is the interface :
#interface MyClass : NSObject <NSCoding> {
#private
NSMutableArray *elements_;
__block BOOL isShuffling_;
}
#property(readonly) BOOL isShuffling;
-(void)startShuffling;
-(void)stopShuffling;
#end
And the implementation :
#implementation MyClass
#synthesize isShuffling = isShuffling_;
-(void)startShuffling {
if(self.isShuffling) {
return;
}
isShuffling_ = YES;
dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
dispatch_async(queue, ^{
while(isShuffling_) {
// code that shuffles, one element by turn
NSUInteger elementIndex = arc4random() % [elements_ count];
id c = [[elements_ objectAtIndex:elementIndex] retain];
[elements_ removeObjectAtIndex:elementIndex];
[elements_ insertObject:c atIndex:[elements_ count]];
[c release];
}
});
}
-(void)stopShuffling {
isShuffling_ = NO;
}
#end
My class conforms to NSCoding protocol and I don't want to abort encoding even if the object is shuffling. Instead I want my object to stop shuffling and then encode itself. So I have written this encoding method :
-(void)encodeWithCoder:(NSCoder *)aCoder {
if(self.isShuffling) {
[self stopShuffling];
}
[aCoder encodeObject:elements_ forKey:kIVPCodingKeyMyClassElements];
}
Finally, here my question.
I think it is possible for the encodeObject:forKey: method to get called while the shuffling loop terminates its last turn (maybe I'm wrong ?).
Is there any way I can make encodeObject:forKey: method get called after wait for the shuffling loop last turn to terminate ?
Yes, the the shuffle code may still be running when the encodeObject:forKey: method is called.
In general, you don't want to dispatch some random block off onto a queue that executes for a really long time, potentially forever. You want to break up the work into blocks of work. And there-in lies your answer.
Something like:
- (void)shuffleAndCheck
{
if (stillShuffling) {
dispatch_async(globalConcurrentQueue, ^{
dispatch_apply(shuffleQueue, ^{... shuffle one card code ...});
});
dispatch_async(shuffleQueue, ^{ [self shuffleAndCheck]; });
}
}
- (void) startShuffling
{
if (stillShuffling) return;
stillShuffling = YES;
[self shuffleAndCheck];
}
- (void) stopShuffling
{
stillShuffling = NO;
dispatch_async(shuffleQueue, ^{ ... encode stuff here ... });
}
Or something.

Objective-C blocks usage

I have been looking around online, doing research into how to use blocks. I have also decided to set up a basic example to try and understand the way in which they work.
Essentially what I want to do is have a 'block variable' (no sure if thats the correct term) in which I can store a block of code. I then want to be able to set the code in this block at pointX (methodA or methodB) in my code, then run the block of code at pointY (methodX).
So to be specific, my question is 3-fold
Using the example below is the setup / usage of blocks correct and valid?
In methodX how do I execute the code inside the block (self.completionBlock)?
When creating the block in methodA and methodB will the code be called there and then? If so how can I stop this from happening (all I want to do is set up the code in the block to be called later)?
I may have completely misunderstood how blocks are used, apologies if this is the case, however I'm relatively new to Objective-C and I'm trying to learn.
Here is my code so far:
.h
typedef void (^ CompletionBlock)();
#interface TestClass : NSObject
{
CompletionBlock completionBlock;
NSString *stringOfText;
NSString *otherStringOfText;
}
#property(nonatomic, copy)CompletionBlock completionBlock;
#property(nonatomic, retain)NSString *stringOfText;
#property(nonatomic, retain)NSString *otherStringOfText;
- (void)methodA:(NSString *)myText;
- (void)methodB:(NSString *)myText and:(NSString *)myOtherText;
- (void)methodX;
#end
.m
- (void)methodA:(NSString *)myText;
{
if ([self.stringOfText isEqualToString:#""])
{
// Set the variable to be used by the completion block
self.stringOfText = #"I visited methodA"; // normally make use of myText
// Create the completion block
__block TestClass *blocksafeSelf = self;
self.completionBlock = ^()
{
[blocksafeSelf methodA:blocksafeSelf.stringOfText];
blocksafeSelf.stringOfText = nil;
};
}
else
{
// Do some other stuff with self.stringOfText
}
}
- (void)methodB:(NSString *)myText and:(NSString *)myOtherText;
{
if ([self.stringOfText isEqualToString:#""] || [self.otherStringOfText isEqualToString:#""])
{
// Set the variable to be used by the completion block
self.stringOfText = #"I visited methodB"; // normally make use of myText
self.otherStringOfText = #"I also visited methodB"; // normally make use of myOtherText
// Create the completion block
__block TestClass *blocksafeSelf = self;
self.completionBlock = ^()
{
[blocksafeSelf methodB:blocksafeSelf.stringOfText and:blocksafeSelf.otherStringOfText];
blocksafeSelf.stringOfText = nil;
blocksafeSelf.otherStringOfText = nil;
};
}
else
{
// Do some other stuff with self.stringOfText and self.otherStringOfText
}
}
- (void)methodX
{
// At this point run the block of code in self.completionBlock...how?!
}
In my example either methodA or methodB will be called first. Then some time later (perhaps from a different class) methodX will be called (only ever after methodA or methodB have been called).
It's worth noting that the methods methodA, methodB and methodX are all in a singleton class.
NOTE: This is just a dummy example to try and understand the workings of blocks, I'm fully aware there are other ways to achieve the same result.
Here's the code, just to be clear:
- (void)methodX
{
if(self.completionBlock)
self.completionBlock();
}
I think you want to do self.completionBlock(); in methodX.

premature dealloc in ARC based app

I have a problem that seems to be a premature release of an in-use object in an ARC based app. I'm trying to create a folder on an FTP server. The relevant parts of code are below; i'll describe the problem first.
problem with the code is, that the debug output in the
- (void)stream:(NSStream *)aStream handleEvent:(NSStreamEvent)eventCode
method is never called.
Instead, i just get an _EXC_BAD_ACCESS_ error.
While debugging i found out two things:
the error only appears if the following line of code (createDir method) is executed:
[ftpStream open];
if that message isn't sent, the rest of the code doesn't really make sense - but it doesn't crash either...
I tracked the EXC_BAD_ACCESS down with NSZombieEnabled: With zombie objects enabled, the GDB produces the following debugger info:
*** -[FTPUploads respondsToSelector:]: message sent to deallocated instance 0x9166590
The referred address 0x9166590 is the address of my FTPUploads object.
It looks like the streams delegate is deallocated before it can handle messages.
Why does the system deallocate an in-use object? How can i prevent it from being deallocated prematurely?
code:
FTPUploads.h excerpt:
#import <Foundation/Foundation.h>
enum UploadMode {
UploadModeCreateDir,
UploadModeUploadeData
};
#class UploadDatasetVC;
#interface FTPUploads : NSObject<NSStreamDelegate> {
#private
NSString *uploadDir;
NSString *ftpUser;
NSString *ftpPass;
NSString *datasetDir;
NSArray *files;
/* FTP Upload fields */
NSInputStream *fileStream;
NSOutputStream *ftpStream;
// some more fields...
enum UploadMode uploadMode;
UploadDatasetVC *callback;
}
- (id) initWithTimeseriesID: (int) aTimeseriesID
fromDatasetDir: (NSString *) aDir
withFiles: (NSArray *) filesArg
andCallbackObject: (UploadDatasetVC *) aCallback;
- (void) createDir;
#end
FTPUploads.m excerpt
#import "FTPUploads.h"
#import "UploadDatasetVC"
#implementation FTPUploads
- (id) initWithTimeseriesID: (int) aTimeseriesID
fromDatasetDir: (NSString *) aDir
withFiles: (NSArray *) filesArg
andCallbackObject: (UploadDatasetVC *) aCallback {
self = [super init];
if (self) {
uploadDir = [NSString stringWithFormat: #"ftp://aServer.org/%i/", aTimeseriesID];
ftpUser = #"aUser";
ftpPass = #"aPass";
datasetDir = aDir;
files = filesArg;
bufferOffset = 0;
bufferLimit = 0;
index = 0;
callback = aCallback;
}
return self;
}
- (void) createDir {
uploadMode = UploadModeCreateDir;
NSURL *destinationDirURL = [NSURL URLWithString: uploadDir];
CFWriteStreamRef writeStreamRef = CFWriteStreamCreateWithFTPURL(NULL, (__bridge CFURLRef) destinationDirURL);
assert(writeStreamRef != NULL);
ftpStream = (__bridge_transfer NSOutputStream *) writeStreamRef;
[ftpStream setProperty: ftpUser forKey: (id)kCFStreamPropertyFTPUserName];
[ftpStream setProperty: ftpPass forKey: (id)kCFStreamPropertyFTPPassword];
ftpStream.delegate = self;
[ftpStream scheduleInRunLoop: [NSRunLoop currentRunLoop] forMode: NSDefaultRunLoopMode];
// open stream
[ftpStream open];
CFRelease(writeStreamRef);
}
- (void)stream:(NSStream *)aStream handleEvent:(NSStreamEvent)eventCode {
NSLog(#"aStream has an event: %i", eventCode);
switch (eventCode) {
// all cases handled properly
default:
// no event
NSLog(#"default mode; no event");
break;
}
}
EDIT: added creation code that is used in the class UploadDatasetVC:
FTPUploads *uploads = [[FTPUploads alloc] initWithTimeseriesID: timeseries_id
fromDatasetDir: datasetDir
withFiles: files
andCallbackObject: self];
[uploads createDir];
It looks to me like the only reference to your FTPUploads object is the delegate property on the stream. This won't retain your object, so if nothing else has a reference to the object, the object will be dealloced. A.R.C. doesn't try to prevent this scenario.
What you need to do is have the code that allocates the FTPUploads object keep a reference to the object until it completes.
It also wouldn't be a bad idea to set the ftpStream.delegate property to nil in your FTPUploads dealloc method, as this will prevent a crash if the object is dealloced prematurely.
The problem is that your ftpStream object is being deallocated. You create it with CFWriteStreamCreateWithFTPURL(), then release it with CFRelease(). You used a __bridge cast, which basically means "don't do any memory management on this assignment". So ARC didn't retain it when you assigned it to ftpStream. Since your intention was to transfer ownership from CF to ARC, that was the wrong cast to use.
You actually wanted either __bridge_retained or __bridge_transfer. I can never remember which is which, though. Luckily, there's another option—the CFBridgingRetain() and CFBridgingRelease() macros. They resolve down to those same bridging casts, but are named far more clearly.
In this case, you want CF to release it, but bridge it over to ARC. So you want CFBridgingRelease(). That will tell ARC to take ownership of the object, and then do a CFRelease. In short, replace this:
ftpStream = (__bridge NSOutputStream *) writeStreamRef;
with this:
ftpStream = CFBridgingRelease(writeStreamRef);
And then remove the call to CFRelease() a few lines later.
My guess is that you should either wait until the stream is finished to do CFRelease(writeStreamRef), or do a __bridge_transfer to transfer the ownership over to ftpStream before you release your writeStreamRef