Welcome!
This is the community forum for my apps Pythonista and Editorial.
For individual support questions, you can also send an email. If you have a very short question or just want to say hello — I'm @olemoritz on Twitter.
Access to pyObject() of ObjCInstance is crashy?
-
@JonB, this runs peacefully on iPhone X.
-
@JonB well, that cracks it then, definitely something in the deep woods of the interaction between the objective-c runtime and the python interpreter. I'm going to try grafting the retry technique above to the NavigationView wrapper to see if it will make it stable.
-
@shinyformica, try just using
c_void_p
andon_main_thread
first, without any retries? -
I'll say when I removed on_main_thread, and only wrapped the pyObject call, it originally failed every 10th time. Later, I couldn't get it to reproduce! But c_void_p is at least safer.
-
@mikael, @JonB I wish I had better news, but it seems that no matter whether I attempt ctypes.py_object, ctypes.c_void_p with a cast, multiple retries, on_main_thread, etc. I always eventually crash. So I am going to give up for now...the solution I have now is less efficient, but stable, and works well for my purposes.
It's quite a bit harder to debug than the simple tests demonstrated throughout this thread. I can create tests which crash, and ones which don't by switching around the order of things and where objects are created. In the actual code, there is a ui.NavigationView, which hosts views with ui.TableView children, and those tables have data sources which respond via their action callbacks by eventually calling the code which tries to get the pyObject of the current navigation view and then push or pop a view in response. So where the issue really arises is somewhere in the complex interaction of ui thread and main thread.
Thanks for the ideas and help!
-
Are you getting crashes just getting c_void_p? Or during cast? C_void_p shouldn't give you a seg fault. Then your can check it is valid before using it.
If you have access to the views you are going to push, there may be two other options.
First, set the
tag
parameter on both the objc_instance and ui.View, to unique integers.Then you can query the tag attribute, and look up the tag in python (not sure if the tag lookup methods in python views work with navigation views)
Or, you can actually store your own pyObject, via objc_addAssociatedObject. When the views are first created.
objc_addAssociatedObject(view.objc_instance, sel('pyObject'), ns(id(view)), 3)
Then the view can be retrieved as
py_object.from_address(ObjCInstance(objc_getAssociatedObject(objc_view, sel('pyObject'))).integerValue()).value
I am using this approach to develop a more pythonic way to use create_objc_class -- using a python class to encapsulate the objc class, with method decorators that replace the |_self, _sel ` arguments with a regular self (python object) argument, which is automatically looked up from the objc object.
You can store whatever python data you want using associated objects, just change the key (which should be a selector, of your choosing).
(Some of this is from memory, I'll post a working example later)
-
@JonB I don't really have time to dig further into it. It doesn't matter whether I attempt to get the python ui.View from the objc instance via:
nc = self.navigationview.objc_instance.navigationController() view = nc.topViewController().view() pobj = view.pyObject(argtypes=[], restype=ctypes.c_void_p)
or
nc = self.navigationview.objc_instance.navigationController() view = nc.topViewController().view() pobj = view.pyObject(argtypes=[], restype=ctypes.py_object)
in either case, things become unstable and eventually crashes occur, especially after trying it again. The py_object seems to crash immediately, while the c_void_p way just makes things highly unstable. Oddly, getting the value via c_void_p gives me back the actual ui.View instance, not a c_void_p object which I need to cast(), which is what I was expecting...which to me is a sign of something fishy going on. It never appears to be None, and since it's coming in as an actual ui.View instance and not ctypes.c_void_p, there's no "value" property to check.
I'll look at the objc_addAssociatedObject() idea, though at this point that seems about the same as just keeping a mapping entirely in python of the id of the objc view to the python ui.View object, since I'm already going to the trouble of tracking things manually.
-
I am using this approach to develop a more pythonic way to use create_objc_class -- using a python class to encapsulate the objc class, with method decorators that replace the |_self, _sel ` arguments with a regular self (python object) argument, which is automatically looked up from the objc object.
Yes, please!
-
@mikael
Still a little work in progress, I need to find something useful to do with it.https://gist.github.com/e3ea36bfd652e7ace38987714c01622c
@shinyformica This includes a get_associatedObject, that you could call when initializing a view.
set_associated_object(v.objc_instance, v) ... pyobj=get_associated_object(n.objc_instance.navigationController().viewControllers()[0].view())
-
@JonB regardless of the troubles I was having...that objc decorator idea is brilliant, and ought to be rolled into objc_util.
re: my problems with pyObject being crashy. After going over the way my code was executing, I rearranged things to be absolutely certain that all attempts to access internals via pyObject() and cast() were within a @on_main_thread...and that seemed to finally make it stable. So, though I'm not entirely clear where it was going wrong, somewhere at least one of those calls was being made outside the main thread, and was therefore making the whole thing unstable at any future call.