Calling performSelector for a @objc_method created by rubicon from Objective C crashes in Python 3.9
Describe the bug
In Python 3.9, calling [obj performSelector:NSSelectorFromString(@"name")] crashes with EXC_BAD_ACCESS (code=2, address=0x1056f7f48) if the obj is an instance of a class created by rubicon with @objc_method def name(self): ....
To Reproduce Steps to reproduce the behavior:
- Create a briefcase iOS project:
$ briefcase new # Give it a name `py39crashdemo` $ cd py39crashdemo $ briefcase create iOS - Add the objc class definition using rubicon in
app.py:$ cat iOS/Xcode/py39crashdemo/py39crashdemo/app/py39crashdemo/app.py """ My first application """ from rubicon import objc class Runner(objc.NSObject): @objc.objc_method def execScript(self): pass def main(): # This should start and launch your app! Interop = objc.ObjCClass("Interop") # Interop is a class defined in objc. Interop.sharedRunner = Runner.alloc().init() - Add the objective code that invokes the
execScriptmethod:$ cat iOS/Xcode/py39crashdemo/py39crashdemo/main.m // // main.m // A main module for starting Python projects under iOS. // #import <Foundation/Foundation.h> #import <UIKit/UIKit.h> #include <Python.h> #include <dlfcn.h> NSObject *_sharedRunner; @interface Interop: NSObject @property(class) NSObject *sharedRunner; @end @implementation Interop + (void)setSharedRunner:(NSObject *)sharedRunner { _sharedRunner = sharedRunner; } + (NSObject *)sharedRunner { return _sharedRunner; } @end @interface PythonAppDelegate : NSObject<UIApplicationDelegate> @end @implementation PythonAppDelegate - (void)applicationDidFinishLaunching:(UIApplication *)application { [Interop.sharedRunner performSelector:NSSelectorFromString(@"execScript")]; // <---- This is the crash } @end int main(int argc, char *argv[]) { // ... below are untouched - Run the iOS application, and it crashes with
EXC_BAD_ACCESS (code=2, address=0x1056f7f48)at[Interop.sharedRunner performSelector:NSSelectorFromString(@"execScript")];
Expected behavior It should not crash.
Environment:
- Operating System: macOS 11.3
- Python version: 3.9.4
- Software versions:
- Briefcase: 0.3.5
- Xcode: 12.5
- Toga: n/a
- ...
Additional context I had an earlier conversion on BeeWare Discoard with @freakboy3742, and I'm instructed to file this bug.
Thank you for the bug report! (For reference, here is a link to the Discord discussion.)
To check if the crash has anything to do with performSelector:, can you try calling the Python-defined method directly and see if that crashes as well? If you do [Interop.sharedRunner execScript] in the Objective-C code, the compiler will give you a warning (because NSObject doesn't declare an execScript method), but it should still compile and run.
If you want to avoid the warning, you can define a protocol on the Objective-C side for the Python-defined Runner class to implement:
@protocol RunnerProtocol
-(id)execScript;
@end
NSObject<RunnerProtocol> *_sharedRunner;
// and all other declarations rewritten to use NSObject<RunnerProtocol>
And on the Python side:
RunnerProtocol = objc.ObjCProtocol("RunnerProtocol")
class Runner(objc.NSObject, protocols=[RunnerProtocol]):
# ...
Then you should be able to call [Interop.sharedRunner execScript] without any compiler warnings.
Thanks. I need to use the protocol approach (otherwise it fails to compile, probably there is a flag to turn it to warning/error?), but calling [Interop.sharedRunner execScript] crashes in the same way.
Doing some housekeeping; I've transferred this issue to the Rubicon-ObjC repository, since it doesn't appear to be specifically related to the Apple support build.