typhoon

EXC_BAD_ACCESS when injecting a closure at runtime using Typhoon


I have a mixed ObjC and Swift iOS project.

I have a class for testing the injection of a trivial closure at runtime:

@objc
class TyphoonClosureTester: NSObject {
    @objc var closure: (() -> Void)?

    @objc
    override init() {}

    @objc
    init(closure: (() -> Void)?) {
        self.closure = closure
    }

    @objc
    func callClosure() {
        guard let closure = closure else {
            assert(false, "no closure 1")
            return
        }
        closure()
        NSLog("Have called closure 1 OK")
    }
}

My assembly file for Typhoon contains this function:

func testAClosure(closure: @escaping () -> Void) -> AnyObject {
    return TyphoonDefinition.withClass(TyphoonClosureTester.self) { definition in
        definition?.useInitializer(#selector(TyphoonClosureTester.init(closure:))) { initializer in
            initializer?.injectParameter(with: closure)
        }

        definition?.scope = .prototype
    } as AnyObject
}

And I try using this closure as follows:

let closureTester1: TyphoonClosureTester = assembler.testAClosure(closure: {
    NSLog(" closure 1 called!")
}) as! TyphoonClosureTester

// causes EXC_BAD_ACCESS
closureTester1.callClosure()

but this results in EXC_BAD_ACCESS when I call the closure. (Exact message is Thread 1: EXC_BAD_ACCESS (code=1, address=0x0)) So it looks like the closure is being deallocated somewhere.

I've tried adding capture into the assembly (i.e. [closure]) but it makes no difference:

func testAClosure(closure: @escaping () -> Void) -> AnyObject {
    return TyphoonDefinition.withClass(TyphoonClosureTester.self) { [closure] definition in
        definition?.useInitializer(#selector(TyphoonClosureTester.init(closure:))) { [closure] initializer in
            initializer?.injectParameter(with: closure)
        }

        definition?.scope = .prototype
    } as AnyObject
}

I also tried injecting the closure as a property, instead of via init, and it made no difference.


Solution

  • From memory this is not supported. You may try looking in the test cases for an example.

    Injecting wrapped primitives is described here: https://github.com/appsquickly/typhoon/wiki/wrap-primitive-values-into-NSValue

    . . however there's no reference to blocks/closures.

    If you describe what you would like to achieve, a next-best workaround could be suggested.