Swift: Bundle subclass incomplete init/alloc

Hi,

I noticed some unexpected behavior when I tried to subclass from Bundle.

//: Playground - noun: a place where people can play
import Foundation

class Test : Bundle {
    var foo = "Hey"
    override init?(path: String) {
        super.init(path: path)
    }
}

let mainB = Test(path: Bundle.main.bundlePath)
print(mainB?.foo ?? "Fallback")


Will crash when I try to access a property of the subclass (line 12) with:

error: Execution was interrupted, reason: EXC_BAD_ACCESS (code=2, address=0x...).


This crash only occurs if I use the main bundlePath. To me this looks like an error within Foundation/Swift. If it is invalid/forbidden to create a second instance of the main bundle the failable init should fail and return nil. But to receive an object that isn't properly initialized/allocated is just wrong.


Is this a Bug within the Foundation Framework or do I understand something wrong?


Best Regards

Timo

Answered by QuinceyMorris in 280604022

The actual behavior is documented (https://developer.apple.com/documentation/foundation/bundle/1412741-init):


"This method initializes and returns a new instance only if there is no existing bundle associated with fullPath, otherwise it deallocates self and returns the existing object."


It's also documented in the NSBundle.h header file:


/* Because NSBundle caches allocated instances, subclasses should be prepared

to receive an already initialized object back from [super initWithPath:] */

Accepted Answer

The actual behavior is documented (https://developer.apple.com/documentation/foundation/bundle/1412741-init):


"This method initializes and returns a new instance only if there is no existing bundle associated with fullPath, otherwise it deallocates self and returns the existing object."


It's also documented in the NSBundle.h header file:


/* Because NSBundle caches allocated instances, subclasses should be prepared

to receive an already initialized object back from [super initWithPath:] */

Hi

Thank you for the answer. This certainly explains the issue.


Timo

I opened a suggestion to make Bundle final because with this limitation it does not make sense to subclass it anyway. Especially if you consider the fact that NSBundle caches what ever type it gets.


//: Playground - noun: a place where people can play
import Foundation
class Test : Bundle {
    var foo = "Hey"
    override init?(path: String) {
        super.init(path: path)
        print(type(of: self))
    }
}
class Test1 : Bundle {
    var bar = "Hey"

    override init?(path: String) {
        super.init(path: path)
        print(type(of: self))
    }
}
let chess  = Test(path: "/Applications/Chess.app")
let chess1 = Test1(path: "/Applications/Chess.app")
let mainB  = Test(path: Bundle.main.bundlePath)

will print

Test
Test
NSBundle
Swift: Bundle subclass incomplete init/alloc
 
 
Q