Comments (7)
As usual, your observation is correct. :) It's a consequence of how custom Objective-C subclasses are handled. Currently, you need to override the Objective-C init
method instead of the Python __init__
method:
class CantRoller(UIViewController):
@objc_method
def init(self):
self = ObjCInstance(rubicon.objc.send_super(self, 'init'))
# Initialization code goes here.
# Note that self is an ObjCInstance, not a normal Python object.
# You can only call Objective-C methods and set Objective-C properties on self.
# It's not possible to set normal Python attributes on self, at least not reliably.
# The Python attributes might get lost in some cases.
return self
There are a few reasons for this behavior:
- All Objective-C objects are represented by an
ObjCInstance
object on the Python side, no matter what their class is in Objective-C. (ObjCClass
andObjCMetaClass
are special cases, but that doesn't matter here.) That means that an Objective-CCantRoller
object is represented by a PythonObjCInstance
object, not a PythonCantRoller
object. And of course,CantRoller.__init__
is never called on something that isn't aCantRoller
. - The
__new__
and__init__
methods ofObjCInstance
are called every time anObjCInstance
is created for an Objective-C object. (This happens for example when a function/method/property returns an Objective-C object, or when you make anObjCInstance
from a pointer by hand.) This means that__new__
and__init__
are often called more than once for each Objective-C object. This means that even if an Objective-CCantRoller
object was a PythonCantRoller
object, the__init__
method would be called at random times, and not when the object is created.
It might be possible to add a special case for this, to add a default init
method to custom Objective-C subclasses, which automatically calls the __init__
method of the class. I'm not sure how useful that would be though. The only difference is the method name, you still wouldn't be able to set Python attributes on self
correctly.
from rubicon-objc.
I'm going to close this ticket; it's not obvious to me that there's a specific action that needs to be taken. If you think there's a feature lurking in this ticket that is worth addressing, let us know and we can make that more explicit (either by reopening this ticket, or opening a new ticket)
from rubicon-objc.
What about other methods?
Let's say I had:
class Whatever:
def helper(self):
self.foo = self.bar
@objc_method
def exported_(self, somearg): # called from ObjC domain
self.helper()
Is it possible or at present not recommended?
How to refer to Python's self
correctly?
from rubicon-objc.
In an objc_method
, there is no Python self
as you'd expect. The self
you get is actually an ObjCInstance
, so it only has the Objective-C methods. For example:
>>> class Whatever(rubicon.objc.NSObject):
... def py_method(self):
... print("Hello from py_method!", self)
... @rubicon.objc.objc_method
... def objcMethod(self):
... print("Hello from objc_method!", self)
...
>>> o = Whatever.alloc().init()
>>> o.objcMethod()
Hello from objc_method! <Whatever: 0x1019a0af0>
>>> o.py_method()
Traceback (most recent call last):
File "/Library/Frameworks/Python.framework/Versions/3.6/lib/python3.6/site-packages/IPython/core/interactiveshell.py", line 2881, in run_code
exec(code_obj, self.user_global_ns, self.user_ns)
File "<ipython-input-9-52bd697d1711>", line 1, in <module>
o.py_method()
File "/Users/Shared/_Zeug/Python/projects/rubicon-objc/rubicon/objc/objc.py", line 1292, in __getattr__
raise AttributeError('%s.%s %s has no attribute %s' % (type(self).__module__, type(self).__qualname__, self.objc_class.name, name))
AttributeError: rubicon.objc.objc.ObjCInstance Whatever has no attribute py_method
There's no way to call py_method
, because py_method
actually doesn't exist anymore:
>>> Whatever.py_method
Traceback (most recent call last):
File "/Library/Frameworks/Python.framework/Versions/3.6/lib/python3.6/site-packages/IPython/core/interactiveshell.py", line 2881, in run_code
exec(code_obj, self.user_global_ns, self.user_ns)
File "<ipython-input-14-fcb043b9ed85>", line 1, in <module>
Whatever.py_method
File "/Users/Shared/_Zeug/Python/projects/rubicon-objc/rubicon/objc/objc.py", line 1292, in __getattr__
raise AttributeError('%s.%s %s has no attribute %s' % (type(self).__module__, type(self).__qualname__, self.objc_class.name, name))
AttributeError: rubicon.objc.objc.ObjCClass Whatever has no attribute py_method
This happens because when the Whatever
class was created, it was converted from a Python class to an Objective-C class. In the process, all objc_method
s were registered on the Objective-C side. After that, the class only exists in Objective-C, and all Python attributes are gone. Now you can only access things that were registered in Objective-C before (methods, class methods, properties, etc.).
In short, anything you write in a custom Objective-C subclass has to be an objc_method
(or objc_classmethod
, or objc_property
, etc.) or it gets lost. This is certainly not a good solution - ideally, there should either be an error if you have non-ObjC things in your ObjC subclass, or the Python objects need to be stored somehow.
from rubicon-objc.
Okay, or the time being I'll just use a closure.
For the future though, I may need multiple instances of Python-defined Obj-C class.
If I use closure there too, then Obj-C class will be defined multiple times, is that:
- forbidden?
- allowed, but leaks memory?
- ok?
Perhaps a better approach is to define a py
property, of a suitable type that gets automatically represented as whatever Python object it happens to be, i.e. composition? Is there some boiler-plate for this?
from rubicon-objc.
It's not possible to define two Objective-C classes with the same name. If you try, you'll get an error. You can define an Objective-C class inside a function, but that will fail if you call the function more than once.
You can of course add an ivar to you class that holds a pointer to a Python object. But if you do that, you have to prevent the Python object from being garbage-collected. That means you either need to keep a reference to the object elsewhere in Python, or use the Python C API to increase the refcount in init
and decrease it in dealloc
.
from rubicon-objc.
Looks like any practical program needs a mechanism to keep Python reference in Objective-C space.
The inverse is .retain()
, that's straightforward enough, a "reference holder" class is easy to write. There's still an open question of reference cycles, but that's inevitable, right?
The workaround is a module global, but that's kinda... impractical, as ultimately a program would end up with 2 parallel trees, one of Objective-C objects, another of Python objects.
from rubicon-objc.
Related Issues (20)
- Update CI configuration to test release artefacts
- Improve correspondence between ObjectiveC objects and Python wrappers HOT 17
- New caching logic may inadvertently release objects that are still needed HOT 5
- ctypes.ArgumentError message is split into individual characters
- Warning about deprecated `pkg_resources` API
- Incompatibility with `from __future__ import annotations` HOT 1
- Incompatibility with Python 3.12.0b1 HOT 4
- Scroll blocks eventloop for ios HOT 3
- Can't invoke Protocol methods that collide with object properties HOT 2
- "ImportError: No module named 'rubicon'" if trying to bundle script via py2app. HOT 1
- Sphinx 7.2 dropped support for Python 3.8
- Importing rubicon.objs hangs on pypy HOT 1
- Normalize contribution docs with Briefcase/Toga
- Duplicate elements in repr of `ObjCInstance` HOT 7
- ctypes_patch breaks with Python 3.13.0a6 HOT 2
- Event loop stalls in network test
- Change the implementation of ObjCStrInstance HOT 2
- Partial method argument order HOT 5
- Improve the error message of partial methods HOT 3
- `SafeChildWatcher` is deprecated in Python 3.14 HOT 2
Recommend Projects
-
React
A declarative, efficient, and flexible JavaScript library for building user interfaces.
-
Vue.js
🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
-
Typescript
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
-
TensorFlow
An Open Source Machine Learning Framework for Everyone
-
Django
The Web framework for perfectionists with deadlines.
-
Laravel
A PHP framework for web artisans
-
D3
Bring data to life with SVG, Canvas and HTML. 📊📈🎉
-
Recommend Topics
-
javascript
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
-
web
Some thing interesting about web. New door for the world.
-
server
A server is a program made to process requests and deliver data to clients.
-
Machine learning
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
-
Visualization
Some thing interesting about visualization, use data art
-
Game
Some thing interesting about game, make everyone happy.
Recommend Org
-
Facebook
We are working to build community through open source technology. NB: members must have two-factor auth.
-
Microsoft
Open source projects and samples from Microsoft.
-
Google
Google ❤️ Open Source for everyone.
-
Alibaba
Alibaba Open Source for everyone
-
D3
Data-Driven Documents codes.
-
Tencent
China tencent open source team.
from rubicon-objc.