views:

79

answers:

1

I am creating an application and I get an EXC_BAD_ACCESS error.


CODE

@interface DNProjectsCategory : DNCategory {
  NSArray *projects;
}

@property(nonatomic, retain) NSArray *projects;

@end

And:

@implementation DNProjectsCategory
@synthesize projects;

// MEM

- (void)dealloc {
  [projects release];

  [super dealloc];
}

// INIT.
- (id)init {
  if (self = [super init]) {
    title = NSLocalizedString(@"PROJECTS", nil);
    isSubCategory = NO;

    // Initialize projects
    //!!LINE 32 IS HERE!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
    projects = [NSArray arrayWithContentsOfFile:DNPROJECTSFILE];
  }

  return self;
}

// CATEGORIES

- (NSArray *)subCategories {
  NSMutableArray *projectsArray = [[[NSMutableArray alloc] init] autorelease];

  for (NSDictionary *project in projects) {
    DNCategory *projectCategory = [[DNCategory alloc] initWithTitle:[project valueForKey:@"title"]
                                                      subCategories:nil
                                                      isSubCategory:YES];

    [projectsArray addObject:projectCategory];
    [projectCategory release];
  }

  return projectsArray;
}

CONTENTS OF DNPROJECTSFILE

See http://gist.github.com/618628


CONSOLE & INSTRUMENTS

This is what the Console says when ran (NSZombie is enabled):

run
[Switching to process 41257]
Running…
2010-10-09 23:32:36.899 Done[41257:a0f] *** -[CFString isKindOfClass:]: message sent to deallocated instance 0x1001caab0
sharedlibrary apply-load-rules all

Here is what Instruments says in an NSZombie test:

Zombie Messaged

An Objective-C message was sent to a deallocated object (zombie) at address: 0x10012af80.

 

Stack Trace

   0 CoreFoundation _CFRuntimeCreateInstance
   1 CoreFoundation __CFStringCreateImmutableFunnel3
   2 CoreFoundation CFStringCreateWithBytes
   3 CoreFoundation _uniqueStringForCharacters
   4 CoreFoundation getString
   5 CoreFoundation parseXMLElement
   6 CoreFoundation parseXMLElement
   7 CoreFoundation parseArrayTag
   8 CoreFoundation parseXMLElement
   9 CoreFoundation parsePListTag
  10 CoreFoundation parseXMLElement
  11 CoreFoundation _CFPropertyListCreateFromXMLStringError
  12 CoreFoundation _CFPropertyListCreateWithData
  13 CoreFoundation CFPropertyListCreateFromXMLData
  14 Foundation _NSParseObjectFromASCIIPropertyListOrSerialization
  15 Foundation +[NSArray(NSArray) newWithContentsOf:immutable:]
  16 Foundation +[NSArray(NSArray) arrayWithContentsOfFile:]
  17 Done -[DNProjectsCategory init] /Users/rsonic/Developer/Done/DNProjectsCategory.m:32
  18 Done -[DNBindingsController categories] /Users/rsonic/Developer/Done/DNBindingsController.m:18
  19 Foundation -[NSObject(NSKeyValueCoding) valueForKey:]
  20 Foundation -[NSObject(NSKeyValueCoding) valueForKeyPath:]
  21 AppKit -[NSBinder valueForBinding:resolveMarkersToPlaceholders:]
  22 AppKit -[NSArrayDetailBinder _refreshDetailContentInBackground:]
  23 AppKit -[NSObject(NSKeyValueBindingCreation) bind:toObject:withKeyPath:options:]
  24 AppKit -[NSIBObjectData nibInstantiateWithOwner:topLevelObjects:]
  25 AppKit loadNib
  26 AppKit +[NSBundle(NSNibLoading) _loadNibFile:nameTable:withZone:ownerBundle:]
  27 AppKit +[NSBundle(NSNibLoading) loadNibNamed:owner:]
  28 AppKit NSApplicationMain
  29 Done main /Users/rsonic/Developer/Done/main.m:13
  30 Done start

QUESTION

I really don't know how to fix this double-release. For as far as I know I don't release the projects variable anywhere except in dealloc. Can somebody help me, please? Thanks.

+5  A: 

You are not using the accessor, so projects is never retained. Two choices for your init method:

projects = [[NSArray arrayWithContentsOfFile:DNPROJECTSFILE] retain];

or

self.projects = [NSArray arrayWithContentsOfFile:DNPROJECTSFILE];

@property(nonatomic, retain) NSArray *projects;

the property "creates" two methods, a getter - (NSArray *)projects and a, in your case more important, setter - (void)setProjects:(NSArray*)a; The retain statement which you wrote in your property declaration only applies to the setter. If you set the variable directly with projects = foo; the setter is not used.
But self.projects = foo; is equivalent to [self setProject:foo], which is your dynamically created setter.
Your setter looks similar to this:

- (void)setProjects:(NSArray*)anArray {
    [anArray retain];
    [projects release];
    projects = anArray;
}

So if you use the setter, your autoreleased NSArray you got from arrayWithContentsOfFile: is retained.
Every call you make in Objective C that is not "alloc", "copy", "retain" or anything starting with new returns an autoreleased object. You have to retain those if you want to use them later (i.e. after you left the method where they were created).

Maybe you want to take another look at the Apple Memory Managment Guide

fluchtpunkt
It worked, but I don't really understand how. Could you explain it, please? I never understood how `retain` worked. ^^
Time Machine
@Koning: The `[NSArray array...]` methods return an instance that is automatically autoreleased. So sometime after the initializer is left, the instance is release by an autorelease pool. In order to prevent this you need to retain the instance to signal that you want/need to hold on to this instance. You can do so either by manually calling `retain`, or by assigning it to the property which does the retaining for you (since it has the retain flag).
DarkDust
@DarkDust awesome, thanks =D
Time Machine
@koning-baard I explained the other side of the retain-release-issues you had in my edit. HTH.
fluchtpunkt