Commits

Ronald Oussoren committed 0b86b9c

- It is now possible to override ``dealloc``. It is still possible to
define ``__del__``.

- As an experimental feature it is also possible to override ``retain`` and
``release``. Note it almost never a good idea to do this (even when you're
programming in Objective-C and much more so in Python).

- ``poseAsClass:`` can be used, although it is not very useful in python, use
categories instead.

A major issue with ``poseAsClass:`` is that existing references to the old
version of the class won't be changed to point to the new class.

- Some cleanup of class-builder.m (side effect of the first two items)

- Don't use deprecated API's of Object and Protocol in the unittests. The
tests still generate a warning, but that's a bug in NSIsFreedObject (which
is used in one of the tests)

- It is now possible to access all instance variables of a class using
the functions ``objc.listInstanceVariables(aClassOrInstance)``,
``objc.getInstanceVariable(obj, name)`` and
``objc.setInstanceVariable(obj, name, value [, updateRefCount])``.

- objc_support.m can now deal with type-strings for instance variables,
the typestring for a struct contains field names for an instance variable
but not in other situations.

Comments (0)

Files changed (23)

 </ul>
 <ul>
 <li>Also restructure class-builder.m, this file is way to large.</li>
-<li>Allow ivars of Objective-C classes to be introspected
-(but not directly from the __dict__?)<p>NOTE: instance-var.[hm] implements most of this, it just needs to be
-wired up correctly. I'd do this by adding <code><span>pyobjc_instancevars</span></code> to
-instances (simular to <code><span>pyobjc_instanceMethods</span></code>). They cannot be added
-directly to the <code><span>__dict__</span></code> because of this common idiom:</p>
-<pre>
-@interface Foo : NSObject
-{
-   int myvar;
-}
--(int)myvar;
--(void)setMyvar:(int)value;
-@end
-</pre>
-</li>
 <li>We've several types that wrap pointer values, such as <code><span>SessionWrapper</span></code> in
 <code><span>Modules/AppKit</span></code>. Should add a function for creating these types, 
 possibly exposed to C code.</li>
 <li>SessionWrapper (in <code><span>Modules/AppKit</span></code>) is one of several types that wrap 
 a single pointer, create a function to build such types (similar to
 <code><span>Modules/objc/struct-wrapper.m</span></code>)</li>
-<li>Use the <code><span>CArray</span></code> API whenever appropriate.</li>
 </ul>
 <h3><a href="#id15" name="cleanup-examples">Cleanup Examples</a></h3>
 <p>The CurrencyConverter example should be removed, this should be the same as the
 
 * Also restructure class-builder.m, this file is way to large.
 
-* Allow ivars of Objective-C classes to be introspected
-  (but not directly from the __dict__?)
-
-  NOTE: instance-var.[hm] implements most of this, it just needs to be
-  wired up correctly. I'd do this by adding ``pyobjc_instancevars`` to
-  instances (simular to ``pyobjc_instanceMethods``). They cannot be added
-  directly to the ``__dict__`` because of this common idiom::
-
-    @interface Foo : NSObject
-    {
-       int myvar;
-    }
-    -(int)myvar;
-    -(void)setMyvar:(int)value;
-    @end
-
 * We've several types that wrap pointer values, such as ``SessionWrapper`` in
   ``Modules/AppKit``. Should add a function for creating these types, 
   possibly exposed to C code.
   a single pointer, create a function to build such types (similar to
   ``Modules/objc/struct-wrapper.m``)
 
-* Use the ``CArray`` API whenever appropriate.
-
 Cleanup Examples
 ................
 
 <p>In Python you can use the method <code><span>__del__</span></code> to clean up resources when your
 object is garbage collected. In Objective-C/Cocoa this is done with a method 
 named <code><span>dealloc</span></code>.</p>
-<p>In PyObjC you should always use the <code><span>__del__</span></code> method, the <code><span>dealloc</span></code> method
-can safely be ignored and the bridge will complain when you try to override
-this method.</p>
+<p>In Python subclasses of Objective-C classes you can use either convention. You
+should pick one convention for your own code, the order in which <code><span>__del__</span></code>
+and <code><span>dealloc</span></code> methods are called is undefined.</p>
 <h3><a href="#id19" name="copying">Copying</a></h3>
 <p>It is possible to implement the <code><span>NSCopying</span></code> protocol in your classes. Some
 care must be taken when you inherit from a class that already implements 
 object is garbage collected. In Objective-C/Cocoa this is done with a method 
 named ``dealloc``. 
 
-In PyObjC you should always use the ``__del__`` method, the ``dealloc`` method
-can safely be ignored and the bridge will complain when you try to override
-this method.
+In Python subclasses of Objective-C classes you can use either convention. You
+should pick one convention for your own code, the order in which ``__del__``
+and ``dealloc`` methods are called is undefined.
 
 Copying
 .......

Lib/objc/_convenience.py

     ( 'initWithObjectsAndKeys_', initWithObjectsAndKeys_ ),
 )
 
-def UnsupportedMethod(self, *args):
-    raise ValueError, "Unsupported method"
-
-CONVENIENCE_METHODS['poseAsClass:'] = (
-    ('poseAsClass_', (UnsupportedMethod)),
-)
-
 def sort(self, cmpfunc=cmp):
     def doCmp(a, b, cmpfunc):
         return cmpfunc(a, b)

Lib/objc/test/test_ivar.py

 
 import objc
 import sys
+from objc.test.instanceVariables import ClassWithVariables
 
 NSObject = objc.lookUpClass('NSObject')
 
     def testDelete(self):
         self.assertRaises(TypeError, delattr, self.object.idVar)
 
+
+class TestAllInstanceVariables (unittest.TestCase):
+    # Some tests for accessing any instance variable, even those not 
+    # declared in python.
+
+    def assertInstanceOf(self, obj, cls):
+        self.assert_(isinstance(obj, cls))
+
+    def testReading(self):
+        obj = ClassWithVariables.alloc().init()
+
+        getter = objc.getInstanceVariable
+
+        cls = getter(obj, 'isa')
+        self.assert_(cls is type(obj))
+
+        self.assertEquals(getter(obj, 'intValue'), 42)
+        self.assertInstanceOf(getter(obj, 'intValue'), int)
+
+        self.assertEquals(getter(obj, 'floatValue'), -10.055)
+        self.assertInstanceOf(getter(obj, 'floatValue'), float)
+
+        self.assertEquals(getter(obj, 'charValue'), ord('a'))
+        self.assertInstanceOf(getter(obj, 'charValue'), int)
+
+        self.assertEquals(getter(obj, 'strValue'), "hello world")
+        self.assertInstanceOf(getter(obj, 'strValue'), str)
+
+        self.assertInstanceOf(getter(obj, 'objValue'), NSObject)
+
+        self.assert_(getter(obj, 'nilValue') is None)
+
+        self.assertEquals(getter(obj, 'pyValue'), slice(1, 10, 4))
+        self.assertInstanceOf(getter(obj, 'pyValue'), slice)
+
+        self.assertEquals(getter(obj, 'rectValue'), ((1, 2), (3, 4)))
+
+        self.assertRaises(AttributeError, getter, obj, "noSuchMember")
+
+    def testWriting(self):
+        obj = ClassWithVariables.alloc().init()
+
+        getter = objc.getInstanceVariable
+        setter = objc.setInstanceVariable
+
+        self.assertEquals(getter(obj, 'intValue'), 42)
+        setter(obj, 'intValue', 99)
+        self.assertEquals(getter(obj, 'intValue'), 99)
+
+        self.assertEquals(getter(obj, 'floatValue'), -10.055)
+        setter(obj, 'floatValue', 0.5)
+        self.assertEquals(getter(obj, 'floatValue'), 0.5)
+
+        self.assertEquals(getter(obj, 'charValue'), ord('a'))
+        setter(obj, 'charValue', 'b')
+        self.assertEquals(getter(obj, 'charValue'), ord('b'))
+        setter(obj, 'charValue', 10)
+        self.assertEquals(getter(obj, 'charValue'), 10)
+
+        self.assertEquals(getter(obj, 'strValue'), "hello world")
+        setter(obj, 'strValue', "foo bar")
+        self.assertEquals(getter(obj, 'strValue'), "foo bar")
+        setter(obj, 'strValue', None)
+        self.assertEquals(getter(obj, 'strValue'), None)
+
+        o = NSObject.new()
+        self.assert_(getter(obj, 'objValue') is not o)
+        self.assertRaises(TypeError, setter, 'objValue', o)
+        self.assert_(getter(obj, 'objValue') is not o)
+        setter(obj, 'objValue', o, True)
+        self.assert_(getter(obj, 'objValue') is o)
+
+        o2 = NSObject.new()
+        o2.retain()
+        self.assert_(getter(obj, 'objValue') is not o2)
+        setter(obj, 'objValue', o2, False)
+        self.assert_(getter(obj, 'objValue') is o2)
+
+        self.assertEquals(getter(obj, 'pyValue'), slice(1, 10, 4))
+        setter(obj, 'pyValue', [1,2,3])
+        self.assertEquals(getter(obj, 'pyValue'), [1,2,3])
+
+        self.assertEquals(getter(obj, 'rectValue'), ((1, 2), (3, 4)))
+        setter(obj, 'rectValue', ((-4, -8), (2, 7)))
+        self.assertEquals(getter(obj, 'rectValue'), ((-4, -8), (2, 7)))
+
+        self.assertRaises(AttributeError, setter, obj, "noSuchMember", 'foo')
+
+    def testClassMod(self):
+        # It's scary as hell, but updating the class of an object does "work"
+        # (for some perverted interpretation of the word)
+
+        class DummyClass (NSObject):
+            __slots__ = ()
+
+        o = NSObject.alloc().init()
+        self.assert_(isinstance(o, NSObject))
+        self.assert_(not isinstance(o, DummyClass))
+
+        objc.setInstanceVariable(o, "isa", DummyClass)
+        self.assert_(isinstance(o, DummyClass))
+
+    def testDir(self):
+        obj = ClassWithVariables.alloc().init()
+
+        # Note: cannot check the exact contents of dir(), who knows
+        # what NSObject defines...
+        v = objc.listInstanceVariables(obj)
+        self.assert_(('charValue', objc._C_CHR) in v)
+        self.assert_(('intValue', objc._C_INT) in v)
+        self.assert_(('isa', objc._C_CLASS) in v)
+
 if __name__ == '__main__':
     unittest.main()

