Issues with threading, blocks, CGImageRef and scope - objective-c

I have a program where I set up a completion block that is running on a background thread. Inside the block I set up a CGImageRef and then on the main thread set my layer contents. THe problem is, sometimes the app crashes on the main thread portion.
This is the completion block, in the code below both the fullImage and cfFullImage are declared in my .h
requestCompleteBlock completionBlock = ^(id data)
{
// Seems I need to hold onto the data for use later
fullImage = (NSImage*)data;
NSRect fullSizeRect = NSMakeRect(0, 0, self.frame.size.width, self.frame.size.height);
// Calling setContents with an NSImage is expensive because the image has to
// be pushed to the GPU first. So, pre-emptively push it to the GPU by getting
// a CGImage instead.
cgFullImage = [fullImage CGImageForProposedRect:&fullSizeRect context:nil hints:NULL];
// Rendering needs to happen on the main thread or else crashes will occur
[self performSelectorOnMainThread:#selector(displayFullSize) withObject:nil waitUntilDone:NO];
};
The final line of my completion block is to call displayFullSize. That function is below.
- (void)displayFullSize
{
[self setContents:(__bridge id)(cgFullImage)];
}
Do you see or know of any reason why the setContents might fail?
Thanks
Joe

cgFullImage is not retained. The CGImage Core Foundation object is deallocated and you are using a deallocated object.
Core Foundation object pointer types like CGImageRef are not managed by ARC. You should either annotate the instance variable with __attribute__((NSObject)), or change the type of the instance variable to an Objective-C object pointer type like id.

Related

Can I dealloc a controller in a method with its own callback block?

I have a general question about using callback blocks to release a controller all in a given stack frame. This is currently working as desired and the controller is getting a dealloc message and no other obvious issues. This is useful (in my opinion) for displaying transient popovers, dialogs and other views without maintaining ivars or other state in the host class. If the xib for the controller is set up to dealloc on close this becomes nice and clean.
This is the general pattern:
- (void)showTransientView
{
MyCustomController *controller = nil;
void(^completeBlock)(ResponseCodeType) = ^(ResponseCodeType response){
if (response == ResponseOk){
[self transientViewDidEnd:controller];
}
[controller autorelease]; //project is not using ARC
};
controller = [[MyCustomController alloc] initWithCallback:completeBlock];
}
My question is basically are there hidden problems or other objections here that I am not considering?
Also, how will this change when turning ARC on besides the obvious removal of [controller autorelease]?
Not sure why you are doing autorelease here as it seems a plain release should do the job seems as you are just balancing the alloc
With ARC there is nothing holding onto that controller so it would be instantly deallocated here.
You could rejig this slightly so that you are passing the completion after instantiation that way the block will actually capture the instance which is what I believe you are trying to achieve.
You will also need to add the __block storage specifier to modify controller within the block and nil out the controller so ARC will send the release
__block MyCustomController *controller = MyCustomController.new;
controller.completion = ^{
controller = nil;
};
// Using weak in this example to ensure the dispatch_after is not the thing holding onto our controller
__weak __typeof(controller) weakController = controller;
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(2 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
weakController.completion();
});

dispatch async with blocks exc_bad_access non ARC project

