I'm having a few problems with some Objective-C and would appreciate some pointers.
So I have a class MapFileGroup
which has the following simple interface (There are other member variables but they aren't important):
@interface MapFileGroup : NSObject {
NSMutableArray *mapArray;
}
@property (nonatomic, retain) NSMutableArray *mapArray;
mapArray
is @synthesize
'd in the .m file.
It has an init method:
-(MapFileGroup*) init
{
self = [super init];
if (self)
{
mapArray = [NSMutableArray arrayWithCapacity: 10];
}
return self;
}
It also has a method for adding a custom object to the array:
-(BOOL) addMapFile:(MapFile*) mapfile
{
if (mapfile == nil) return NO;
mapArray addObject:mapfile];
return YES;
}
The problem I get comes when I want to use this class - obviously due to a misunderstanding of memory management on my part.
In my view controller I declare as follows:
(in the @interface):
MapFileGroup *fullGroupOfMaps;
With @property @property (nonatomic, retain) MapFileGroup *fullGroupOfMaps;
Then in the .m file I have a function called loadMapData
that does the following:
MapFileGroup *mapContainer = [[MapFileGroup alloc] init];
// create a predicate that we can use to filter an array
// for all strings ending in .png (case insensitive) NSPredicate *caseInsensitivePNGFiles = [NSPredicate predicateWithFormat:@"SELF endswith[c] '.png'"];
mapNames = [unfilteredArray filteredArrayUsingPredicate:caseInsensitivePNGFiles];
[mapNames retain];
NSEnumerator * enumerator = [mapNames objectEnumerator];
NSString * currentFileName;
NSString *nameOfMap;
MapFile *mapfile;
while(currentFileName = [enumerator nextObject]) {
nameOfMap = [currentFileName substringToIndex:[currentFileName length]-4]; //strip the extension
mapfile = [[MapFile alloc] initWithName:nameOfMap];
[mapfile retain];
// add to array
[fullGroupOfMaps addMapFile:mapfile];
}
This seems to work ok (Though I can tell I've not got the memory management working properly, I'm still learning Objective-C); however, I have an (IBAction)
that interacts with the fullGroupOfMaps
later. It calls a method within fullGroupOfMaps
, but if I step into the class from that line while debugging, all fullGroupOfMaps
's objects are now out of scope and I get a crash.
So apologies for the long question and big amount of code, but 开发者_如何学PythonI guess my main question it:
How should I handle a class with an NSMutableArray as an instance variable? What is the proper way of creating objects to be added to the class so that they don't get freed before I'm done with them?
Many thanks
When you call [NSMutableArray arrayWithCapacity:]
, this actually creates an “autoreleased” array. This means that it will be released automatically at some time in the future. Usually, it will be released at the end of the current run loop.
What you want to do is create an array that is not autoreleased, because you want to explicitly release it when you're done with it. To do this, use:
mapArray = [[NSMutableArray alloc] initWithCapacity: 10];
Don't forget, you need to release it when you're done with it. Typically, if an instance variable is initialised in the class's initialiser, then it should be released in the class's dealloc
method, like this:
- (void) dealloc
{
[mapArray release];
[super dealloc]; // so that NSObject can clean up itself
}
Some methods return autoreleased objects as a convenience to you, so that you don't have to manage the memory yourself (it will be cleaned up automatically). In the case of instance variables, you almost never want them to be cleaned up by automatically.
You can also prevent an autoreleased object from being automatically released by retaining it again.
mapArray = [[NSMutableArray arrayWithCapacity:10] retain];
The Cocoa Memory Management Programming Guide has a few simple rules to follow about when you should explicitly release, and when you should explicitly retain. Keep the link bookmarked until the rules have become second nature and you'll have no troubles with Cocoa Memory Management. It is much simpler than it originally seems (or at least, that was the case for me).
精彩评论