Lib/objc/test/test_posing.py

             def testPosingMethod(self):
                 return "<PoseClass instance>"
 
-        self.assertRaises(ValueError, PoseClass.poseAsClass_, BaseClass)
 
-        # Whoops, this is a problem: We keep referencing the old class!
-        #obj = objc.lookUpClass(BaseName).new()
-        #obj = objc.runtime.__getattr__(BaseName).alloc().init()
+        PoseClass.poseAsClass_(BaseClass)
+
+        # BaseClass still refers to the old class, if we look it up again
+        # we get to see the new value. There's not much we can do about that.
+        obj = objc.lookUpClass(BaseName).new()
+        self.assertEquals(obj.testPosingMethod(), "<PoseClass instance>")
+
+        # XXX: next assertion fails because the runtime seems to copy the
+        # original class.
         #self.assert_(isinstance(obj, PoseClass))
-        #self.assertEquals(obj.testPosingMethod(), "<PoseClass instance>")
-        #del obj
+        self.assertNotEquals(BaseClass.__name__, BaseName)
+        self.assertEquals(PoseClass.__name__, BaseName)
+        del obj
 
 
 

Lib/objc/test/test_splitsig.py

         self.assertEquals(objc.splitSignature("@:10{NSRect=ff}"), ('@',':','{NSRect=ff}'))
         self.assertEquals(objc.splitSignature("@:o^@"), ('@',':','o^@'))
 
