Valentyn Kuznietsov
Valentyn Kuznietsov

Reputation: 543

Accessing Instance variables inside blocks

I have a question about the best practice at using async blocks in situations like mine.

For example, I have two controllers: (let it be controller1 and controller2)

I'm pushing controller2 inside controller1:

controller2 * c = [[controller2 alloc] init];
[self.navigationController pushViewController:c animated:YES];
[c release];

controller2 has an instance variable:

@interface controller2 : UITableViewController{
    UIImageView * imageView;
}

allocates and releases it:

- (id)init{
   ...
   imageView = [[UIImageView alloc] init];
   ...
}

- (void)dealloc{
   [imageView release];
   [super dealloc];
}

and controller2 downloads an image for this imageView:

- (void)viewDidLoad{
 ...

[NSURLConnection sendAsynchronousRequest:request queue:[[NSOperationQueue alloc] init] completionHandler:
    ^(NSURLResponse *response, NSData *data, NSError *error) {
        UIImage * image = [[UIImage alloc] initWithData:data scale:[[UIScreen mainScreen] scale]];
        imageView.image = image; 
        [image release];    
    }];
 }

Obviously user can press "Back" button on the top of navigation bar and our controller2's object will be released with this imageView.

Situation:

So, what I should do to make it cool in my code? I like blocks! So much fun and less code/classes compared to NSURLConnection delegates.

So, whats the best practice in using such blocks? Maybe I should not use blocks in such situation to make my code better?

p.s.: I tried to do this: make NSOperationQueue as instance variable, and stop all tasks in dealloc.. but that kills the advantage of this block :( better to use my downloader class with delegate in that case ;( anyway there are too much code.

p.p.s.: I know that I should stop download after popping my controller; But Im not about it. Let it be any task (for example, converting video, etc, any "heavy" background thread), that should be done anyway, even if user left this controller, but it will use some instance variables if they are alive.

Thank You

Upvotes: 0

Views: 831

Answers (3)

Valentyn Kuznietsov
Valentyn Kuznietsov

Reputation: 543

[NSURLConnection sendAsynchronousRequest:request queue:imageDownloadersQueue completionHandler:^(NSURLResponse *response, NSData *firstImageData, NSError *error) {
        NSLog(@"first block beginning [self retainCount] = %d, [imagesDictionary retainCount] = %d",[self retainCount], [imagesDictionary retainCount]);

        NSURLRequest * request = [NSURLRequest requestWithURL:[NSURL URLWithString:url]];
        UIImage * first_image = [[[UIImage alloc] initWithData:firstImageData scale:[[UIScreen mainScreen] scale]] autorelease];
        [NSURLConnection sendAsynchronousRequest:request queue:imageDownloadersQueue completionHandler:^(NSURLResponse *response, NSData *secondImageData, NSError *error) {
            NSLog(@"second block beginning  [self retainCount] = %d, [imagesDictionary retainCount] = %d",[self retainCount], [imagesDictionary retainCount]);
            UIImage * second_image = [[[UIImage alloc] initWithData:secondImageData scale:[[UIScreen mainScreen] scale]] autorelease];
            if(first_image && second_image){
                [imagesDictionary setObject:[NSArray arrayWithObjects:first_image, second_image, nil] forKey:match.match_id];
            }            
        }];
    }];

first block beginning [self retainCount] = 2, [imagesDictionary retainCount] = 1

second block beginning [self retainCount] = 2, [imagesDictionary retainCount] = 1

seems like block is retaining "self" and releasing it at the end so "self" (my instance) cannot be deallocated before the end of the block

thats the answer, huh? hope so ...

thanks

Upvotes: 0

user1447414
user1447414

Reputation: 1486

You should declare your variable with:

    __block UIImageView*imageView;

Upvotes: 1

danh
danh

Reputation: 62686

I think your block approach is good. I think it's memory management that's getting you: Why does controller2 dealloc the imageView in it's dealloc method. That's should be

[imageView release];

not dealloc. Does controller2 retain that imageView?

The reason this is going to work is that NSURLConnection block is going to capture the imageView, so it hangs around even after controller2 is gone. In the case where you press back, the controller goes away, the imageView gets a new image then it goes away too. Everything should be fine.

I agree with you that blocks are great. The other thing that's great is ARC, and the two go well together. Can you use ARC in this project?

Upvotes: 0

Related Questions