Question

Currently I didn't use ARC in my app, and I tried to create a NSAlert object as a local variable, at the end of function, I didn't release it. I expected that the app crash with that, the function code is here.

 #define GLOBAL_VARIABLE      0

 NSString *msgText = [self.defaultValueTextFiled stringValue];
    NSString *informativeText = @"Informative Text";

  #if !GLOBAL_VARIABLE
        NSAlert *alertView = nil;
  #endif

    if (alertView == nil)
    {
        alertView = [[NSAlert alloc] init];
        [alertView setMessageText:msgText];
        [alertView setInformativeText:informativeText];

        NSTextField *accessory = [[NSTextField alloc] initWithFrame:NSMakeRect(0,0,200,22)];
        [accessory setStringValue:@"accessory result"];
        [alertView setAccessoryView:accessory];
    }

    NSView *accessory= nil;
    NSInteger result = [alertView runModal];
    if (result == NSAlertAlternateReturn)
    {
        accessory = [alertView accessoryView];
        if (accessory != nil)
        {
            NSTextField *txtFiled = (NSTextField *)accessory;
            NSLog(@"%ld", [accessory retainCount]);
            NSString *str = [txtFiled stringValue];
            NSLog(@"%ld", [accessory retainCount]);
            [self.resultValueTextField setStringValue:str];
            NSLog(@"%ld", [accessory retainCount]);
        }
    }

Questions: (1) There is no [alertView release] in the end, why not crash? It has even no leaks.

(2) Refer to here , the accessory view shouldn't be release. However, I tried to release the view before [alertView runModal], then get its stringValue later, that could works. Why?

(3) The return value of function retainCount is interesting. When I created the alertView object, the retainedCount of alertView is 3. (Why?)

Before [alertView setAccessoryView:accessory], the accessory's retainedCount is 1, then it changed to 2 after executing it. That's normal and right. However, the log result for the above code, they're 20, 21, 22. How did they come from?

Thanks for your attention!

Was it helpful?

Solution 2

The rules for memory management are generally quite simple. Most problems come from overthinking them and trying to guess what other parts of the system are going to do, rather than just following the rules as written.

The first rule of Memory Management:

  1. Use ARC.

If you cannot follow rule one (for instance, you are developing for OS X 10.5 as I do), then here are the rules:

  1. You must balance each call you make to +alloc…, +new…, -…copy… or -retain with a call to -release or -autorelease.

That's really it. Everything else is really just commentary to help you follow that rule. So, you called [NSAlert alloc] and [NSTextField alloc], you need to call release on those objects.

Why doesn't it crash?

Because a leak is not going to crash unless it causes you to run out of memory.

Why doesn't it leak?

The most likely cause is that you're only running it once and then expecting Instruments to detect the leak. It may not show up if you only run it once. It depends on how NSAlert is internally implemented. Instruments finds leaks by walking all the pointers in the system and determining if any accessible pointers still reference a piece of allocated memory. There are many cases when a "leak is not a leak."

But this also suggests you're not running the static analyzer, because the analyzer should definitely have detected that leak. Run the static analyzer (Cmd-Shift-B) all the time and clean up what it finds.

the accessory view shouldn't be release.

That's not what the link you reference says. You shouldn't add an extra release. But in the referenced code, you'll notice that they release the view to balance their own +alloc.

… retainCount …

Never use -retainCount. Not even for debugging. Not even for anything else. There is no point at which retainCount is going to return you a piece of information that is going to be more enlightening than confusing. Why is it greater than you think it should be? Because other objects are retaining the alert view? Which objects? That's not your business. That's an internal implementation detail, subject to change. It could be pending autorelease calls, which show up temporarily as a "too high retainCount". It could be the run loop. It could be an internal controller. It could be anything. Calling retainCount tells you nothing useful.

Now that you understand manual memory management, switch to ARC and think about object graphs rather than retain counts. It is a much better way to deal with memory.

OTHER TIPS

  1. It does leak. Put a breakpoint on dealloc (or create a subclass of NSAlert and add a log statement to dealloc) to show this
  2. The accessry view should be released. You alloc it and then it would be retained by the alertView
  3. Here's a concise guide on when to use retainCount.
Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top