Search code examples
iosswiftjavascriptcore

NSObject not being released after passing to JavaScriptCore


I'm creating a JSContext and evaluating a simple piece of javascript...

function test(obj) {
}

Next, i'm creating an NSObject that contains nothing more then an INIT and DEINIT function.

When I call the javascript Test function and pass in the NSObject, the NSObjects DEINIT method never gets called. I'm not sure why its not being released when nothing should be keeping it alive after the Test function executes.

Swift Playground

import UIKit
import JavaScriptCore

/**************************/
/******* SwiftObject ******/
/**************************/
@objc protocol SwiftObjectExport: JSExport {
}

class SwiftObject: NSObject, SwiftObjectExport {
    override init() {
        super.init()
        print("[INIT] SwiftObject")
    }

    deinit {
        print("[DEINIT] SwiftObject")
    }
}

/**************************/
/******** Main ************/
/**************************/

// DEINIT does NOT fires with this test
func test() {
    let jsContext = JSContext()
    jsContext?.evaluateScript("function test(obj) { } ")

    let obj = SwiftObject()
    jsContext?.objectForKeyedSubscript("test").call(withArguments: [obj])
}

// DEINIT fires with this test
func test2() {
    let jsContext = JSContext()
    jsContext?.evaluateScript("function test(obj) { } ")

    let obj = SwiftObject()
}


print("Running test 1...")
test() // DEINIT does NOT fires with this test

print("Running test 2...")
test2() // DEINIT fires with this test

Solution

  • In real application, all your code is wrapped in an autorelease pool. In playgrounds, that's not the case.

    Simply add an autorelease pool around your test code, and that will fix your issue:

    autoreleasepool {
        print("Running test 1...")
        test()
    
        print("Running test 2...")
        test2()
    
        print("Inside autorelease pool")
    }
    
    print("Outside autorelease pool")
    

    Output:

    Running test 1...
    [INIT] SwiftObject
    Running test 2...
    [INIT] SwiftObject
    [DEINIT] SwiftObject
    Inside autorelease pool
    [DEINIT] SwiftObject
    Outside autorelease pool