views:

66

answers:

2

Hey, everyone! I'm totally confused with this small "out of scope" thing. So here is some code to describe my situation:

Simple iphone view-based application.

View controller header:

#import <UIKit/UIKit.h>

@interface global_nsstring_testViewController : UIViewController {
 UIImageView* image_view;
 NSString*  image_name;
}

@property (nonatomic,retain) UIImageView* image_view;
@property (nonatomic,retain) NSString*  image_name;

- (void) fadeView:(UIImageView*)View andThenChangeImageTo:(NSString*)Name;
- (void) switchImageAfterFade;

@end

View controller .m:

#import "global_nsstring_testViewController.h"
@implementation global_nsstring_testViewController
@synthesize image_view, image_name;

- (void) viewDidLoad {
 image_view = [[UIImageView alloc] initWithImage:[UIImage imageWithContentsOfFile:[[NSBundle mainBundle] pathForResource:@"image1.png" ofType:nil]]];

//  ## Block 1 ## 
// NSString* name = @"image2.png";
// [self fadeView:image_view andThenChangeImageTo:name];

// ## Block 2 ##
// NSString* name = [NSString stringWithFormat:@"image%d.png",2];
// [self fadeView:image_view andThenChangeImageTo:name];

 [self.view addSubview:image_view];
 [super viewDidLoad];
}

- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event {
//  ## Block 3 ##
// NSString* name = @"image2.png";
// [self fadeView:image_view andThenChangeImageTo:name];

//  ## Block 4 ##
    NSString* name = [NSString stringWithFormat:@"image%d.png",2];
    [self fadeView:image_view andThenChangeImageTo:name];
}

- (void) fadeView:(UIImageView*)View andThenChangeImageTo:(NSString*)Name {
 image_name = Name;
 image_view = View; // ## Point 1 ##

 [UIView beginAnimations:nil context:nil];
 [UIView setAnimationDuration:1];
 [UIView setAnimationDelegate:self];
 [UIView setAnimationDidStopSelector:@selector(switchImageAfterFade)];
 View.alpha = 0.3;
 [UIView commitAnimations];
}

- (void) switchImageAfterFade {
 UIImage* image = [UIImage imageWithContentsOfFile:[[NSBundle mainBundle] pathForResource:image_name ofType:nil]]; // ## Point 2 ##
 [image_view setImage:image];
}

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

@end

With uncommented #Block 4# I get “out of scope” message from debugger on image_name var at #Point 1#. And therefore I get EXC_BAD_ACCESS at #Point 2#.

However with uncommented #Block 3# or #Block 1# instead of #Block 4# everything works fine, so I suppose this is all about NSString...

I've tried to use NSMutableString, but without any success either.

So if anyone could tell me, what I do wrong or how to fix it, I would be greatly appreciate.

Another strange thing, that I don't get:

If to uncomment #Block 2# and not to use any breakpoints, it won't crash, but image will disappear. But if I set a breakpoint at #Point 2#, it will crash with EXC_BAD_ACCESS after just one step.

xCode version: 3.2.3

A: 

Block 4 uses a convenience initializer, so you should use [[NSString stringWithFormat:@"image%d.png",2] retain] or it will get autoreleased. Don't forget to release it after assigning it with self.image_name = name

tob
A: 

First of all, "out of scope" in the debugger simply means the debugger can't figure out how to display the variable. It's essentially meaningless. You can print the value by clicking on the console (which has "gdb>" in it) and typing

po image_name

Secondly, you are not using setters correctly. You've got @property's for your two variables, but you're not using them. When you do

variable = value;

it's simply setting the variable -- no property methods are called. However,

self.variable = value;

will call the property setter, which since you've got "nonatomic, retain" will retain your variable, and all will be swell.

In this case, you're doing

image_name = Name;

where you want to do

self.image_name = Name;

or (as tob suggests)

image_name = [Name retain];

It's a very common thing when you start doing Objective-C coding that you confuse the two ways to set a variable in a class, and it's vital that you get the difference. Your properties are not called when you do not use instance dot variable.

Coincidentally, a lot of people also tend to confuse this even more by doing things like

self.image_name = [Name retain];

This is absolutely incorrect. You will end up leaking "Name" every time you set image_name, because of the double-retain (once for @property (nonatomic, retain) in your setter, and once for [Name retain]).

Kalle
Thanks a lot for a great explanation! Now it's much more clear. :)
Squatch