0

I have a method:

- (void)setName:(ModelClass *)model {

    [model  release];
    ModelClass  *tmp    =   [[[ModelClass   alloc]  initWithId:@"New"]  autorelease];
    model   =   [tmp    retain];

    NSLog([NSString stringWithFormat:@"Gia tri trong la %@",model.modelClassId]);

}

and

- (void)viewDidLoad {

    [super viewDidLoad];

    ModelClass  *tmp3   =   [[ModelClass    alloc]  initWithId:@"Old"];

    [self   setName:tmp3];

    ModelClass  *tmp4   =   [[ModelClass    alloc]  initWithId:@"Old"];
    ModelClass  *tmp5   =   [[ModelClass    alloc]  initWithId:@"Old"];
    ModelClass  *tmp6   =   [[ModelClass    alloc]  initWithId:@"Old"];
    ModelClass  *tmp7   =   [[ModelClass    alloc]  initWithId:@"Old"];

    NSLog(tmp3.modelClassId);


}

It runs OK. But when i write:

- (void)setName:(ModelClass *)model {

        ModelClass  *tmp    =   [[[ModelClass   alloc]  initWithId:@"New"]  autorelease];
        [model  release];
        model   =   [tmp    retain];

        NSLog([NSString stringWithFormat:@"Gia tri trong la %@",model.modelClassId]);

    }

it breaks my app. Anybody can explain to me what the difference is between the two cases?

1
  • 1
    Your code would do pretty much nothing. What are you trying to achieve exactly? The issue might be in ModelClass too so if you can show us the code it would help. Commented Nov 17, 2010 at 4:23

2 Answers 2

2

There is a fundamental error in your implementation of setName: and it is pure luck that you only ended up finding out it was incorrect when you moved some lines around.

When you assign a new value to model in your setName: method you are not actually changing the reference you passed in. Think of model as a local variable of the function (since that's basically what it is). No change you make to the value of that pointer will be visible when the method ends. However you CAN change what it points to. And you do so because you release it. Your setName: code is basically equivalent to this:

- (void)setName:(ModelClass *)model {
    Model* otherModel = model;
    [model release];
    ModelClass *tmp = [[[ModelClass   alloc]  initWithId:@"New"]  autorelease];
    otherModel = [tmp retain];
    NSLog([NSString stringWithFormat:@"Gia tri trong la %@", otherModel.modelClassId]);
}

Which is the same as:

- (void)setName:(ModelClass *)model {
    // Release the `ModelClass` passed in.
    [model release];

    // Create a new object, put in in the autorelease pool, then retain it one
    // extra time (causing it to leak). NSLog it's modelClassId.
    ModelClass *tmp = [[[ModelClass   alloc]  initWithId:@"New"]  autorelease];
    otherModel = [tmp retain];
    NSLog([NSString stringWithFormat:@"Gia tri trong la %@", tmp.modelClassId]);
}

So in light of that fact, your other method (with the extra tmp objects removed) does this:

- (void)viewDidLoad {
    [super viewDidLoad];

    // Create a new `ModelClass` instance.
    ModelClass *tmp3 = [[ModelClass alloc] initWithId:@"Old"];

    // Send `tmp3` to `setName:` therefore releasing it. Since `tmp3` was the only
    // reference, the instance has been deallocated.
    [self setName:tmp3];

    // Attempt to NSLog a member/property of a deallocated object, causing
    // undefined behavior. 
    NSLog(tmp3.modelClassId);
}
Sign up to request clarification or add additional context in comments.

Comments

0

You need to read the Memory Management Rules. Note the bit in bold:

You only release or autorelease objects you own.

You only own objects you retain or create with alloc, new or a method containing "copy".

So looking specifically at the object called model in your setName: method, it was passed in as a parameter. You have not retained it (in the scope of setName:) so you must not release it. This is the cause of your crash.

You first allocate an object to tmp3, then you call setName: with tmp3 as a parameter. This releases tmp3 so it is no longer pointing to a valid object. The fact that you assign to model inside the method is not relevant. Objective-C passes the pointer by value so the pointer tmp3 is unaffected by anything in setName: although the object it points to has now gone.

Then you get to the NSLog, tmp3 is a dangling pointer so the most probable result of sending a message to it (as you do) is a crash. In the original implementation of setName: you have fluked the right result because the new object you created has reused the memory vacated by tmp3's object.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.