According to Apple's documentation Swift does not necessary require override of initializer. In a following code example Bar inherits initializer of Foo:
class Foo {
let value: Int
init(value: Int = 5) {
self.value = value
}
}
class Bar: Foo {
}
As soon as we add some generic into Foo such as class Foo<T> { Xcode provides us a error Initializer does not override a designated initializer from its superclass. Is there a documentation or swift evolution discussion that explains why it is happening?
Update. It seems that generic is not a major cause for override requirement. Here are an option how to define a class with generic that does not require override of designated initializer:
protocol FooProtocol {
associatedtype T
}
class Foo<U>: FooProtocol {
typealias T = U
let value: Int
init(value: Int, otherValue: T) {
self.value = value
self.otherValue = otherValue
}
}
class Bar: Foo<Int> {
}
However there is another interesting observation of behavior. Defining initializer like following cause override requirement:
init(value: Int = 5) {
self.value = value
}
The funny thing thing that adding one more parameter as following into such designated initializer cause this override requirement to disappear:
init(value: Int = 5, otherValue: T) {
self.value = value
}
Update 2. I can not find a logical explanation to this behavior, at this point I reported it as Compiler bug — https://bugs.swift.org/browse/SR-1375
I actually filled a bug report for inheriting from generic class:
It was back in November last year and didn't get an answer yet, so ¯_(ツ)_/¯
It's clearly a bug. Moreover, although the bug is elicited by subclassing a generic, its proximate cause is the default value. This compiles just fine:
class Foo<T> {
let value: Int
init(value: Int) {
self.value = value
}
}
class Bar: Foo<String> {
}
But this does not:
class Foo<T> {
let value: Int
init(value: Int = 5) {
self.value = value
}
}
class Bar: Foo<String> {
}
That sort of arbitrary distinction without a difference is a sure indication that this is a compiler bug.
Related
I'm working on a method I can use to only update a property with a new value if the new value is different from the current. I have come up with a protocol for this. However, I am getting compiler errors:
Here are a few sample models:
class Model {
var id: Int = 0
}
class Person: Model {
var name: String = ""
}
class Dog: Model {
var color: UIColor = .black
}
Here is the protocol:
protocol Updatable: AnyObject{
#discardableResult
func update<Value: Equatable>(
_ keyPath: ReferenceWritableKeyPath<Self, Value>,
to value: Value
) -> Bool
}
And here is a default implementation:
extension Updatable {
#discardableResult
func update<Value: Equatable>(
_ keyPath: ReferenceWritableKeyPath<Self, Value>,
to value: Value
) -> Bool {
guard self[keyPath: keyPath] != value else { return false }
self[keyPath: keyPath] = value
return true
}
}
Now I would like for all of my Model subclasses to inherit this update feature:
extension Model: Updatable {}
However, I get a compiler error:
Protocol 'Updatable' requirement 'update(_:to:)' cannot be satisfied by a non-final class ('Model') because it uses 'Self' in a non-parameter, non-result type position
Now I understand why this would be a problem due to inheritance. The Self constraint would be different for each Model subclass and overriding would be impossible.
What gets me is that I can work around this issue by defining a blank protocol and adding a default protocol method with the same signature to an extension:
protocol Updatable2: AnyObject { }
extension Updatable2 {
#discardableResult
func update2<Value: Equatable>(
_ keyPath: ReferenceWritableKeyPath<Self, Value>,
to value: Value
) -> Bool {
print("Default: \(keyPath) -> \(value)")
guard self[keyPath: keyPath] != value else { return false }
self[keyPath: keyPath] = value
return true
}
}
extension Model: Updatable2 {}
The compiler gives me no trouble at all. All of the following are valid:
let model = Model()
model.update2(\.id, to: 200)
let person = Person()
person.update2(\.name, to: "Sally")
person.update2(\.id, to: 400)
let car = Car()
car.update2(\.wheels, to: 5)
let models: [Model] = [
model,
person,
car,
]
models.forEach { $0.update2(\.id, to: 666) }
How is this possible?
Now I understand why this would be a problem due to inheritance. The Self constraint would be different for each Model subclass and overriding would be impossible.
Overriding is not the reason. The reason, as explained in this question, is that the inherited method does not implement the protocol anymore.
class ModelSubclass: Model {
/*
inherits
func update<Value: Equatable>(
_ keyPath: ReferenceWritableKeyPath<Model, Value>,
to value: Value
) -> Bool {
but should have
func update<Value: Equatable>(
_ keyPath: ReferenceWritableKeyPath<ModelSubclass, Value>,
to value: Value
) -> Bool {
*/
}
Naturally, removing the requirement works, because this whole thing is caused by the protocol requiring ModelSubclass to have a certain method.
Now, I know what you're going to say: "But I have a default implementation, so every implementation should have all the update methods it needs!" Well, logically I think you are correct, but the Swift compiler doesn't bother checking for things like that. This is the line of code in the compiler where it emits the error message. As you can see, before that, it only checks if the protocol has Self in an invariant position (the final check is done before calling the enclosing method):
if (selfRefInfo.selfRef == TypePosition::Invariant) {
// References to Self in a position where subclasses cannot do
// the right thing. Complain if the adoptee is a non-final
// class.
Side note:
If Self is the parameter type (contravariant) or return type (covariant), then it's fine. Consider:
protocol Foo {
func f(x: Self)
func g() -> Self
}
class Bar: Foo {
func f(x: Bar) { }
// note that the covariant Self return type is required here
// you cannot use Bar here
func g() -> Self {
fatalError()
}
}
class BarSubclass: Bar {
/*
inherits:
func f(x: Bar)
which can take any BarSubclass as parameter, so it can satisfy
func f(x: BarSubclass)
also inherits:
func g() -> Self
which is the same as what it should implement :)
*/
}
I have the script below written in Swift 5 and I am doing something wrong to configure the generics.
Basically it's a Foo protocol with a function with has an argument with a generic type and it's implemented in both Fez and Foz, where the type of the bar function is defined in the class definition. By using associatedtype is possible to fix this?
Any idea of what could be and how to solve it? Am I doing something wrong with the setup of generics? I need to support both Int and String.
protocol Foo {
func bar<T>(zed: T)
}
class Fez: Foo {
private var zed: Int = 0
func bar<Int>(zed: Int) {
self.zed = zed //Cannot assign value of type 'Int' to type 'Swift.Int'
}
}
class Foz: Foo {
private var zed: String = ""
func bar<String>(zed: String) {
self.zed = zed //Cannot assign value of type 'String' to type 'Swift.String'
}
}
Thank you very much.
For the downvoter: I hope you have a nice day.
A generic like this says that the caller can choose any type to pass to bar, and the implementation will handle it. You mean that the implementation gets to decide what type it can be passed, and that's an associatedtype as you suggest.
protocol Foo {
associatedtype Zed
func bar(zed: Zed)
}
class Fez: Foo {
private var zed: Int = 0
func bar(zed: Int) {
self.zed = zed
}
}
class Foz: Foo {
private var zed: String = ""
func bar(zed: String) {
self.zed = zed
}
}
A range of basic classes in the foundation framework can be made by simply assigning a basic number to the value where the desired type is known take for example CGFloat:
let a: CGFloat = 42
instead of having to simply use an init like so:
let a = CGFloat(42)
My question is what is this called when a struct or a class implements this behaviour and how can it be implemented for your own classes.
I don't believe this is matter of CGFloat being a type alias and I cannot seem to find a suitable answer for this.
Your type would need to implement the IntegerLiteralConvertible protocol.
That protocol requires you to implement a constructor of the form:
init(integerLiteral value: Self.IntegerLiteralType) {}
Example:
struct MyCoolStruct {
let value: Int
}
extension MyCoolStruct : IntegerLiteralConvertible {
init(integerLiteral value: Int) {
self.value = value
}
}
let instance: MyCoolStruct = 3
instance.value
Swift 4/5:
IntegerLiteralConvertible has been renamed in ExpressibleByIntegerLiteral:
extension MyStruct: ExpressibleByIntegerLiteral {
init(integerLiteral value: IntegerLiteralType) {
self.value = value
}
}
I'm trying to add functionality to an NSManagedObject via a protocol. I added a default implementation which works fine, but as soon as I try to extend my subclass with the protocol it tells me that parts of it are not implemented, even though I added the default implementation.
Anyone having Ideas of what I'm doing wrong?
class Case: NSManagedObject {
}
protocol ObjectByIdFetchable {
typealias T
typealias I
static var idName: String { get }
static func entityName() -> String
static func objectWithId(ids:[I], context: NSManagedObjectContext) -> [T]
}
extension ObjectByIdFetchable where T: NSManagedObject, I: AnyObject {
static func objectWithId(ids:[I], context: NSManagedObjectContext) -> [T] {
let r = NSFetchRequest(entityName: self.entityName())
r.predicate = NSPredicate(format: "%K IN %#", idName, ids)
return context.typedFetchRequest(r)
}
}
extension Case: ObjectByIdFetchable {
typealias T = Case
typealias I = Int
class var idName: String {
return "id"
}
override class func entityName() -> String {
return "Case"
}
}
The error I get is Type Case doesn't conform to protocol ObjectByIdFetchable
Help very much appreciated.
We'll use a more scaled-down example (below) to shed light on what goes wrong here. The key "error", however, is that Case cannot make use of the default implementation of objectWithId() for ... where T: NSManagedObject, I: AnyObject; since type Int does not conform to the type constraint AnyObject. The latter is used to represent instances of class types, whereas Int is a value type.
AnyObject can represent an instance of any class type.
Any can represent an instance of any type at all, including function types.
From the Language Guide - Type casting.
Subsequently, Case does not have access to any implementation of the blueprinted objectWithId() method, and does hence not conform to protocol ObjectByIdFetchable.
Default extension of Foo to T:s conforming to Any works, since Int conforms to Any:
protocol Foo {
typealias T
static func bar()
static func baz()
}
extension Foo where T: Any {
static func bar() { print ("bar") }
}
class Case : Foo {
typealias T = Int
class func baz() {
print("baz")
}
}
The same is, however, not true for extending Foo to T:s conforming to AnyObject, as Int does not conform to the class-type general AnyObject:
protocol Foo {
typealias T
static func bar()
static func baz()
}
/* This will not be usable by Case below */
extension Foo where T: AnyObject {
static func bar() { print ("bar") }
}
/* Hence, Case does not conform to Foo, as it contains no
implementation for the blueprinted method bar() */
class Case : Foo {
typealias T = Int
class func baz() {
print("baz")
}
}
Edit addition: note that if you change (as you've posted in you own answer)
typealias T = Int
into
typealias T = NSNumber
then naturally Case has access to the default implementation of objectWithId() for ... where T: NSManagedObject, I: AnyObject, as NSNumber is class type, which conforms to AnyObject.
Finally, note from the examples above that the keyword override is not needed for implementing methods blueprinted in a protocol (e.g., entityName() method in your example above). The extension of Case is an protocol extension (conforming to ObjectByIdFetchable by implementing blueprinted types and methods), and not really comparable to subclassing Case by a superclass (in which case you might want to override superclass methods).
I found the solution to the problem. I thought it's the typealias T which is the reason for not compiling. That's actually not true, it's I which I said to AnyObject, the interesting thing is that Int is not AnyObject. I had to change Int to NSNumber
I have the following protocol and a class that conforms to it:
protocol Foo{
typealias BazType
func bar(x:BazType) ->BazType
}
class Thing: Foo {
func bar(x: Int) -> Int {
return x.successor()
}
}
When I try to create an Array of foos, I get an odd error:
var foos: Array<Foo> = [Thing()]
Protocol Foo can only be used as a generic constraint because it has
Self or associated type requirements.
OK, so it can only be used if it has an associated type requirement (which it does), but for some reason this is an error?? WTF?!
I'm not sure I fully understand what the compiler is trying to tell me...
Let's say, if we could put an instance of Thing into array foos, what will happen?
protocol Foo {
associatedtype BazType
func bar(x:BazType) -> BazType
}
class Thing: Foo {
func bar(x: Int) -> Int {
return x.successor()
}
}
class AnotherThing: Foo {
func bar(x: String) -> String {
return x
}
}
var foos: [Foo] = [Thing()]
Because AnotherThing conforms to Foo too, so we can put it into foos also.
foos.append(AnotherThing())
Now we grab a foo from foos randomly.
let foo = foos[Int(arc4random_uniform(UInt32(foos.count - 1)))]
and I'm going to call method bar, can you tell me that I should send a string or an integer to bar?
foo.bar("foo") or foo.bar(1)
Swift can't.
So it can only be used as a generic constraint.
What scenario requires a protocol like this?
Example:
class MyClass<T: Foo> {
let fooThing: T?
init(fooThing: T? = nil) {
self.fooThing = fooThing
}
func myMethod() {
let thing = fooThing as? Thing // ok
thing?.bar(1) // fine
let anotherThing = fooThing as? AnotherThing // no problem
anotherThing?.bar("foo") // you can do it
// but you can't downcast it to types which doesn't conform to Foo
let string = fooThing as? String // this is an error
}
}
I have been playing with your code trying to understand how to implement the protocol. I found that you can't use Typealias as a generic type because it is just an alias not a type by itself. So if you declare the Typealias outside your protocol and your class you can effectively use it in your code without any problem.
Note: the Typealias has the Int type in its declaration, that way you can always use the alias instead of the Int type and use all of its associated methods and functions.
Here's how I make it work:
typealias BazType = Int
protocol Foo{
func bar(x:BazType) -> BazType
}
class Thing: Foo {
func bar(x: BazType) -> BazType {
return x.successor()
}
}
let elements: Array<Foo> = [Thing(), Thing()]