+        # struct definition in an struct objc_ivar
+        self.assertEquals(objc.splitSignature('{_NSRect="origin"{_NSPoint="x"f"y"f}"size"{_NSSize="width"f"height"f}}'), ('{_NSRect="origin"{_NSPoint="x"f"y"f}"size"{_NSSize="width"f"height"f}}',))
+
     def testSignatureCount(self):
         EXCEPTIONS=[
 

Lib/objc/test/test_subclass.py

 # Most useful systems will at least have 'NSObject'.
 NSObject = objc.lookUpClass('NSObject')
 NSArray = objc.lookUpClass('NSArray')
+NSAutoreleasePool = objc.lookUpClass('NSAutoreleasePool')
 
 class TestSubclassing(unittest.TestCase):
     def testMethodRaise(self):
 
         self.assert_(isinstance(StaticMethodTest.stMeth, types.FunctionType))
 
+
+class TestOverridingSpecials(unittest.TestCase):
+    def testOverrideSpecialMethods(self):
+        aList = [0]
+
+        class ClassWithAlloc(NSObject):
+            def alloc(cls):
+                aList[0] += 1
+                return super(ClassWithAlloc, cls).alloc()
+
+            
+        self.assertEquals(aList[0], 0)
+        o = ClassWithAlloc.alloc().init()
+        self.assertEquals(aList[0], 1)
+        self.assert_(isinstance(o, NSObject))
+        del o
+
+        class ClassWithRetaining(NSObject):
+            def retain(self):
+                aList.append('retain')
+                return super(ClassWithRetaining, self).retain()
+
+            def release(self):
+                aList.append('release')
+                return super(ClassWithRetaining, self).release()
+
+            def __del__(self):
+                aList.append('__del__')
+
+        del aList[:]
+        o = ClassWithRetaining.alloc().init()
+        v = o.retainCount()
+        o.retain()
+        self.assertEquals(aList, ['retain'])
+        self.assertEquals(o.retainCount(), v+1)
+        o.release()
+        self.assertEquals(aList, ['retain', 'release'])
+        self.assertEquals(o.retainCount(), v)
+        del o
+
+        self.assertEquals(aList, ['retain', 'release', 'release', '__del__'])
+
+        # Test again, now remove all python references and create one
+        # again.
+        del aList[:]
+        pool = NSAutoreleasePool.alloc().init()
+        o = ClassWithRetaining.alloc().init()
+        v = NSArray.arrayWithArray_([o])
+        del o
+        self.assertEquals(aList, ['retain'])
+        o = v[0]
+        self.assertEquals(aList, ['retain'])
+        del v
+        del o
+        del pool
+        
+        self.assertEquals(aList, ['retain', 'release', 'release', '__del__'])
+
+        class ClassWithRetainCount(NSObject):
+            def retainCount(self):
+                aList.append('retainCount')
+                return super(ClassWithRetainCount, self).retainCount()
+        
+        del aList[:]
+        o = ClassWithRetainCount.alloc().init()
+        self.assertEquals(aList, [])
+        v = o.retainCount()
+        self.assert_(isinstance(v, int))
+        self.assertEquals(aList, ['retainCount'])
+        del o
+
+    def testOverrideDealloc(self):
+        aList = []
+
+        class Dummy:
+            def __del__(self):
+                aList.append('__del__')
+
+        self.assertEquals(aList, [])
+        Dummy()
+        self.assertEquals(aList, ['__del__'])
+
+        class ClassWithDealloc(NSObject):
+            def init(self):
+                self = super(ClassWithDealloc, self).init()
+                if self is not None:
+                    self.obj = Dummy()
+                return self
+
+            def dealloc(self):
+                aList.append('dealloc')
+                return super(ClassWithDealloc, self).dealloc()
+
+        del aList[:]
+        o = ClassWithDealloc.alloc().init()
+        self.assertEquals(aList, [])
+        del o
+        self.assertEquals(len(aList), 2)
+        self.assert_('dealloc' in aList)
+        self.assert_('__del__' in aList)
+
+        class SubClassWithDealloc(ClassWithDealloc):
+            def dealloc(self):
+                aList.append('dealloc.dealloc')
+                return super(SubClassWithDealloc, self).dealloc()
+
+        del aList[:]
+        o = SubClassWithDealloc.alloc().init()
+        self.assertEquals(aList, [])
+        del o
+        self.assertEquals(len(aList), 3)
+        self.assert_('dealloc.dealloc' in aList)
+        self.assert_('dealloc' in aList)
+        self.assert_('__del__' in aList)
+
+        class ClassWithDeallocAndDel(NSObject):
+            def init(self):
+                self = super(ClassWithDeallocAndDel, self).init()
+                if self is not None:
+                    self.obj = Dummy()
+                return self
+
+            def dealloc(self):
+                aList.append('dealloc')
+                return super(ClassWithDeallocAndDel, self).dealloc()
+
+            def __del__(self):
+                aList.append('mydel')
+
+        del aList[:]
+        o = ClassWithDeallocAndDel.alloc().init()
+        self.assertEquals(aList, [])
+        del o
+        self.assertEquals(len(aList), 3)
+        self.assert_('mydel' in aList)
+        self.assert_('dealloc' in aList)
+        self.assert_('__del__' in aList)
+
+
 if __name__ == '__main__':
     unittest.main()

Modules/objc/alloc_hack.m

 	IMP anIMP;
 	Class aClass;
 	SEL volatile aSel;
-	PyObject* v;
 
 	if (PyArg_ParseTuple(arguments, "") < 0) {
 		return NULL;
 	PyObjC_END_WITH_GIL
 }
 
+static PyObject*
+call_NSObject_dealloc(PyObject* method, 
+	PyObject* self, PyObject* arguments)
+{
+	struct objc_super super;
+	IMP anIMP;
+	Class aClass;
+	SEL volatile aSel;
+
+	if (PyArg_ParseTuple(arguments, "") < 0) {
+		return NULL;
+	}
+
+	if (!PyObjCObject_Check(self)) {
+		PyErr_SetString(PyExc_TypeError, "Expecting object");
+		return NULL;
+	}
+
+	if (PyObjCIMP_Check(method)) {
+		anIMP = PyObjCIMP_GetIMP(method);
+		aClass = PyObjCClass_GetClass(self);
+		aSel = PyObjCIMP_GetSelector(method);
+
+		PyObjC_DURING
+			(void)anIMP(aClass, aSel);
+
+		PyObjC_HANDLER
+			PyObjCErr_FromObjC(localException);
+
+		PyObjC_ENDHANDLER;
+
+	} else {
+		RECEIVER(super) = PyObjCObject_GetObject(self);
+		super.class = PyObjCSelector_GetClass(method); 
+		aSel = PyObjCSelector_GetSelector(method);
+
+		PyObjC_DURING
+			(void)objc_msgSendSuper(&super, aSel); 
+
+		PyObjC_HANDLER
+			PyObjCErr_FromObjC(localException);
+
+		PyObjC_ENDHANDLER;
+	}
+
+	PyObjCObject_ClearObject(self);
+
+	if (PyErr_Occurred()) {
+		return NULL;
+	}
+
+
+	Py_INCREF(Py_None);
+	return Py_None;
+}
+
+static void 
+imp_NSObject_dealloc(
+	ffi_cif* cif __attribute__((__unused__)), 
+	void* resp __attribute__((__unused__)),  
+	void** args __attribute__((__unused__)), 
+	void* callable)
+{
+	PyObject* arglist = NULL;
+	PyObject* v = NULL;
+	PyObject* result = NULL;
+
+	PyObjC_BEGIN_WITH_GIL
+
+		arglist = PyTuple_New(1);
+		if (arglist == NULL) {
+			PyObjC_GIL_FORWARD_EXC();
+		}
+
+		v = PyObjC_IdToPython(*(id*)args[0]);
+		if (v == NULL) {
+			Py_DECREF(arglist);
+			PyObjC_GIL_FORWARD_EXC();
+		}
+
+		PyTuple_SET_ITEM(arglist, 0, v);
+		v = NULL;
+
+		result = PyObject_Call((PyObject*)callable, arglist, NULL);
+		if (result == NULL) {
+			Py_DECREF(arglist);
+			PyObjC_GIL_FORWARD_EXC();
+		}
+
+		Py_DECREF(arglist); 
+
+		/* TODO: assert result is None */
+		Py_DECREF(result);
+
+	PyObjC_END_WITH_GIL
+}
+
+static PyObject*
+call_NSObject_release(PyObject* method, 
+	PyObject* self, PyObject* arguments)
+{
+	struct objc_super super;
+	IMP anIMP;
+	Class aClass;
+	SEL volatile aSel;
+
+	if (PyArg_ParseTuple(arguments, "") < 0) {
+		return NULL;
+	}
+
+	if (!PyObjCObject_Check(self)) {
+		PyErr_SetString(PyExc_TypeError, "Expecting object");
+		return NULL;
+	}
+
+	if (PyObjCIMP_Check(method)) {
+		anIMP = PyObjCIMP_GetIMP(method);
+		aClass = PyObjCClass_GetClass(self);
+		aSel = PyObjCIMP_GetSelector(method);
+
+		PyObjC_DURING
+			(void)anIMP(aClass, aSel);
+
+		PyObjC_HANDLER
+			PyObjCErr_FromObjC(localException);
+
+		PyObjC_ENDHANDLER;
+
+	} else {
+		RECEIVER(super) = PyObjCObject_GetObject(self);
+		super.class = PyObjCSelector_GetClass(method); 
+		aSel = PyObjCSelector_GetSelector(method);
+
+		PyObjC_DURING
+			(void)objc_msgSendSuper(&super, aSel); 
+
+		PyObjC_HANDLER
+			PyObjCErr_FromObjC(localException);
+
+		PyObjC_ENDHANDLER;
+	}
+
+	if (PyErr_Occurred()) {
+		return NULL;
+	}
+
+
+	Py_INCREF(Py_None);
+	return Py_None;
+}
+
+static PyObject*
+call_NSObject_retain(PyObject* method, 
+	PyObject* self, PyObject* arguments)
+{
+	struct objc_super super;
+	IMP anIMP;
+	Class aClass;
+	SEL volatile aSel;
+	id retval;
+
+	if (PyArg_ParseTuple(arguments, "") < 0) {
+		return NULL;
+	}
+
+	if (!PyObjCObject_Check(self)) {
+		PyErr_SetString(PyExc_TypeError, "Expecting object");
+		return NULL;
+	}
+
+	if (PyObjCIMP_Check(method)) {
+		anIMP = PyObjCIMP_GetIMP(method);
+		aClass = PyObjCClass_GetClass(self);
+		aSel = PyObjCIMP_GetSelector(method);
+
+		PyObjC_DURING
+			retval = anIMP(aClass, aSel);
+
+		PyObjC_HANDLER
+			PyObjCErr_FromObjC(localException);
+
+		PyObjC_ENDHANDLER;
+
+	} else {
+		RECEIVER(super) = PyObjCObject_GetObject(self);
+		super.class = PyObjCSelector_GetClass(method); 
+		aSel = PyObjCSelector_GetSelector(method);
+
+		PyObjC_DURING
+			retval = objc_msgSendSuper(&super, aSel); 
+
+		PyObjC_HANDLER
+			PyObjCErr_FromObjC(localException);
+
+		PyObjC_ENDHANDLER;
+	}
+
+	if (PyErr_Occurred()) {
+		return NULL;
+	}
+
+	return PyObjC_IdToPython(retval);
+}
+
+static void 
+imp_NSObject_release(
+	ffi_cif* cif __attribute__((__unused__)), 
+	void* resp __attribute__((__unused__)),  
+	void** args __attribute__((__unused__)), 
+	void* callable)
+{
+	PyObject* arglist = NULL;
+	PyObject* v = NULL;
+	PyObject* result = NULL;
+
+	PyObjC_BEGIN_WITH_GIL
+
+		arglist = PyTuple_New(1);
+		if (arglist == NULL) {
+			PyObjC_GIL_FORWARD_EXC();
+		}
+
+		v = PyObjCObject_NewClassic(*(id*)args[0]);
+		//v = PyObjC_IdToPython(*(id*)args[0]);
+		if (v == NULL) {
+			Py_DECREF(arglist);
+			PyObjC_GIL_FORWARD_EXC();
+		}
+
+		PyTuple_SET_ITEM(arglist, 0, v);
+		v = NULL;
+
+		result = PyObject_Call((PyObject*)callable, arglist, NULL);
+		if (result == NULL) {
+			Py_DECREF(arglist);
+			PyObjC_GIL_FORWARD_EXC();
+		}
+
+		Py_DECREF(arglist); 
+
+		/* TODO: assert result is None */
+		Py_DECREF(result);
+
+	PyObjC_END_WITH_GIL
+}
+
+static void 
+imp_NSObject_retain(
+	ffi_cif* cif __attribute__((__unused__)), 
+	void* resp __attribute__((__unused__)),  
+	void** args __attribute__((__unused__)), 
+	void* callable)
+{
+	PyObject* arglist = NULL;
+	PyObject* v = NULL;
+	PyObject* result = NULL;
+	int err;
+
+	PyObjC_BEGIN_WITH_GIL
+
+		arglist = PyTuple_New(1);
+		if (arglist == NULL) {
+			PyObjC_GIL_FORWARD_EXC();
+		}
+
+		v = PyObjCObject_NewClassic(*(id*)args[0]);
+		if (v == NULL) {
+			Py_DECREF(arglist);
+			PyObjC_GIL_FORWARD_EXC();
+		}
+
+		PyTuple_SET_ITEM(arglist, 0, v);
+		v = NULL;
+
+		result = PyObject_Call((PyObject*)callable, arglist, NULL);
+		if (result == NULL) {
+			Py_DECREF(arglist);
+			PyObjC_GIL_FORWARD_EXC();
+		}
+
+		Py_DECREF(arglist); 
+
+		err = depythonify_c_value(@encode(id), result, resp);
+		Py_DECREF(result); 
+		if (err == -1) {
+			PyObjC_GIL_FORWARD_EXC();
+		}
+
+	PyObjC_END_WITH_GIL
+}
+
 
 int
 PyObjC_InstallAllocHack(void)
 		imp_NSObject_alloc);
 	if (r != 0) return r;
 
