[Pyobjc-dev] Bug Fixed!!! Dealing with alloc / init oddities
Brought to you by:
ronaldoussoren
|
From: Bill B. <bb...@co...> - 2002-10-24 14:32:15
|
(Another "debugging out loud" session -- but this one ended happily.
I'll put the 'solution' first and those interested can read down for
the details that led to it...)
#if 1
self_obj = nil;
if (*[methinfo methodReturnType] == _C_ID) {
[inv setReturnValue:&self_obj];
}
[inv setTarget:self_obj];
[inv setArgument:&self_obj atIndex:0];
#endif
[inv release];
[methinfo release];
inv = nil;
The above is found in objc_support.m around line 1423. By changing
the "#if 1" to an "#if 0", the bug goes away and all unit tests pass
(except invocation of class methods when an instance method of the same
name also exists).
The code #if'd in/out looks to be there to force the NSInvocation to
clean up after itself. In theory, that should be handled by the [inv
-release] on the line immediately after that block of code. In
practice, this still shouldn't cause a crash assuming that the
NSInvocation only retains things that it releases.
Why the crash was happening isn't clear to me. The [lack of the
above] code may also potentially be causing a memory leak?
Change committed.
---
I believe I have tracked the crasher w/the collection classes down to
being related to the following behavior of NSArray:
int main (int argc, const char * argv[]) {
NSAutoreleasePool * pool = [[NSAutoreleasePool alloc] init];
NSArray *a;
a = [NSMutableArray alloc];
NSLog(@"0x%x", a);
a = [a init];
NSLog(@"0x%x", a);
a = [NSMutableArray alloc];
NSLog(@"0x%x", a);
a = [a init];
NSLog(@"0x%x", a);
[pool release];
return 0;
}
The above outputs -- of note, If I change the second [NSMutableArray
alloc] to [NSArray alloc], the output remains *unchanged*.
2002-10-24 09:35:37.398 barfoo[19606] 0x4b580
2002-10-24 09:35:37.424 barfoo[19606] 0x57c90
2002-10-24 09:35:37.439 barfoo[19606] 0x4b580
2002-10-24 09:35:37.452 barfoo[19606] 0x57cb0
Clearly, the Foundation/Core are playing games with the
NSArray/NSMutableArray class clusters. The +alloc methods return
placeholders that are turned into the appropriate instance upon
initialization. This makes sense given that the -init* method used
determines the role and, hence, the appropriate private subclass, of
the instance of the class cluster.
Now, consider the following:
[bumbox:~/bbum-developer/sourceforge/pyobjc] bbum% python
>>> from Foundation import *
>>> NSArray.alloc().init()
()
>>> NSMutableArray.alloc().init()
()
>>> NSArray.alloc().init()
2002-10-24 09:37:46.189 python[19612] Did you forget to nest alloc and
init?
2002-10-24 09:37:46.191 python[19612] *** Uncaught exception:
<NSInvalidArgumentException> *** -length only defined for abstract
class. Define -[NSPlaceholderMutableString length]!
It appears that the bridge somehow caches the placeholder instance and
ends up releasing or corrupting it upon the second call to the alloc()
method *for that particular class*?
Given that last bug, it seems like the shared placeholder array
instance may be being released one time to many??
It is! (see above)
|