rubicon-objc icon indicating copy to clipboard operation
rubicon-objc copied to clipboard

Calling performSelector for a @objc_method created by rubicon from Objective C crashes in Python 3.9

Open yilei opened this issue 4 years ago • 3 comments

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:

  1. Create a briefcase iOS project:
    $ briefcase new
    # Give it a name `py39crashdemo`
    $ cd py39crashdemo
    $ briefcase create iOS
    
  2. 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()
    
  3. Add the objective code that invokes the execScript method:
     $ 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
    
  4. 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.

yilei avatar Jul 07 '21 04:07 yilei

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.

dgelessus avatar Jul 07 '21 11:07 dgelessus

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.

yilei avatar Jul 07 '21 15:07 yilei

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.

freakboy3742 avatar May 12 '22 23:05 freakboy3742