i have a non arc project. i'm trying to use dispatch_async to get data from server and save it in sqlite. the dispatch_async happens inside a method with callback. on calling the method the app crashes with exc bad access. here is how i've implemented the code.
- (void) HandleData:(const char*) receivedData WithSuccess:(void(^)(BOOL finishing))completed
{
dispatch_queue_t fetchQ = dispatch_queue_create("Refreshing", NULL);
dispatch_async(fetchQ, ^{
[self write_data_in_sqlite]// **<--crash happens here in the method which is called here**
}
dispatch_sync(dispatch_get_main_queue(), ^{
completed(YES);
});
});
dispatch_release(fetchQ);
}
and i call the method as follow:
HandleResponse *handleResponse = [[[HandleResponse alloc] init] autorelease];
[handleResponse HandleData:aData WithSuccess:^(BOOL finishing) {
if(finishing)
{
//update the UI here
}
}];
if i remove the dispatch_async then it doesnt crash, but my UI gets blocked while writing to the sqlite.
what am i doing wrong?
edit:
removing the block and using dipatch_async produces the same exc_bad_access crash.
edit 2:
i tried example answer given below, it still crashes.
i thought to copy it then autorelease it. it crashes still but nit that often. i'm gonna check for memory leak. i'll report.
HandleResponse *handleResponse = [[[HandleResponse alloc] init] autorelease];
[handleResponse HandleData:aData WithSuccess: [[^(BOOL finishing) {
if(finishing)
{
//update the UI here
}
} copy] autorelease];
edit 3:
the crash happens in strlen even the xml content is in xmlResopnse. but why this happen with dispatch and not without it
xmlDocPtr xml= xmlParseMemory(xmlResopnse, strlen(xmlResponse);
edit 4:
as in answer below suggested not to use c objects in dispatch async. so i converted xmlResponse from const char* to nsstring and it doesnt crash.
Everything you've shown seems to be okay in terms of blocks and memory management. It must be something else.
I notice that you're passing in a C string (the char pointer receivedData) that you're not using. If you're not showing us the real code, and you are actually using the receivedData variable in the block, then that could be a problem, because the block simply captures the char pointer, but does not manage the memory of the string behind the pointer (it is not an Objective-C object). Therefore, it is possible that the C string is only valid in the calling scope (before the asynchronous operation), and no longer valid when the asynchronous operation runs. Your statement that something is crashing at strlen supports the idea that there is something wrong with some C string. You should try using NSString objects instead, since as objects they are properly memory-managed by blocks.

Blocks and Variables

At the moment I explore blocks opportunities, I read more then ten times apples docs) but i cant understand blocks behaviour in blogs examples and in my code. I know about __block modifier. So please look at few my examples, and say why it work or not:
1)Not working. Need __block, because I want to modified object.And self retained(correct?) and in blogs I'm frightened by retain cycle of self. Does it is?
NSDictionary *result = nil;
dispatch_async(queue, ^{
result = [self sendRequest:apiRequest];
});
2)Not working. I dont understand about variable scope of dispatch_async(dispatch_get_main_queue(), ^{});. Does it see all local variables in main thread or it is separate block from main thread, but exicuted in main thread? Like dispatch_async(dispatch_get_main_queue()^{}); copies all local variables in dispatch_async(queue,^{}); and switches semaphore to main thread.
NSDictionary *result = nil;
dispatch_async(queue, ^{
NSDictionary *data = [self sendRequest:apiRequest];
dispatch_async(dispatch_get_main_queue(), ^{
result=[data retain];
});
});
3)And examples from blogs and stanford's course that particularly muddle me, because they work.
- (void)viewWillAppear:(BOOL)animated
{
dispatch_queue_t downloadQueue = dispatch_queue_create(“image downloader”, NULL);
dispatch_async(downloadQueue, ^{
NSData *imageData = [NSData dataWithContentsOfURL:networkURL];
dispatch_async(dispatch_get_main_queue(), ^{
UIImage *image = [UIImage imageWithData:imageData];
self.imageView.image = image;
self.imageView.frame = CGRectMake(0, 0, image.size.width, image.size.height);
 self.scrollView.contentSize = image.size;
});
});
dispatch_release(downloadQueue);
}
I dont understand, because first they dont mention about __block for self in the articles and the lessons, second this code modified variables, but through properties and complier doesn't swear that variables arent assignable and properties change reference, not value. Final it works. Thanks in advance.
Old question, but I feel like new readers might benefit from a more precise answer than the current one:
1) result is a pointer to an object, not an object itself. Prepending __block is necessary because that pointer is assigned to within the block. Modifying objects pointed to by pointer variables outside of the block is not the problem, e.g.
NSMutableDictionary* result = [NSMutableDictionary new];
and then modifying the object within the block via [result setObject...] would be fine. And that's the reason why 3) works: Only variables of the object pointed to by self are being modified. The pointer self is never assigned to.
Regarding retainment under ARC of the objects referenced by the block, see e.g.:
ARC with blocks and retain cycles
ARC, Blocks and Retain Cycles
blocks and ARC - copy or crash with release build (caused by optimization level)
1) Yes you'd need to declare:
__block NSDictionary *results = nil;
If you're using ARC it should automatically take care of retaining and releasing the results object. If not using ARC, then do a retain as the first thing you do and a release as the last thing you do in the block.
2) A block should have visibility of all variables/objects available to it's parent's scope. So in a method you should be able to see all local variables to that method and the object which that method belongs to (as self).
3) Not too sure about this one. Variables do go away once you leave the function/method (which will be as so as most blocks have been called, rather than finished executing), where as anything that is part of self won't. Maybe that has something to do with it.

Message sent to deallocated instance -- short and simple

