Re: [Pyobjc-dev] Headache over hacking an XML editor
Brought to you by:
ronaldoussoren
From: Ronald O. <ous...@ci...> - 2003-01-14 22:32:38
|
On Tuesday, Jan 14, 2003, at 20:39 Europe/Amsterdam, bb...@ma... wrote: > On Tuesday, Jan 14, 2003, at 14:24 US/Eastern, Just van Rossum wrote: >> bb...@ma... wrote: >> >>>> self = cls.alloc() >>>> self.release() # ownership transferred to the Python >> wrapper >>>> return self >>> >>> This code is lacking the call to the -init method -- problematic. >> >> I was thinking of this pattern, which is not 100% Pythonic, yet 100% >> better than having to do a release() "by hand": >> >> inst = ASubclassOfNSObject().init() >> >> So basically "i = cls()" is equivalent to "i = cls.alloc(); >> i.release()". > > There are cases where this will not work -- Ronald has already had to > special case around a number of them. NSData, NSArray, and NSCell (I > think) are examples. Not because of bugs in Foundation/AppKit, but > because of the implementation pattern used. > > It would have to be something like.... > > i = cls.alloc() > i.autorelease() No, it is i.release(). the cls.alloc() returns an object where we own the reference, and then the bridge calls retain. We therefore have to call release to get the correct reference-count. I still think we can get this working >95% of the time, with the other <5% being methods that also return a new reference but that we don't know of. The current code to deal with this is slightly to simpleminded, but IMHO it is possible to correctly recognize an 'alloc + init' sequence and then adjust the reference count. The other documented cases of method returning a new reference can be processed by the current logic. The Apple Objective-C manual mentions about 5 methods that return new references, all others should return borrowed references. As Bill noted earlier real code doesn't always follow these conventions, but I think it would be sick to return a borrowed reference from an 'alloc + init' sequence. I hope to write down a design for this soonish, but I'd like to do some performance testing before that. > > ... anyway. (but won't work because of the reasons noted above) > >>> What about arguments to the constructor? We can handle the no >>> argument case transparently, but what about-- say-- NSView's >>> -initWithFrame: designated initializer? >> >> aView = NSView().initWithFrame(...) > > Makes sense. But als you note below not really feasable unless we'd use 'def __new__(cls): return cls.alloc()'. Hmm, I kind of like this, but we should document that you *must* call an init-method afterwards. The following objective-C code might crash: [[SomeClass alloc] release]. This does crash for some classes in Cocoa and according to Apple this is correct behaviour: Until you call an init method the instance is not instantiated and therefore any method-call might fail. > >>> Also, keep in mind that -init* methods return an object reference for >>> a reason; a number of intializers return a different instance than >>> the one that was called for a number of [valid] reasons. >> >> Solved above ;-) > > Except manipulating the retain count on the return value from +alloc* > leads to really nasty crashes (of which, some *are* bugs in the > Foundation/AppKit -- Ronald has reported a couple, I believe). According to Apple these are not bugs, you should only call an init method on the reference returned by +alloc. Me thinks this is a sad situation :-(. > >>>> This doesn't touch the alloc() semantics, yet provides a much more >>>> Pythonic way to create instances. Heck, this will even call >>>> __init__() for you! ;-) >>> >>> But __init__() is not generally the designated intiailizer for ObjC >>> classes-- even Python implemented subclasses of ObjC classes. >>> >>> When subclassing ObjC from Python, it makes more sense to follow the >>> ObjC semantics than the Python semantics -- at least, it does to me. >> >> Sure, but calling __init__ won't hurt either if you're writing an >> NSObject subclass solely to be instantiated from Python. But I'm >> afraid >> this behavior is implicit in the Python object protocol (since 2.2). I >> _think_ it's this: >> >> inst = cls.__new__(cls, *args, **kwargs) >> if isinstance(inst, cls): >> inst.__init__(*args, **kwargs) >> >> (I also think it's identical for tp_new/tp_init on the C side.) > > True. And I'm all for anything that can make the Python side of the > bridge more pythonic -- as long as it can be done consistently and > without introducing instability. If adding a __new__ method would cause __init__ to be called when the class is instantiated from Python, we should make sure that __init__ is also called when the class is instantiated from Objective-C, otherwise we'd get some very odd behaviour ('It works when I run my test script, but doesn't when I use it from Interface Builder'). Ronald |