Question

So here is my scenario -- In the header file of a class I do:

@interface MyClass : NSObject
{
NSString *string1;
NSString *string2;
}

- (void) methodOne: (NSString *) passedString;
- (void) methodTwo: (NSString *) passedString;

@end

In the implementation file I do this:

 #import MyClass.h

    @implementation MyClass

    - (void) methodOne: (NSString *) passedString
    {
       NSLog(@"%@", passedString);
       string1 = passedString;
    }

    - (void) methodTwo: (NSString *) passedString
    {
       NSLog(@"%@", passedString);
       string2 = passedString;
    }

What I am finding is that there is some kind of inconsistency when doing this versus [NSString alloc] initWithString:].

As you can see string1 and string2 are treated exactly the same, but what is happening is that string1 is getting set, but string2 is remaining empty. I get a bad access when I reference it later.

I thought maybe I was passing an empty string to methodTwo: so I added that NSLog which proves that it is not empty, but has the expected string.

Since I have noticed this inconsistency before I decided to change to this:

        - (void) methodOne: (NSString *) passedString
        {
           NSLog(@"%@", passedString);
           string1 = passedString;
        }

        - (void) methodTwo: (NSString *) passedString
        {
           NSLog(@"%@", passedString);
           string2 = [[NSString alloc] initWithString: passedString];
        }

Now both strings are working as expected. My question is why is there this inconsistency?

This is not the only time this has happened to me. It has happened with all kinds of objects. The only thing that seems to work every time is alloc init. Methods like stringWithString: work most of the time, but not always.

Was it helpful?

Solution

It's because in the first example, you don't retain or copy the strings. string2 is getting deallocated at some point before you use it. It's actually pure luck that string1 is OK. You should change your code to be something like this:

- (void) methodOne: (NSString *) passedString
{
   NSLog(@"%@", passedString);
   NSString* oldString = string1;
   string1 = [passedString copy];
   [oldString release];
}

- (void) methodTwo: (NSString *) passedString
{
   NSLog(@"%@", passedString);
   NSString* oldString = string2;
   string2 = [passedString copy];
   [oldString release];
}

and release in dealloc

-(void) dealloc
{
    [string1 release];
    [string2 release];
    // other stuff
    [super dealloc];
}

I strongly recommedn you create properties for string1 and string2 to handle all that reference counting stuff:

@interface MyClass : NSObject
{
NSString *string1;
NSString *string2;
}

- (void) methodOne: (NSString *) passedString;
- (void) methodTwo: (NSString *) passedString;

@property (copy) NSString* string1;
@property (copy) NSString* string2;  

@end

@imlementation MyClasss
@synthesize string1, string2;

- (void) methodOne: (NSString *) passedString
{
   NSLog(@"%@", passedString);
   [self setString1: passedString];
}

- (void) methodTwo: (NSString *) passedString
{
   NSLog(@"%@", passedString);
   [self setString2: passedString];
}

// dealloc as before

@end

OTHER TIPS

You are making memory management errors. You have to retain or copy the strings when you assign them to your ivars (and release them later).

It is possible that you can still access an object even though it has been deallocated when the memory it occupied has not been overwritten yet. But you cannot rely on it.

If the passed strings are autoreleased there is no retain when they are assigned. Constant strings (@str") essentially are never released, created strings such as by stringWithFormat need to be retained.

Please show the callers.

Using @properties with retain will eliminate many retain issues. Or consider using ARC, that eliminates the need for retain/release/autorelease.

As others have stated in this thread you have some memory management issues here, perhaps you aren't quite understanding the way that NSObjects get allocated and retained, you should read up on Objective-C memory management. In the mean time there are two approaches you could take to solve your above issue.

You could have your NSString member variables (string1 & string2) kept as properties of your class, aside from some other functionality declaring these as properties would give them setter and getter accessors, that you would call instead of method1 and method2. So this would change your code to look like the following in your header file

@interface MyClass : NSObject
{
     NSString *string1;
     NSString *string2;
}

@property( nonatomic, retain)NSString* string1;
@property( nonatomic, retain)NSString* string1;

Then remember to add the following to your source file (typically at the top of the file after the @implementation MyClass line)

@implementation MyClass
@synthesize string1;
@synthesize string2;

Then in the class where you were calling method1 and method2 from you can change the code to look like

    //Lets assume somewhere you've called an init Method for your MyClass Object, something like
    MyClass* myClassObject = [[MyClass alloc] init];

//you can then call the setters to set the string like so
[myClassObject setString1:@"some string"]; //or myClassObject.string1 = @"some string";
[myClassObject setString2:@"some string"]; //or myClassObject.string2 = @some other string";
//Calling these member variables either of the above ways is valid, I prefer the former as it's a bit easier on the eye for me

//and to get the values back out of the strings you could call
NSString* output = [myClassObject string1];
//or
NSString* output2 = myClassObject.string2;

Now you may for some reason not want to use an @property for the NSString member variables, so you could modify your original source (.m) file to look like

@implementation MyClass

- (void) methodOne: (NSString *) passedString
{
   NSLog(@"%@", passedString);
   if( string1 != nil )
   {
      [string1 release];
   }
   string1 = [[NSString alloc] initWithString:passedString];
}

- (void) methodTwo: (NSString *) passedString
{
   NSLog(@"%@", passedString);
   if( string2 != nil )
   {
      [string2 release];
   }
   string2 = [[NSString alloc] initWithString:passedString];
}

That should solve the issue of why your strings aren't valid, as you wont be overwriting the memory and trying to read back garbage this way. You will have to remember to release these NSStrings in your dealloc if they are not nil, otherwise you'll get a memory leak there too.

Hope this helps.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top