This has to be very basic, but I don't see the problem. The program crashes whenever the following code block is executed. Analyzer reports a possible memory leak:
if (anImage) {
eventImageView.frame = defaultEventImageFrame;
UIImage *scaledImage = [anImage scaleToFitWithin:defaultEventImageFrame.size interpolationQuality:kCGInterpolationHigh];
eventImageView.backgroundColor = [UIColor colorWithPatternImage:scaledImage];
}
The message is -[UIImage release]: message sent to deallocated instance 0x1129d920*
Instance 0x1129d920 is scaledImage
I tried adding retains and releases, like this
if (anImage) {
eventImageView.frame = defaultEventImageFrame;
UIImage *scaledImage = [[anImage scaleToFitWithin:defaultEventImageFrame.size interpolationQuality:kCGInterpolationHigh] retain];
eventImageView.backgroundColor = [UIColor colorWithPatternImage:scaledImage];
[scaledImage release];
}
and still get the error message.
So I tried replacing the assignment with a copy, like this
if (anImage) {
eventImageView.frame = defaultEventImageFrame;
UIImage *scaledImage = [anImage copy];
eventImageView.backgroundColor = [UIColor colorWithPatternImage:scaledImage];
}
And the problem is gone.
Checking the scaleToFitWithin method, I see it returns an autoreleased object:
- (UIImage *) scaleToFitWithin:(CGSize) newSize
interpolationQuality:(CGInterpolationQuality)quality{
CGSize originalImageSize = self.size;
CGSize newImageSize;
if (originalImageSize.width <= originalImageSize.height) {
newImageSize.width = self.size.width * newSize.width / self.size.width;
newImageSize.height = self.size.height * newSize.width / self.size.width;
}
else {
newImageSize.width = self.size.width * newSize.height / self.size.height;
newImageSize.height = self.size.height * newSize.height / self.size.height;
}
return [[[self normalize] resizedImage:newImageSize interpolationQuality:kCGInterpolationHigh] autorelease];
}
So there is something about memory management that I'm not understanding. What is the problem likely to be?
The problem is most likely that the scaleToFitWithin:interpolationQuality: method is calling autorelease on an object which has already previously been autoreleased. This can occur if you initialise the UIImage using a temporary constructor, like +[UIImage imageWith...], earlier in the same method you call the scaling method from. The reason it works when you use [anImage copy] is because the behaviour of the copy constructor is such that the object returned to you has already had retain called on it (so it has a local retain count of 1 and zero autoreleases).
What happens at the end of the current run loop is: the autorelease pool which is currently in use is drained, and as a part of that two release messages will be sent to the UIImage. When the first one is sent, the application then runs off and calls dealloc on the image, because the retainCount has decreased to zero. When the second one is sent, the application throws an exception because a message is being sent to an object which was previously deallocated.
Try removing the autorelease message from the scaleToFitWithin:interpolationQuality: method. Even if your resizedImage:interpolationQuality: method IS returning a new object, you should only be calling autorelease in that method rather than the scaling method.
It seems that the method resizedImage:interpolationQuality: itself returns an autoreleased object and you are again autoreleasing it in the reutun statement. Just remove the autorelease from the return statement,
return [[self normalize] resizedImage:newImageSize
interpolationQuality:kCGInterpolationHigh];
Then you don't have to retain/release or copy the returned object in if (anImage) {...} block.

strange run time error message from CIImage initWithContentsOfURL

When executing the following code I receive a run time error when the code executes the second line of code. The error (which shows up in the debugger) says: [NSButton initWithContentsOfURL:]: unrecognized selector sent to instance 0x100418e10. I don't understand this message, because it looks to me (based on my source code) like the initWithContentsOfURL message is being sent to the myImage instance (of the CIImage class) ... not NSButton. Any idea what is going on?
If it matters ... this code is in the Application Controller class module of an Xcode project (a Cocoa application) -- within a method that is called when I click on a button on the application window. There is only the one button on the window ...
// Step1: Load the JPG file into CIImage
NSURL *myURL = [NSURL fileURLWithPath:#"/Users/Adam/Documents/Images/image7.jpg"];
CIImage *myImage = [myImage initWithContentsOfURL: myURL];
if (myImage = Nil) {
NSLog(#"Creating myImage failed");
return;
}
else {
NSLog(#"Created myImage successfully");
}
This line
if (myImage = Nil) {...
Does assignment instead of comparison
Also, don't put a space before parameter for your method in question. And it should be something like this:
CIImage *myImage = [[CIImage alloc] initWithContentsOfURL:myURL];
CIImage *myImage = [myImage initWithContentsOfURL: myURL];
You have not initialized the myImage variable, but you are sending its value an initWithContentsOfURL: message. When, by chance, it contains the pointer to an existing object (such as existing NSButton object), the exception in your question occurs.
If you are really unlucky, the object you end up sending the message to will respond to initWithContentsOfURL:, in which case this will re-initialize this object with a different URL. Depending on the URL, it may make the object have the wrong contents, or release itself. Either way, it will probably cause several leaks, and will cause a crash, either by sending later CIImage messages to an object that is still not a CIImage (simply a re-initialized other object), or by sending messages to an object that released itself and so is now dead.
The solution is, as Eimantas stated, to allocate a new CIImage object (by sending the CIImage class an alloc message), then send the initWithContentsOfURL: message to that object, then assign that result to the variable.
if (myImage = Nil) {
As Eimantas noted, this is an assignment, not a comparison. Yes, it is perfectly valid to assign to a variable within a condition in C (and so in Objective-C). The compiler offers a warning for this; you should turn it and a bunch of others on. The solution is to use the equality operator, ==.
Furthermore, as Wevah noted, Nil is the wrong constant to use here, since you are comparing an object's pointer to it, not a class's pointer. The correct constant is nil.