Reputation: 2406
Mac OS X 10.6, Cocoa project, with retain/release gc
I've got a function which:
e.g. (error handling removed for brevity).
NSMutableArray * ListAllSubFoldersForFolderPath(NSString *folderPath)
{
NSMutableArray *a = [NSMutableArray arrayWithCapacity:100];
NSString *itemName = nil;
NSFileManager *fm = [NSFileManager defaultManager];
NSDirectoryEnumerator *e = [fm enumeratorAtPath:folderPath];
while (itemName = [e nextObject]) {
NSString *fullPath = [folderPath stringByAppendingPathComponent:itemName];
BOOL isDirectory;
if ([fm fileExistsAtPath:fullPath isDirectory:&isDirectory]) {
if (isDirectory is_eq YES) {
[a addObject: fullPath];
}
}
}
return a;
}
The calling function takes the array just once per session, keeps it around for later processing:
static NSMutableArray *gFolderPaths = nil;
...
gFolderPaths = ListAllSubFoldersForFolderPath(myPath);
[gFolderPaths retain];
All appears good at this stage. [gFolderPaths count] returns the correct number of paths found, and [gFolderPaths description] prints out all the correct path names.
The problem:
When I go to use gFolderPaths later (say, the next run through my event loop) my assertion code (and gdb in Xcode) tells me that it is nil.
I am not modifying gFolderPaths in any way after that initial grab, so I am presuming that my memory management is screwed and that gFolderPaths is being released by the runtime.
My assumptions/presumptions
I do not have to retain each string as I add it to the mutable array because that is done automatically, but I do have to retain the the array once it is handed over to me from the function, because I won't be using it immediately. Is this correct?
Any help is appreciated.
Upvotes: 0
Views: 107
Reputation: 96333
Objects do not “go nil
”.
static NSMutableArray *gFolderPaths = nil;
This declaration declares that gFolderPaths
is a variable that holds a pointer to an NSMutableArray object.
You initialize it with a pointer to no object: nil
.
This initialization is valid, and makes sense because you don't have an array to put here yet—better to initialize with the nil
pointer than to not initialize and risk some random pointer being in the variable. (That can't happen with a static
variable, as static
variables are initialized to nil
anyway, but explicitness is good and the explicit initialization is harmless.)
When I go to use
gFolderPaths
later (say, the next run through my event loop) my assertion code (and gdb in Xcode) tells me that it isnil
.I am not modifying
gFolderPaths
in any way after that initial grab, so I am presuming that my memory management is screwed and thatgFolderPaths
is being released by the runtime.
No. The runtime does not release objects. The runtime is part of the language, and retain
and release
are part of the Foundation framework. The framework sits just on top of the language.
So, you might guess that you or some other code (e.g., in a framework) released the object whose pointer you had previously stored in gFolderPaths
.
No. If that had happened, the gFolderPaths
variable would not suddenly contain nil
; it would still contain the same pointer to the same object. If this were the last release before the object's death, the gFolderPaths
variable would still contain the same pointer to the same, now-dead, object.
Attempting to log the pointer (e.g., with NSLog(@"%p", gFolderPaths)
) would print a valid-looking address, such as 0x2381ab6780
. Attempting to log the object (e.g., with %@
) would almost certainly crash, because the object is dead.
That's not what happened. You said that your assertion and your commands to the debugger revealed that the gFolderPaths
variable contains nil
.
There are two obvious possibilities:
nil
, or you never assigned anything. You say that you're logging the array whose pointer you assigned to the variable, and that the description checks out, so we can dismiss this possibility entirely. (Logging the count would not be so reliable a test, as [nil count]
will successfully return 0.)That leads to a third possibility:
3. You have two gFolderPaths
variables.
I'm guessing you have two functions or methods (or one of each) that both contain this line:
static NSMutableArray *gFolderPaths = nil;
That won't work. Both gFolderPaths
variables are static, but also local to the function/method you declared them in. Each function/method gets its own gFolderPaths
variable, so you have two such variables, separate from each other.
You need to declare gFolderPaths
as a static global variable, outside of any function or method. Better yet, if it is only being accessed from instances, make it an instance variable. Either way, it cannot be a local variable if you want to share it between two functions or methods.
The other way this could happen is if you have two such global declarations, but each in a different file. static
on a variable declared at file scope means “only visible within this file”, so this causes the same problem: Two separate variables when you mean to have one shared variable. If this is your problem, the immediate fix is to remove the static
keyword from both of them, but you should rethink your design if you mean to use a global variable in this way.
Upvotes: 2