2

I got an EXC_BAD_ACCESS in main(), here is my code:

int main(int argc, char *argv[])
{
    NSAutoreleasePool * pool = [[NSAutoreleasePool alloc] init];
    int retVal = UIApplicationMain(argc, argv, nil, @"TestBedAppDelegate");
    [pool release];
    return retVal;
}

@interface TestBedAppDelegate : NSObject <UIApplicationDelegate>
@end

@implementation TestBedAppDelegate
- (void)applicationDidFinishLaunching:(UIApplication *)application {    
    UIWindow *window = [[UIWindow alloc] initWithFrame:[[UIScreen mainScreen] bounds]];
    UINavigationController *nav = [[UINavigationController alloc] initWithRootViewController:[[TestBedViewController alloc] init]];
    [window addSubview:nav.view];
    [window makeKeyAndVisible];
}
@end

- (void) action: (id) sender
{
    [self highRetainCount];
}

@implementation TestBedViewController
- (void) highRetainCount
{
    UIView *view = [[[UIView alloc] init] autorelease];
    printf("Count: %d\n", [view retainCount]);

    NSArray *array1 = [NSArray arrayWithObject:view];
    printf("Count: %d\n", [view retainCount]);
    [array1 autorelease]; // If comment this line, everything will be OK
}
@end

The program stopped at main():

int retVal = UIApplicationMain(argc, argv, nil, @"TestBedAppDelegate");

As the comment says, after commenting out [array1 autorelease];, everything was OK.

So here is my question:

  1. EXC_BAD_ACCESS often indicates using an object already released. Clearly there's something to do with [array1 autorelease];, but I can't understand their relationship.

  2. Why stopped at this position -- main() -- instead of somewhere else?

Newbie question :)

Kristian Glass
  • 37,325
  • 7
  • 45
  • 73
Shane Hou
  • 4,808
  • 9
  • 35
  • 50
  • 1
    `array1` is already autoreleased, thus calling `autorelease` on it makes everything blow up. – Manlio Mar 31 '12 at 11:17
  • Saphrosit is right, there is no need for you to call [array1 autorelease]; – stephenmuss Mar 31 '12 at 11:22
  • It is not 'already autoreleased', it is not owned by the caller. There is a difference (though to the external observer the two may appear similar). – Kristian Glass Mar 31 '12 at 11:22
  • @KristianGlass -- arrayWithObject by definition produces an autoreleased NSArray. array1 is definitely already autoreleased. – Hot Licks Mar 31 '12 at 11:50
  • One wonders what the purpose of the code in highRetainCount is. Why create an object, release it (auto or not), and the discard the pointer, without doing anything with it??? – Hot Licks Mar 31 '12 at 11:52
  • 2
    @HotLicks - No, it really doesn't, please go and read the documentation link in my answer. It returns an `NSArray*` that the caller does not own. It may perhaps be that the implementation creates an `NSArray`, autoreleases it and returns it, but it may also be that the implementation just returns a pointer to an existing instance. The API specifies nothing more than lack of ownership; you are making claims about the implementation. – Kristian Glass Mar 31 '12 at 11:53
  • @KristianGlass -- The only way it would not be autoreleased is if it's a "static" object, like a literal NSString. Otherwise the API must retain and autorelease it to assure it's continued existence, not knowing that the user is going to immediately discard it. Simply having an "existing instance" is not sufficient since that prior instance may evaporate at any time. – Hot Licks Mar 31 '12 at 11:57
  • 1
    @HotLicks Not when the existing instance is owned by the callee, c.f. internal memoisation – Kristian Glass Mar 31 '12 at 11:59
  • As to why stopped in that location in `main`, it's really stopped on the `[pool release];` statement, where the double autorelease was detected. – Hot Licks Mar 31 '12 at 12:01
  • @KristianGlass -- The user could easily release the "existing instance" -- `arrayWithObject` has no idea who "owns" what, or how long prior retained instances will continue to exist. And with something like `arrayWithObject` the object cannot be "permanently retained" in most cases (or in any way retained beyond what the user implies with `release`/`autorelease`) since proper release semantics are needed for the contained object. – Hot Licks Mar 31 '12 at 12:06
  • @HotLicks Except `NSArray` (or similar) would keep ownership of it in the case of memoisation – Kristian Glass Mar 31 '12 at 12:10
  • @KristianGlass I think he means the status of array1 was set to "autorelease", but it actually released at [pool release] in main(). – Shane Hou Mar 31 '12 at 12:48
  • @KristianGlass -- If NSArray kept ownership of instances containing views that would royally screw up the ability of the user to manage storage. – Hot Licks Mar 31 '12 at 13:08

1 Answers1

5

arrayWithObject: returns an object you do not own. Therefore it is wrong for you to subsequently send it autorelease.

See the Basic Memory Management Rules, specifically:

  • You must not relinquish ownership of an object you do not own

and

  • You own any object you create

You create an object using a method whose name begins with “alloc”, “new”, “copy”, or “mutableCopy” (for example, alloc, newObject, or mutableCopy).

Also, as a more general point, don't use retainCount. Unless you happen to be doing low-level hacking of the runtime or something, you don't need it, and it won't return anything of use to you.

Kristian Glass
  • 37,325
  • 7
  • 45
  • 73
  • retainCount is occasionally useful when trying to diagnose storage problems, but, as you suggest, should never be used in "real" code. – Hot Licks Mar 31 '12 at 11:53
  • 1
    I've yet to see an example of people trying to do that without relying on a variety of invalid-but-occasionally-coincidentally-working examples – Kristian Glass Mar 31 '12 at 11:57
  • I've used it on a number of occasions, for gnarly storage problems. You just have to understand how it all works first. – Hot Licks Mar 31 '12 at 12:07
  • 1
    I put it that if you actually understand how it all works, you wouldn't use it, c.f. http://www.friday.com/bbum/2011/12/18/retaincount-is-useless/ and http://stackoverflow.com/questions/5784084/calling-retaincount-considered-harmful – Kristian Glass Mar 31 '12 at 12:14
  • @Hot Licks :I got the point. So array1 is an object managed by system, when [pool release], it released every objects in our program. In my case, I released array1 before, then system released it again, that caused the problem. THX for you two's answer! – Shane Hou Mar 31 '12 at 12:42
  • Actually, if you understand how it works then you know how to use it. – Hot Licks Mar 31 '12 at 13:06
  • 2
    If you know how it works, you know that you are wasting your time using retainCount because there is a better way that requires less or no code.... – bbum Mar 31 '12 at 17:24