Swift compile error, subclassing NSValue, using super.init(nonretainedObject:)
Asked Answered
S

1

2

This code

class ID<T: AnyObject> : NSValue {
    init(baseObject: T) {
        super.init(nonretainedObject: baseObject)
    }
}

gives this compiler error:

error: must call a designated initializer of the superclass 'NSValue'
    super.init(nonretainedObject: baseObject)
    ^

How do I get rid of this?

Things I thought of

I thought the error might be because the NSValue initializer has an AnyObject? type (Note well: postfix ?). I tried various flavors of casting and [?!] postfixing in places, and it fixed nothing.

Also, presumably NSValue(nonretainedObject:) must call the designated initializer, right?

Schappe answered 27/6, 2014 at 15:48 Comment(0)
B
2

NSValue(nonretainedObject:) isn't a designated initializer. The only initializer listed in the NSValue reference (and hence the designated initializer) is NSValue(value:CConstVoidPointer, withObjCType type:CString)

The other constructors are all convenience constructors derived from class helper methods.

You might try:

init(baseObject: T) {
    super.init(bytes:&baseObject, withObjCType:"^v")
}

"^v" is the type string returned by an NSValue created with valueWithNonRetained....

Unfortunately, I'm not coming up with an appropriate way to pass baseObject as a CConstVoidPointer.

Barring that, the best thing I can come up with is to wrap NSValue instead of subclassing it:

class ID<T:AnyObject> {
    let wrapped:NSValue

    init(baseObject:T) {
        wrapped = NSValue(nonretainedObject:baseObject)
    }
}

Finally got something to work, but it's a little bit ugly, basically add a convenience constructor that wraps the nonretainedObject constructor and then use that in your subclass:

extension NSValue {
    convenience init<T:AnyObject>(unretained:T) {
        self.init(nonretainedObject:unretained)
    }
}


class ID<T>:NSValue {
    convenience init<T:AnyObject>(unretained:T) {
        self.init(unretained:unretained)
    }
}

Depending on what you're actually trying to do the category alone might be sufficient?

Bioastronautics answered 27/6, 2014 at 15:59 Comment(4)
At the moment, I see init(nonretainedObject:) in these Apple docs . The docs seem confused on whether they are referring to +value/-init methods. Regardless, you've pointed the way to a good workaround.Schappe
Yeah, those docs are pretty explicit that init(bytes value: CConstVoidPointer, objCType type: Cstring) is the designated initializer. Now if you can just figure out how to pass baseObject in you're golden :)Bioastronautics
IIUC, it shouldn't matter what the designated initializer is, so long as you chain to it. I'd figure all of NSValues convenience initializers should be doing this, right?Schappe
You have to eventually call the (a) designated initializer from your subclass. There's also some restrictions on flagging initializers that aren't the designated initializer as convenience, but I'd have to look up exactly what the rules are. Basically the compiler will tell you to flag it "convenience" and you do :)Bioastronautics

© 2022 - 2024 — McMap. All rights reserved.