勿绮语
勿绮语

Reputation: 9320

a not-so-obvious decrement of ref count

Xcode Analyze complained that I incorrectly decremented ref count at the line marked "this line". it seems a bit strange, since it was not obvious that that line decrements the ref count.

- (void)connectionDidFinishLoading:(NSURLConnection *)connection
{
    UIImage * image = [[UIImage alloc] initWithData:receivedData];
    if (image == nil) {
        image = [UIImage imageNamed:@"null.bmp"];
    }
    self.itemImage.image = image; //this line
    self.promotion.image = image; 
    [image release];
}

Upvotes: 2

Views: 151

Answers (3)

jcpennypincher
jcpennypincher

Reputation: 4030

I believe Josh got this mostly, but I would clarify...

[UIImage imageNamed:@"null.bmp"];

This creates an autorelease UIImage.

But...

UIImage * image = [[UIImage alloc] initWithData:receivedData];

creates a variable that requires an explicit release. So, depending whether you received data (perhaps networking is off), you will have different ownership rules depending whether the image was allocated with an explicit release requirement or autorelease. So, [image release] would be incorrect if no data was received.

Upvotes: 1

jscs
jscs

Reputation: 64002

This is a mildly tricky thing; it's understandable that it is causing confusion. The two paths here result in image holding objects with different ownership statuses, and thus different reference counts.

Going through the if results in image holding an object that your code doesn't own.

UIImage * image = [[UIImage alloc] initWithData:receivedData];
// If |initWithData:| succeeds, the object in |image| is owned, because you
// called |alloc| to create it.
if (image == nil) {
    image = [UIImage imageNamed:@"null.bmp"];
    // This object is _not_ owned by your code and you must not send
    // |release| to it.
}
// ... the setter lines are irrelevant to the reference count of |image|.

[image release];
// This is only okay if |initWithData:| succeeded and you have ownership
// of |image|.

I think that the analyzer is indicating the wrong line, in the same way the compiler will complain about a missing semicolon five lines later.

The way to solve this may be to retain the image you get from imageNamed: You can't just not send release, because you do own image in one case, and you need to properly relinquish that ownership. I'd also suggest putting a comment in there about sending retain so you remember eight months later why you did it.

Better than that, as suggested below by the inimitable Bavarious, and as I think you yourself already figured out, would be to autorelease the alloc'd image and remove the later release line.

Upvotes: 4

Chetan Ahuja
Chetan Ahuja

Reputation: 941

I'm not an Objective-C expert but it seems like you're overwriting the contents of sefl.itemImage.image with the local pointer image, therefore reducing one ref to whatever was stored in self.itemImage.image before the assignment.

I don't think the local image being null or non-null has anything to do with it.

Upvotes: 1

Related Questions