+	r = PyObjC_RegisterMethodMapping(
+		PyObjCRT_LookUpClass("NSObject"),
+		@selector(dealloc),
+		call_NSObject_dealloc,
+		imp_NSObject_dealloc);
+	if (r != 0) return r;
+
+	r = PyObjC_RegisterMethodMapping(
+		PyObjCRT_LookUpClass("NSObject"),
+		@selector(retain),
+		call_NSObject_retain,
+		imp_NSObject_retain);
+	if (r != 0) return r;
+
+	r = PyObjC_RegisterMethodMapping(
+		PyObjCRT_LookUpClass("NSObject"),
+		@selector(release),
+		call_NSObject_release,
+		imp_NSObject_release);
+	if (r != 0) return r;
+
+	r = PyObjC_RegisterMethodMapping(
+		PyObjCRT_LookUpClass("NSObject"),
+		@selector(autorelease),
+		call_NSObject_release,
+		imp_NSObject_release);
+	if (r != 0) return r;
+
 	return r;
 }

Modules/objc/class-builder.h

  *    1) Collect the necessary information (name, bases and class_dict)
  *    2) Call PyObjCClass_BuildClass
  *    3) Create the Python class (using type.__new__)
- *    4) Call PyObjCClass_SetClass
+ *    4) Call PyObjCClass_FinishClass
  *
  *    If step 3 fails: call PyObjCClass_UnbuildClass
  *
  * @function PyObjCClass_UnbuildClass
  * @abstract Undo the work of PyObjCClass_BuildClass
  * @param Class A class created by PyObjCClass_BuildClass
+ * @result 0 on success, -1 on failure
  * @discussion
  *    This function destroys the class created by PyObjCClass_BuildClass. This
- *    function can only be called when PyObjCClass_SetClass has not been called
- *    for the class.
+ *    function can only be called when PyObjCClass_FinishClass has not been 
+ *    called for the class.
  *
  *    This limitation is necessary because it is not possible to remove classes
  *    from the Objetive-C runtime on MacOS X.
  */
-void PyObjCClass_UnbuildClass(Class new_class);
+int PyObjCClass_UnbuildClass(Class new_class);
 
 
 /*!
- * @function PyObjCClass_SetClass
+ * @function PyObjCClass_FinishClass
  * @abstract Register the class in the Objective-C runtime
  * @param objc_class A class created by PyObjCClass_BuildClass
- * @param py_class   The python class corresponding with objc_class
  * @result Returns 0 on success, -1 on failure.
  * @discussion
  *    This function updates the bookkeeping information for objc_class and
  *    then registers the class with the Objective-C runtime.
  */
-int PyObjCClass_SetClass(Class objc_class, PyObject* py_class);
+int PyObjCClass_FinishClass(Class objc_class);
 
 #endif /* OBJC_CLASS_BUILDER */

Modules/objc/class-builder.m

 #import <Foundation/NSKeyValueObserving.h>
 #endif
 
-/* List of instance variables, methods and class-methods that should not
- * be overridden from python
- */
-static char* dont_override_methods[] = {
-	"alloc",
-	"dealloc",
-	"retain",
-	"release",
-	"autorelease",
-	"retainCount",
-	NULL
-};
-
 /* Special methods for Python subclasses of Objective-C objects */
 static void object_method_dealloc(
 		ffi_cif* cif,
 
 /*
  * When we create a 'Class' we actually create the struct below. This allows
- * us to add some extra information to the class defintion.
+ * us to add some extra information to the class definition.
  *
- * NOTE1: the meta_class field is first because poseAs: copies the class but
- *        not the meta class (on MacOS X <= 10.2)
- * NOTE2: That doesn't help, test_posing still crashes.
- * XXX: Is this still relevant? The current code only refers to the struct
- *      during the construction of the class, which means poseAs: probably 
- *      works just fine (to be checked after 1.1)
+ * XXX: The struct is not really necessary, it just makes error-recovery 
+ * slightly easier. 
  */
-#define MAGIC 0xDEADBEEF
-#define CLASS_WRAPPER(cls) ((struct class_wrapper*)(cls))
-#define CHECK_MAGIC(o) do { if (CLASS_WRAPPER(o)->magic != MAGIC) abort(); } while(0)
 struct class_wrapper {
 	struct objc_class class;
 	struct objc_class meta_class;
-	PyObject* python_class;
-	unsigned int magic; 
 };
 
 #define IDENT_CHARS "ABCDEFGHIJKLMNOPQSRTUVWXYZabcdefghijklmnopqrstuvwxyz_0123456789"
  * Return 0 on success, -1 on failure.
  */
 int 
-PyObjCClass_SetClass(Class objc_class, PyObject* py_class)
+PyObjCClass_FinishClass(Class objc_class)
 {
-	if (objc_class == nil) {
-		PyErr_SetString(PyObjCExc_InternalError, 
-			"Trying to set class of <nil>\n");
-		return -1;
-	}
-	if (py_class == NULL || !PyObjCClass_Check(py_class)) {
-		PyErr_Format(PyObjCExc_InternalError,
-			"Trying to set class to of %s to invalid value "
-			"(type %s instead of %s)",
-			objc_class->name, py_class->ob_type->tp_name,
-			PyObjCClass_Type.tp_name);
-		return -1;
-	}
-
-	CHECK_MAGIC(objc_class);
-
-	if (CLASS_WRAPPER(objc_class)->python_class != NULL) {
-		PyErr_Format(PyObjCExc_InternalError,
-			"Trying to set update PythonClass of %s",
-			objc_class->name);
-		return -1;
-	}
-
-
-	CLASS_WRAPPER(objc_class)->python_class = py_class;
-	Py_INCREF(py_class);
+	PyObjC_Assert(objc_class != nil, -1);
 
 	objc_addClass(objc_class);
 	return 0;
  * Due to technical restrictions it is not allowed to unbuild a class that
  * is already registered with the Objective-C runtime.
  */
-void 
+int 
 PyObjCClass_UnbuildClass(Class objc_class)
 {
-	struct class_wrapper* wrapper = CLASS_WRAPPER(objc_class); 
+	struct class_wrapper* wrapper = (struct class_wrapper*)objc_class; 
 
-	if (objc_class == nil) {
-		PyErr_SetString(PyObjCExc_InternalError, 
-		"Trying to unregister class <nil>");
-		return;
-	}
-
-	CHECK_MAGIC(objc_class);
-
-	if (wrapper->python_class != NULL) {
-		PyErr_Format(PyObjCExc_InternalError,
-			"Trying to unregister objective-C class %s, but it "
-			"is already registered with the runtime",
-			objc_class->name);
-		return;
-	}
-
+	PyObjC_Assert(objc_class != nil, -1);
+	PyObjC_Assert(objc_lookUpClass(objc_class->name) == nil, -1);
 
 	PyObjCRT_ClearClass(&(wrapper->class));
 	PyObjCRT_ClearClass(&(wrapper->meta_class));
 	free(objc_class);
+	return 0;
 }
 
 /*
- * Be smart about slots: Push them into Objective-C and leave an empty
- * __slots__ attribute, that way we don't store object-state in the python
- * proxy.
+ * The Python proxy for an object should not contain any state, even if 
+ * the class is defined in Python. Therefore transfer all slots to the 
+ * Objective-C class and add '__slots__ = ()' to the Python class.
  */
 static int 
 do_slots(PyObject* super_class, PyObject* clsdict)
 	return 0;
 }
 
+/*
+ * Built a (pure Objective-C) subclass of base_class that defines our version
+ * of 'dealloc' and 'copyWithZone:'. The latter is only defined when the 
+ * base_class also defines it. This makes it possible to override both methods
+ * from Python.
+ */
 static Class
 build_intermediate_class(Class base_class, char* name)
 {
 	struct objc_method_list* method_list = NULL;
 	PyObjCRT_Method_t meth;
 	IMP closure;
-	PyObjCMethodSignature* methinfo;
+	PyObjCMethodSignature* methinfo = NULL;
 
-	method_list = PyObjCRT_AllocMethodList(1);
+	method_list = PyObjCRT_AllocMethodList(2);
 
 	if (method_list == NULL) {
 		PyErr_NoMemory();
 	}
 
 	method_list->method_count = 0;
-	methinfo = PyObjCMethodSignature_FromSignature(copyWithZone_signature);
+	if ([base_class instancesRespondToSelector:@selector(copyWithZone:)]) {
+		methinfo = PyObjCMethodSignature_FromSignature(
+				copyWithZone_signature);
+		if (methinfo == NULL) goto error_cleanup; 
+		closure = PyObjCFFI_MakeClosure(methinfo, 
+				object_method_copyWithZone_, base_class);
+		PyObjCMethodSignature_Free(methinfo); methinfo = NULL;
+		if (closure == NULL) goto error_cleanup;
+		meth = method_list->method_list + method_list->method_count++;
+		PyObjCRT_InitMethod(meth, @selector(copyWithZone:), 
+			copyWithZone_signature, (IMP)closure); 
+	}
+
+	methinfo = PyObjCMethodSignature_FromSignature("v@:");
 	if (methinfo == NULL) goto error_cleanup; 
-	closure = PyObjCFFI_MakeClosure(methinfo, object_method_copyWithZone_,
+	closure = PyObjCFFI_MakeClosure(methinfo, object_method_dealloc,
 		base_class);
 	PyObjCMethodSignature_Free(methinfo); methinfo = NULL;
 	if (closure == NULL) goto error_cleanup;
 	meth = method_list->method_list + method_list->method_count++;
-
-	PyObjCRT_InitMethod(meth, @selector(copyWithZone:), 
-			copyWithZone_signature, (IMP)closure); \
+	PyObjCRT_InitMethod(meth, @selector(dealloc), "v@:", (IMP)closure); 
 
 	root_class = base_class;
 	while (root_class->super_class != NULL) {
 	struct class_wrapper*    new_class = NULL;
 	Class                    root_class;
 	Class                    cur_class;
-	char**                   curname;
 	PyObject*                py_superclass = NULL;
 	int                      item_size;
 	int			 have_intermediate = 0;
+	int			 need_intermediate = 0;
 
 	if (!PyList_Check(protocols)) {
 		PyErr_Format(PyObjCExc_InternalError,  
 		 * python and are in the same module.
 		 * This allows using reload() without hiding erroneous
 		 * redefinition (e.g. someone forgetting that classnames
-		 * must be globally unique.
+		 * must be globally unique).
 		 */
 
 		PyObject* tmp = PyObjCClass_New(cur_class);
 	 *
 	 * FIXME: Better code to look for copyWithZone: in the class dict
 	 */
+
+	need_intermediate = 0;
+
 	if (PyDict_GetItemString(class_dict, "copyWithZone_") == NULL) {
 		PyErr_Clear();
-		i = 0;
 	} else {
-		i = 1;
+		if ([super_class instancesRespondToSelector:@selector(copyWithZone:)]) {
+			need_intermediate = 1;
+		}
 	}
 
-	if (i && !PyObjCClass_HasPythonImplementation(py_superclass) 
-          && [super_class instancesRespondToSelector:@selector(copyWithZone:)]){
+	if (PyDict_GetItemString(class_dict, "dealloc") == NULL) {
+		PyErr_Clear();
+	} else {
+		need_intermediate = 1;
+	}
+
+	if (!PyObjCClass_HasPythonImplementation(py_superclass) && need_intermediate) {
 		Class intermediate_class;
 		char  buf[1024];
 
 	}
 
 
-	/* 
-	 * Check for methods/variables that must not be overridden in python.
-	 */
-	for (curname = dont_override_methods; *curname != NULL; curname++) {
-		key = PyDict_GetItemString(class_dict, *curname);
-		if (key != NULL) {
-			PyErr_Format(PyObjCExc_Error,
-				"Cannot override method '%s' from python", 
-				*curname);
-			goto error_cleanup;
-		}
-	}
-
 	protocol_count = PyList_Size(protocols);
 	if (protocol_count > 0) {
 		int cur_protocol = 0;
 
 		ivar_count        += 0;
 		meta_method_count += 0; 
-		method_count      += 9;
+		method_count      += 10;
 	}
 
 	/* Allocate the class as soon as possible, for new selector objects */
 			PyDict_SetItemString(class_dict, pyname, sel);	\
 			Py_DECREF(sel)
 
-		METH(
-			"dealloc", 
-			@selector(dealloc), 
-			"v@:", 
-			object_method_dealloc);
+		if (!have_intermediate) {
+			METH(
+				"dealloc", 
+				@selector(dealloc), 
+				"v@:", 
+				object_method_dealloc);
+		}
+		/* FIXME: 
+		 * all these should be in the intermediate class as well,
+		 * define the intermediate class when any of them are 
+		 * overridden
+		 */
 		METH(
 			"respondsToSelector_", 
 			@selector(respondsToSelector:), 
 		root_class = root_class->super_class;
 	}
 
-	new_class->magic = MAGIC;
-	new_class->python_class = NULL;
-
 	i = PyObjCRT_SetupClass(
 		&new_class->class, 
 		&new_class->meta_class, 
 					[*(id*)(((char*)self) + var->ivar_offset) release];
 
 				NS_HANDLER
-					// FIXME: I don't like this but we must
-					// do something...
 					NSLog(@"ignoring exception %@ in destructor",
 						localException);
 
 
 		PyErr_Fetch(&ptype, &pvalue, &ptraceback);
 
-		CHECK_MAGIC(GETISA(self));
 		cls = PyObjCClass_New(GETISA(self));
-		if (!PyObjCClass_HasPythonImplementation(cls)) {
-			printf("-dealloc substitute called for pure ObjC "
-			       "class\n");
-			abort();
-		}
 
 		delmethod = PyObjCClass_GetDelMethod(cls);
 		if (delmethod != NULL) {
 	objc_msgSendSuper(&super, _meth);
 }
 
+/* -copyWithZone:(NSZone*)zone */
+static void
+object_method_copyWithZone_(
+		ffi_cif* cif __attribute__((__unused__)),
+		void* resp,
+		void** args,
+		void* userdata)
+{
+	id self = *(id*)args[0];
+	id copy;
+	SEL _meth = *(SEL*)args[1];
+	NSZone* zone = *(NSZone**)args[2];
+	Class cls;
+
+	struct objc_super super;
+	PyGILState_STATE state;
+
+	/* Ask super to create a copy */
+
+	super.class = (Class)userdata;
+	RECEIVER(super) = self;
+	copy = objc_msgSendSuper(&super, _meth, zone);
+
+	if (copy == nil) {
+		*(id*)resp = nil;
+		return;
+	}
+
+	state = PyGILState_Ensure();
+
+	cls = self->isa;
+	while (cls != (Class)userdata) {
+		struct objc_ivar_list* ivars = cls->ivars;
+		if (ivars != NULL) {
+			int i;
+			struct objc_ivar* v;
+			PyObject** p;
+
+			for (i = 0; i < ivars->ivar_count; i++) {
+				v = ivars->ivar_list + i;
+				if (strcmp(v->ivar_type, @encode(PyObject*))!=0)
+					continue;
+
+				/* A PyObject, increase it's refcount */
+				p = (PyObject**)(((char*)copy)+v->ivar_offset);
+				if (*p == NULL) continue;
+
+				if (strcmp(v->ivar_name, "__dict__") == 0) {
+					/* copy __dict__ */
+					*p = PyDict_Copy(*p);
+					if (*p == NULL) {
+						[copy release];
+						PyObjCErr_ToObjCWithGILState(
+								&state);
+						return;
+					}
+				} else {
+					Py_INCREF(*p);
+				}
+			}
+			
+		}
+
+		cls = cls->super_class;
+	}
+
+	PyGILState_Release(state);
+	*(id*)resp = copy;
+}
+
 /* -respondsToSelector: */
 static void 
 object_method_respondsToSelector(
 		}
 	NS_ENDHANDLER
 }
-
-static void
-object_method_copyWithZone_(
-		ffi_cif* cif __attribute__((__unused__)),
-		void* resp,
-		void** args,
-		void* userdata)
-{
-	id self = *(id*)args[0];
-	id copy;
-	SEL _meth = *(SEL*)args[1];
-	NSZone* zone = *(NSZone**)args[2];
-	Class cls;
-
-	struct objc_super super;
-	PyGILState_STATE state;
-
-	/* Ask super to create a copy */
-
-	super.class = (Class)userdata;
-	RECEIVER(super) = self;
-	copy = objc_msgSendSuper(&super, _meth, zone);
-
-	if (copy == nil) {
-		*(id*)resp = nil;
-		return;
-	}
-
-	state = PyGILState_Ensure();
-
-	cls = self->isa;
-	while (cls != (Class)userdata) {
-		struct objc_ivar_list* ivars = cls->ivars;
-		if (ivars != NULL) {
-			int i;
-			struct objc_ivar* v;
-			PyObject** p;
-
-			for (i = 0; i < ivars->ivar_count; i++) {
-				v = ivars->ivar_list + i;
-				if (strcmp(v->ivar_type, @encode(PyObject*))!=0)
-					continue;
-
-				/* A PyObject, increase it's refcount */
-				p = (PyObject**)(((char*)copy)+v->ivar_offset);
-				if (*p == NULL) continue;
-
-				if (strcmp(v->ivar_name, "__dict__") == 0) {
-					/* copy __dict__ */
-					*p = PyDict_Copy(*p);
-					if (*p == NULL) {
-						[copy release];
-						PyObjCErr_ToObjCWithGILState(
-								&state);
-						return;
-					}
-				} else {
-					Py_INCREF(*p);
-				}
-			}
-			
-		}
-
-		cls = cls->super_class;
-	}
-
-	PyGILState_Release(state);
-	*(id*)resp = copy;
-}

Modules/objc/formal-protocol.m

 descriptionForInstanceMethod_(PyObject* object, PyObject* sel)
 {
 	PyObjCFormalProtocol* self = (PyObjCFormalProtocol*)object;	
-	SEL aSelector;
+	SEL aSelector = NULL;
 	struct objc_method_description* descr;
 
 	if (PyObjCSelector_Check(sel)) {
 		}
 
 		aSelector = PyObjCRT_SELUID(s);
+	} else {
+		PyErr_Format(PyExc_TypeError, "expecting a SEL, got instance of %s",
+				sel->ob_type->tp_name);
+		return NULL;
 	}
 
 	descr = [self->objc descriptionForInstanceMethod:aSelector];
 descriptionForClassMethod_(PyObject* object, PyObject* sel)
 {
 	PyObjCFormalProtocol* self = (PyObjCFormalProtocol*)object;	
-	SEL aSelector;
+	SEL aSelector = NULL;
 	struct objc_method_description* descr;
 
 	if (PyObjCSelector_Check(sel)) {

Modules/objc/instance-var.h

 #define PyObjCInstanceVariable_IsOutlet(obj) \
 	(((PyObjCInstanceVariable*)(obj))->isOutlet)
 
+PyObject* PyObjCIvar_Info(PyObject* self, PyObject* arg);
+PyObject* PyObjCIvar_Set(PyObject* self, PyObject* args, PyObject* kwds);
+PyObject* PyObjCIvar_Get(PyObject* self, PyObject* args, PyObject* kwds);
+
+
+
 #endif /* OBJC_INSTANCE_VAR */

Modules/objc/ivar-accessor.m

+/*
+ */
+#include "pyobjc.h"
+
+static Ivar
+find_ivar(NSObject* base, char* name)
+{
+	Class cur = GETISA((id)base);
+	Ivar ivar;
+
+	while (cur != nil) {
+		ivar = class_getInstanceVariable(cur, name);
+		if (ivar != nil) {
+			return ivar;
+		}
+		cur = cur->super_class;
+	}
+	return nil;
+}
+
+PyObject*
+PyObjCIvar_Info(PyObject* self __attribute__((__unused__)), PyObject* object)
+{
+	Class cur;
+
+	if (PyObjCObject_Check(object)) {
+		cur = GETISA((id)PyObjCObject_GetObject(object));
+	} else if (PyObjCClass_Check(object)) {
+		cur = PyObjCClass_GetClass(object);
+	} else {
+		PyErr_Format(PyExc_TypeError, "not a class or object");
+		return NULL;
+	}
+
+	PyObject* result;
+
+	result = PyList_New(0);
+	if (result == NULL) {
+		return result;
+	}
+
+	while (cur != nil) {
+		int i, len;
+		Ivar ivar;
+		PyObject* v;
+		int r;
+
+		if (cur->ivars != NULL) {
+			len = cur->ivars->ivar_count;
+			for (i = 0; i < len; i++) {
+				ivar = cur->ivars->ivar_list + i;
+
+				v = Py_BuildValue("(ss)", 
+						ivar->ivar_name, 
+						ivar->ivar_type);
+				if (v == NULL) {
+					Py_DECREF(result);
+					return NULL;
+				}
+				r = PyList_Append(result, v);
+				Py_DECREF(v);
+				if (r == -1) {
+					Py_DECREF(result);
+					return NULL;
+				}
+			}
+		}
+
+		cur = cur->super_class;
+	}
+	return result;
+}
+
+PyObject*
+PyObjCIvar_Get(PyObject* self __attribute__((__unused__)), 
+		PyObject* args, PyObject* kwds)
+{
+static char* keywords[] = {"obj", "name", NULL };
+	PyObject* anObject;
+	char*     name;
+	Ivar	  ivar;
+	NSObject* objcValue;
+	PyObject* result;
+
+	if (!PyArg_ParseTupleAndKeywords(args, kwds, "Os", keywords, &anObject, &name)) {
+		return NULL;
+	}
+
+	if (!PyObjCObject_Check(anObject)) {
+		PyErr_Format(PyExc_TypeError,
+			"Expecting an Objective-C object, got instance of %s",
+			anObject->ob_type->tp_name);
+		return NULL;
+	}
+
+	objcValue = PyObjCObject_GetObject(anObject);
+
+	ivar = find_ivar(objcValue, name);
+	if (ivar == NULL) {
+		PyErr_Format(PyExc_AttributeError, "%s", name);
+		return NULL;
+	}
+
+	if (strcmp(ivar->ivar_type, @encode(PyObject*)) == 0) {
+		result = *(PyObject**)(((char*)(objcValue)) + ivar->ivar_offset);
+		Py_XINCREF(result);
+	} else {
+		result = pythonify_c_value(ivar->ivar_type, 
+			((char*)(objcValue)) + ivar->ivar_offset);
+	}
+
+	return result;
+}
+
+PyObject*
+PyObjCIvar_Set(PyObject* self __attribute__((__unused__)), 
+		PyObject* args, PyObject* kwds)
+{
+static char* keywords[] = {"obj", "name", "value", "updateRefCounts", NULL };
+	PyObject* anObject;
+	char*     name;
+	Ivar	  ivar;
+	PyObject* value;
+	PyObject* updateRefCounts = NULL;
+	NSObject* objcValue;
+	int       result;
+
+	if (!PyArg_ParseTupleAndKeywords(args, kwds, "OsO|O", keywords, &anObject, 
+				&name, &value, &updateRefCounts)) {
+		return NULL;
+	}
+
+	if (!PyObjCObject_Check(anObject)) {
+		PyErr_Format(PyExc_TypeError,
+			"Expecting an Objective-C object, got instance of %s",
+			anObject->ob_type->tp_name);
+		return NULL;
+	}
+
+	objcValue = PyObjCObject_GetObject(anObject);
+
+	ivar = find_ivar(objcValue, name);
+	if (ivar == NULL) {
+		PyErr_Format(PyExc_AttributeError, "%s", name);
+		return NULL;
+	}
+
+	if (strcmp(ivar->ivar_type, @encode(PyObject*)) == 0) {
+		Py_XINCREF(value);
+		Py_XDECREF(*(PyObject**)(((char*)(objcValue)) + ivar->ivar_offset)); 
+		*(PyObject**)(((char*)(objcValue)) + ivar->ivar_offset) = value; 
+
+	} else if (ivar->ivar_type[0] == _C_ID) {
+		NSObject* tmpValue;
+
+		if (updateRefCounts == NULL) {
+			PyErr_SetString(PyExc_TypeError,
+				"Instance variable is an object, "
+				"updateRefCounts argument is required");
+			return NULL;
+		}
+
+		result = depythonify_c_value(ivar->ivar_type, value, &tmpValue);
+		if (result != 0) {
+			return NULL;
+		}
+			
+		if (PyObject_IsTrue(updateRefCounts)) {
+			[tmpValue retain];
+			[*(NSObject**)(((char*)(objcValue)) + ivar->ivar_offset) autorelease];
+		}
+		*(NSObject**)(((char*)(objcValue)) + ivar->ivar_offset) = tmpValue;
+	} else if (ivar->ivar_type[0] == _C_CLASS && strcmp(name, "isa") == 0) {
+		/* We're changing the class of the ObjC value, also update the
+		 * class of the python proxy.
+		 */
+		PyObject* cls;
+
+		result = depythonify_c_value(ivar->ivar_type, value,
+			((char*)(objcValue)) + ivar->ivar_offset);
+		if (result != 0) {
+			return NULL;
+		}
+
+		cls = PyObjCClass_New(GETISA((id)objcValue));
+		if (cls == NULL) {
+			return NULL;
+		}
+
+		Py_INCREF(cls);
+		Py_DECREF((PyObject*)(anObject->ob_type));
+		anObject->ob_type = (PyTypeObject*)cls;
+
+	} else {
+		result = depythonify_c_value(ivar->ivar_type, value,
+			((char*)(objcValue)) + ivar->ivar_offset);
+		if (result != 0) {
+			return NULL;
+		}
+	}
+	Py_INCREF(Py_None);
+	return Py_None;
+}

Modules/objc/method-accessor.m

  *	anObject.pyobjc_classMethods.description()
  * and
  *	anObject.pyobjc_instanceMethods.description()
- *
- * NOTES:
- *	Does not support reflection. That can be added when
- *	needed.
  */
 #include "pyobjc.h"
 

Modules/objc/module.m

 		PyErr_SetString(PyObjCExc_NoSuchClassError, class_name);
 		return NULL;
 	}
-
 	return PyObjCClass_New(objc_class);
 }
 
 	return protocols;
 }
 
+PyDoc_STRVAR(PyObjCIvar_Info_doc, 
+	"listInstanceVariables(classOrInstance) -> [ (name, typestr), ... ]\n"
+	"\n"
+	"Return information about all instance variables of an object or class\n"
+);
+PyDoc_STRVAR(PyObjCIvar_Get_doc, 
+	"getInstanceVariable(object, name) -> value\n"
+	"\n"
+	"Return the value of an instance variable\n"
+);
+PyDoc_STRVAR(PyObjCIvar_Set_doc, 
+	"setInstanceVariable(object, name, value [, updateRefCount])\n"
+	"\n"
+	"Modify an instance variable. If the instance variable is an object \n"
+	"reference you must include the ``updateRefCount`` argument, otherwise it \n"
+	"is ignored. If ``updateRefCount`` is true the reference counts of the \n"
+	"old and new values are updated, otherwise they are not.\n"
+	"\n"
+	"NOTE: updating instance variables is dangerous, instance variables are \n"
+	"private in Objective-C and classes might not expected that those values \n"
+	"are changed by other code."
+);
 
 static PyMethodDef mod_methods[] = {
 	{
 	{ "inject", (PyCFunction)pyject_inject, METH_VARARGS|METH_KEYWORDS, inject_doc },
 #endif /* MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_X_VERSION_10_3 */
 #endif /* MACOSX */
+	{ "listInstanceVariables", (PyCFunction)PyObjCIvar_Info, 
+		METH_O, PyObjCIvar_Info_doc },
+	{ "getInstanceVariable", (PyCFunction)PyObjCIvar_Get,
+		METH_VARARGS|METH_KEYWORDS, PyObjCIvar_Get_doc },
+	{ "setInstanceVariable", (PyCFunction)PyObjCIvar_Set,
+		METH_VARARGS|METH_KEYWORDS, PyObjCIvar_Get_doc },
 
 	{ 0, 0, 0, 0 } /* sentinel */
 };

Modules/objc/objc-class.m

 	super_class = objc_class->super_class;
 	py_super_class = PyObjCClass_New(super_class);
 	if (py_super_class == NULL) {
-		PyObjCClass_UnbuildClass(objc_class);
+		(void)PyObjCClass_UnbuildClass(objc_class);
 		Py_DECREF(protocols);
 		Py_DECREF(real_bases);
 		return NULL;
 
 	v = PyList_AsTuple(real_bases);
 	if (v == NULL) {
-		PyObjCClass_UnbuildClass(objc_class);
+		(void)PyObjCClass_UnbuildClass(objc_class);
 		Py_DECREF(protocols);
 		Py_DECREF(real_bases);
 		return NULL;
 					p, name, py_super_class, dict)) {
 				Py_DECREF(real_bases);
 				Py_DECREF(protocols);
-				PyObjCClass_UnbuildClass(objc_class);
+				(void)PyObjCClass_UnbuildClass(objc_class);
 				return NULL;
 			}
 		} else if (PyObjCFormalProtocol_Check(p)) {
 					p, name, py_super_class, dict)) {
 				Py_DECREF(real_bases);
 				Py_DECREF(protocols);
-				PyObjCClass_UnbuildClass(objc_class);
+				(void)PyObjCClass_UnbuildClass(objc_class);
 				return NULL;
 			}
 		}
 	if (v == NULL) {
 		Py_DECREF(real_bases);
 		Py_DECREF(protocols);
-		PyObjCClass_UnbuildClass(objc_class);
+		(void)PyObjCClass_UnbuildClass(objc_class);
 		return NULL;
 	}
 
 	} else {
 		Py_INCREF(delmethod);
 		if (PyDict_DelItemString(dict, "__del__") < 0) {
-			PyObjCClass_UnbuildClass(objc_class);
+			(void)PyObjCClass_UnbuildClass(objc_class);
 			Py_DECREF(protocols);
 			Py_DECREF(real_bases);
 			return NULL;
 	 * are treated specially there.
 	 */
 	if (add_convenience_methods(objc_class, dict) < 0) {
-		PyObjCClass_UnbuildClass(objc_class);
+		(void)PyObjCClass_UnbuildClass(objc_class);
 		Py_DECREF(protocols);
 		Py_DECREF(real_bases);
 		return NULL;
 	if (res == NULL) {
 		Py_DECREF(args);
 		Py_DECREF(real_bases);
-		PyObjCClass_UnbuildClass(objc_class);
+		(void)PyObjCClass_UnbuildClass(objc_class);
 		return NULL;
 	}
 	Py_DECREF(args);
 
 	if (objc_class_register(objc_class, res) < 0) {
 		Py_DECREF(res);
-		PyObjCClass_UnbuildClass(objc_class);
+		(void)PyObjCClass_UnbuildClass(objc_class);
 		return NULL;
 	}
 
 	info->delmethod = delmethod;
 	info->hasPythonImpl = 1;
 
-	PyObjCClass_SetClass(objc_class, res);
+	PyObjCClass_FinishClass(objc_class);
 
 	var = class_getInstanceVariable(objc_class, "__dict__");
 	if (var != NULL) {

Modules/objc/objc-object.m

 
 PyDoc_STRVAR(obj_get_instanceMethods_doc,
 "The attributes of this field are the instance methods of this object. This\n"
-"can be used to force access to a class method."
+"can be used to force access to an instance method."
 );
 static PyObject*
 obj_get_instanceMethods(PyObjCObject* self, void* closure __attribute__((__unused__)))

Modules/objc/objc_support.m

 	switch (*type) {
 	/* The following are one character type codes */
 	case _C_UNDEF:
-	case _C_ID:
 	case _C_CLASS:
 	case _C_SEL:
 	case _C_CHR:
 		++type;
 		break;
 
+	case _C_ID:
+		++type;
+#ifdef MACOSX
+		if (*type == '"') {
+			/* embedded field name in an ivar_type */
+			type=strchr(type+1, '"');
+			if (type != NULL) {
+				type++;
+			}
+		}
+#endif
+		break;
+
 	case _C_ARY_B:
 		/* skip digits, typespec and closing ']' */
 
 	case _C_STRUCT_B:
 		/* skip name, and elements until closing '}'  */
 		while (*type != _C_STRUCT_E && *type++ != '='); 
-		while (type && *type != _C_STRUCT_E)
+		while (type && *type != _C_STRUCT_E) {
+			if (*type == '"') {
+				/* embedded field names */
+				type = strchr(type+1, '"');
+				if (type != NULL) {
+					type++;
+				} else {
+					return NULL;
+				}
+			}
 			type = PyObjCRT_SkipTypeSpec (type);
+		}
 		if (type) type++;
 		break;
 
 			int align = 0;
 
 			while (type != NULL && *type != _C_STRUCT_E) {
+				if (*type == '"') {
+					type = strchr(type+1, '"');
+					if (type) type++;
+				}
 				if (have_align) {
 					align = MAX(align, 
 					   PyObjC_EmbeddedAlignOfType(type));
 		while (*type != _C_STRUCT_E && *type++ != '=')
 			; /* skip "<name>=" */
 		while (*type != _C_STRUCT_E) {
+			if (*type == '"') {
+				type = strchr(type+1, '"');
+				if (type) type++;
+			}
 			if (have_align) {
 				align = PyObjC_EmbeddedAlignOfType(type);
 				if (align == -1) return -1;
 	if (ret == NULL) {
 		int nitems;
 
-		for (item=type, nitems=0; 
-				*item != _C_STRUCT_E; 
-				item = PyObjCRT_SkipTypeSpec (item)){
-			nitems++;
+		nitems = 0;
+		item = type;
+		while (*item != _C_STRUCT_E) {
+			nitems ++;
+			if (*item == '"') {
+				item = strchr(item+1, '"');
+				if (item) item ++;
+			}
+			item = PyObjCRT_SkipTypeSpec(item);
 		}
 
 		haveTuple = 1;
 		if (!ret) return NULL;
 	}
 
-	for (item=type, offset=itemidx=0; 
-			*item != _C_STRUCT_E; 
-			item = PyObjCRT_SkipTypeSpec (item)) {
+	item = type;
+	offset = itemidx = 0;
+	while (*item != _C_STRUCT_E) {
 		PyObject *pyitem;
 
+		if (*item == '"') {
+			item = strchr(item+1, '"');
+			if (item) item ++;
+		}
+
 		if (!have_align) {
 			align = PyObjCRT_AlignOfType(item);
 			have_align = 1;
 
 		itemidx++;
 		offset += PyObjCRT_SizeOfType (item);
+		item = PyObjCRT_SkipTypeSpec (item);
 	}
 
 	converted = [OC_PythonObject __pythonifyStruct:ret withType:type_real_start length:type_real_length];
 	PyObject* seq;
 
 	while (*types != _C_STRUCT_E && *types++ != '='); /* skip "<name>=" */
-	for (type=types, nitems=0; 
-		*type != _C_STRUCT_E; 
-		type = PyObjCRT_SkipTypeSpec (type)){
 
+	type=types;
+	nitems=0;
+	while (*type != _C_STRUCT_E) {
+		if (*type == '"') {
+			type = strchr(type+1, '"');
+			type++;
+		}
 		nitems++;
+		type = PyObjCRT_SkipTypeSpec (type);
 	}
 
 	seq = PySequence_Fast(arg, "depythonifying struct, got no sequence");
 		return -1;
 	}
 
-	for (type=types, offset=itemidx=0; 
-		*type != _C_STRUCT_E; 
-		type = PyObjCRT_SkipTypeSpec (type)){
+	type=types;
+	offset = itemidx = 0;
 
-		PyObject *argument = PySequence_Fast_GET_ITEM(seq, itemidx);
+	while (*type != _C_STRUCT_E) {
+		PyObject *argument;
+
+		if (*type == '"') {
+			type = strchr(type+1, '"');
+			type++;
+		}
+
+
+		argument = PySequence_Fast_GET_ITEM(seq, itemidx);
 		int error;
 		if (!have_align) {
 			align = PyObjCRT_AlignOfType(type);
   
 		itemidx++;
 		offset += PyObjCRT_SizeOfType (type);
+		type = PyObjCRT_SkipTypeSpec (type);
 	}
 	Py_DECREF(seq);
 	return 0;

Modules/objc/test/instanceVariables.m

+/*
+ * This module is used in the unittests for object identity.
+ */
+#include "Python.h"
+#include "pyobjc-api.h"
+
+#import <Foundation/Foundation.h>
+
+@interface ClassWithVariables : NSObject
+{ 
+	int	intValue;
+	double	floatValue;
+	char	charValue;
+	char*	strValue;
+	NSRect  rectValue;
+	NSObject* nilValue;
+	PyObject* pyValue;
+	NSString* objValue;
+}
+-init;
+-(void)dealloc;
+@end
+
+@implementation ClassWithVariables
+-init
+{
+	self = [super init];
+	if (self == nil) return nil;
+
+	intValue = 42;
+	floatValue = -10.055;
+	charValue = 'a';
+	strValue = "hello world";
+	rectValue = NSMakeRect(1,2,3,4);
+	nilValue = nil;
+	pyValue = PySlice_New(
+			PyInt_FromLong(1), 
+			PyInt_FromLong(10), 
+			PyInt_FromLong(4));
+	objValue = [[NSObject alloc] init];
+	return self;
+}
+
+-(void)dealloc
+{
+	Py_XDECREF(pyValue);
+	[objValue release];
+	[nilValue release];
+}
+
+@end
+
+
+static PyMethodDef ivar_methods[] = {
+	{ 0, 0, 0, 0 }
+};
+
+void initinstanceVariables(void);
+void initinstanceVariables(void)
+{
+	PyObject* m;
+
+	m = Py_InitModule4("instanceVariables", ivar_methods, 
+			NULL, NULL, PYTHON_API_VERSION);
+
+	PyObjC_ImportAPI(m);
+	PyModule_AddObject(m, "ClassWithVariables",
+		PyObjCClass_New([ClassWithVariables class]));
+}
 as they are no longer useful.</li>
 <li>It is now possible to subclass a class that implements <code><span>copyWithZone:</span></code>
 without setting <code><span>__slots__</span></code> to <code><span>()</span></code>.</li>
+<li>It is now possible to override <code><span>dealloc</span></code>. It is still possible to
+define <code><span>__del__</span></code>.</li>
+<li>As an experimental feature it is also possible to override <code><span>retain</span></code> and
+<code><span>release</span></code>. Note it almost never a good idea to do this (even when you're
+programming in Objective-C and much more so in Python).</li>
+<li><code><span>poseAsClass:</span></code> can be used, although it is not very useful in python, use
+categories instead.<p>A major issue with <code><span>poseAsClass:</span></code> is that existing references to the old
+version of the class won't be changed to point to the new class.</p>
+</li>
+<li>It is now possible to access all instance variables of a class using
+the functions <code><span>objc.listInstanceVariables(aClassOrInstance)</span></code>,
+<code><span>objc.getInstanceVariable(obj,</span> <span>name)</span></code> and 
+<code><span>objc.setInstanceVariable(obj,</span> <span>name,</span> <span>value</span> <span>[,</span> <span>updateRefCount])</span></code>.<p>The last argument of <code><span>setInstanceVariable</span></code> is required when the instance
+variable is an object. If it is true the bridge will update reference counts,
+otherwise it won't.</p>
+</li>
 </ul>
 <h2><a name="version-1-2-2004-12-29">Version 1.2 (2004-12-29)</a></h2>
 <ul>
 
 An overview of the relevant changes in new, and older, releases.
 
+
 Version 1.3 (2005-03-??)
 ------------------------
 
 - It is now possible to subclass a class that implements ``copyWithZone:``
   without setting ``__slots__`` to ``()``. 
 
+- It is now possible to override ``dealloc``. It is still possible to
+  define ``__del__``.
+
+- As an experimental feature it is also possible to override ``retain`` and
+  ``release``. Note it almost never a good idea to do this (even when you're
+  programming in Objective-C and much more so in Python).
+
+- ``poseAsClass:`` can be used, although it is not very useful in python, use
+  categories instead.
+
+  A major issue with ``poseAsClass:`` is that existing references to the old
+  version of the class won't be changed to point to the new class. 
+
+- It is now possible to access all instance variables of a class using
+  the functions ``objc.listInstanceVariables(aClassOrInstance)``,
+  ``objc.getInstanceVariable(obj, name)`` and 
+  ``objc.setInstanceVariable(obj, name, value [, updateRefCount])``.
+
+  The last argument of ``setInstanceVariable`` is required when the instance
+  variable is an object. If it is true the bridge will update reference counts,
+  otherwise it won't. 
+
+
 Version 1.2 (2004-12-29)
 ------------------------
 
Tip: Filter by directory path e.g. /media app.js to search for public/media/app.js.
Tip: Use camelCasing e.g. ProjME to search for ProjectModifiedEvent.java.
Tip: Filter by extension type e.g. /repo .js to search for all .js files in the /repo directory.
Tip: Separate your search with spaces e.g. /ssh pom.xml to search for src/ssh/pom.xml.
Tip: Use ↑ and ↓ arrow keys to navigate and return to view the file.
Tip: You can also navigate files with Ctrl+j (next) and Ctrl+k (previous) and view the file with Ctrl+o.
Tip: You can also navigate files with Alt+j (next) and Alt+k (previous) and view the file with Alt+o.