Create an account

Very important

  • To access the important data of the forums, you must be active in each forum and especially in the leaks and database leaks section, send data and after sending the data and activity, data and important content will be opened and visible for you.
  • You will only see chat messages from people who are at or below your level.
  • More than 500,000 database leaks and millions of account leaks are waiting for you, so access and view with more activity.
  • Many important data are inactive and inaccessible for you, so open them with activity. (This will be done automatically)


Thread Rating:
  • 322 Vote(s) - 3.48 Average
  • 1
  • 2
  • 3
  • 4
  • 5
Class does not implement its superclass's required members

#1
So I updated to Xcode 6 beta 5 today and noticed I received errors in nearly all of my subclasses of Apple's classes.

The error states:

> Class 'x' does not implement its superclass's required members



Here is one example I picked because this class is currently pretty lightweight so it will be easy to post.

class InfoBar: SKSpriteNode { //Error message here

let team: Team
let healthBar: SKSpriteNode

init(team: Team, size: CGSize) {
self.team = team
if self.team == Team.TeamGood {
healthBar = SKSpriteNode(color: UIColor.greenColor(), size:size)
}
else {
healthBar = SKSpriteNode(color: UIColor.redColor(), size:size)
}
super.init(texture:nil, color: UIColor.darkGrayColor(), size: size)

self.addChild(healthBar)

}

}

So my question is, why am I receiving this error, and how can I fix it? What is it that I am not implementing? I'm calling a designated initializer.
Reply

#2
There are two absolutely **crucial** pieces of Swift-specific information that are missing from the existing answers which I think help clear this up completely.

1. If a protocol specifies an initializer as a required method, that initializer must be marked using Swift's `required` keyword.
2. Swift has a special set of inheritance rules regarding `init` methods.

The **tl;dr** is this:

> If you implement any initializers, you are no longer inheriting any of the superclass's designated initializers.

<sub>The only initializers, if any, that you will inherit, are super class convenience initializers that point to a designated initializer which you happened to override.</sub>

So... ready for the long version?


----------

### Swift has a special set of inheritance rules regarding `init` methods.

I know this was the second of two points I made, but we can't understand the first point, or why the `required` keyword even exists until we understand this point. Once we understand this point, the other one becomes pretty obvious.

<sub>All of the information I cover in this section of this answer is from Apple's documentation found [here][1].</sub>

From the Apple docs:

> **Unlike subclasses in Objective-C, Swift subclasses do not inherit their superclass initializers by default.** Swift’s approach prevents a situation in which a simple initializer from a superclass is inherited by a more specialized subclass and is used to create a new instance of the subclass that is not fully or correctly initialized.

<sub>Emphasis mine.</sub>

So, straight from the Apple docs right there, we see that Swift subclasses will not always (and usually don't) inherit their superclass's `init` methods.

**So, when do they inherit from their superclass?**

There are two rules that define when a subclass inherits `init` methods from its parent. From the Apple docs:

> **Rule 1**
>
> If your subclass doesn’t define any designated initializers, it automatically inherits all of its superclass designated initializers.
>
> **Rule 2**
>
> If your subclass provides an implementation of all of its superclass designated initializers—either by inheriting them as per rule 1, or by providing a custom implementation as part of its definition—then it automatically inherits all of the superclass convenience initializers.

Rule 2 isn't particularly relevant to this conversation because `SKSpriteNode`'s `init(coder: NSCoder)` is unlikely to be a convenience method.

So, your `InfoBar` class was inheriting the `required` initializer right up until the point that you added `init(team: Team, size: CGSize)`.

If you were to have not provided this `init` method and instead made your `InfoBar`'s added properties optional or provided them with default values, then you'd have still been inheriting `SKSpriteNode`'s `init(coder: NSCoder)`. However, when we added our own custom initializer, we stopped inheriting our superclass's designated initializers (and [convenience initializers][2] which didn't point to initializers we implemented).

So, as a simplistic example, I present this:

class Foo {
var foo: String
init(foo: String) {
self.foo = foo
}
}

class Bar: Foo {
var bar: String
init(foo: String, bar: String) {
self.bar = bar
super.init(foo: foo)
}
}


let x = Bar(foo: "Foo")

Which presents the following error:

> Missing argument for parameter 'bar' in call.

[![enter image description here][3]][3]

If this were Objective-C, it'd have no problem inheriting. If we initialized a `Bar` with `initWithFoo:` in Objective-C, the `self.bar` property would simply be `nil`. It's probably not great, but it's a perfectly *valid* state for the object to be in. It's **not** a perfectly valid state for the Swift object to be in. `self.bar` is not an optional and cannot be `nil`.

Again, the only way we inherit initializers is by not providing our own. So if we try to inherit by deleting `Bar`'s `init(foo: String, bar: String)`, as such:

class Bar: Foo {
var bar: String
}

Now we're back to inheriting (sort of), but this won't compile... and the error message explains exactly why we don't inherit superclass `init` methods:

> **Issue:** Class 'Bar' has no initializers
>
> **Fix-It:** Stored property 'bar' without initializers prevents synthesized initializers

If we've added stored properties in our subclass, there's no possible Swift way to create a valid instance of our subclass with the superclass initializers which couldn't possibly know about our subclass's stored properties.


----------

### Okay, well, why do I have to implement `init(coder: NSCoder)` at all? Why is it `required`?

Swift's `init` methods may play by a special set of inheritance rules, but protocol conformance is still inherited down the chain. If a parent class conforms to a protocol, its subclasses must to conform to that protocol.

Ordinarily, this isn't a problem, because most protocols only require methods which don't play by special inheritance rules in Swift, so if you're inheriting from a class that conforms to a protocol, you're also inheriting all of the methods or properties that allow the class to satisfy protocol conformance.

However, remember, Swift's `init` methods play by a special set of rules and aren't always inherited. Because of this, a class that conforms to a protocol which requires special `init` methods (such as `NSCoding`) requires that the class mark those `init` methods as `required`.

Consider this example:

protocol InitProtocol {
init(foo: Int)
}

class ConformingClass: InitProtocol {
var foo: Int
init(foo: Int) {
self.foo = foo
}
}

This doesn't compile. It generates the following warning:

> **Issue:** Initializer requirement 'init(foo:)' can only be satisfied by a 'required' initializer in non-final class 'ConformingClass'
>
> **Fix-It:** Insert required

It wants me to make the `init(foo: Int)` initializer required. I could also make it happy by making the class `final` (meaning the class can't be inherited from).

So, what happens if I subclass? From this point, if I subclass, I'm fine. If I add any initializers though, I am suddenly no longer inheriting `init(foo:)`. This is problematic because now I'm no longer conforming to the `InitProtocol`. I can't subclass from a class that conforms to a protocol and then suddenly decide I no longer want to conform to that protocol. I've inherited protocol conformance, but because of the way Swift works with `init` method inheritance, I've not inherited part of what's required to conform to that protocol and I must implement it.


----------

### Okay, this all makes sense. But why can't I get a more helpful error message?

Arguably, the error message might be more clear or better if it specified that your class was no longer conforming to the inherited `NSCoding` protocol and that to fix it you need to implement `init(coder: NSCoder)`. Sure.

But Xcode simply can't generate that message because that actually won't always be the actual problem with not implementing or inheriting a required method. There is at least one other reason to make `init` methods `required` besides protocol conformance, and that's factory methods.

If I want to write a proper factory method, I need to specify the return type to be `Self` (Swift's equivalent of Objective-C's `instanceType`). But in order to do this, I actually need to use a `required` initializer method.

class Box {
var size: CGSize
init(size: CGSize) {
self.size = size
}

class func factory() -> Self {
return self.init(size: CGSizeZero)
}
}

This generates the error:

> Constructing an object of class type 'Self' with a metatype value must use a 'required' initializer

[![enter image description here][4]][4]

It's basically the same problem. If we subclass `Box`, our subclasses will inherit the class method `factory`. So we could call `SubclassedBox.factory()`. However, without the `required` keyword on the `init(size:)` method, `Box`'s subclasses are not guaranteed to inherit the `self.init(size:)` that `factory` is calling.

So we must make that method `required` if we want a factory method like this, and that means if our class implements a method like this, we'll have a `required` initializer method and we'll run into the exact same problems you've run into here with the `NSCoding` protocol.


----------

Ultimately, it all boils down to the basic understanding that Swift's initializers play by a slightly different set of inheritance rules which means you're not guaranteed to inherit initializers from your superclass. This happens because superclass initializers can't know about your new stored properties and they couldn't instantiate your object into a valid state. But, for various reasons, a superclass might mark an initializer as `required`. When it does, we can either employ one of the very specific scenarios by which we actually do inherit the `required` method, or we must implement it ourselves.

The main point here though is that if we're getting the error you see here, it means that your class isn't actually implementing the method at all.

As perhaps one final example to drill in the fact that Swift subclasses don't always inherit their parent's `init` methods (which I think is absolutely central to fully understanding this problem), consider this example:

class Foo {
init(a: Int, b: Int, c: Int) {
// do nothing
}
}

class Bar: Foo {
init(string: String) {
super.init(a: 0, b: 1, c: 2)
// do more nothing
}
}

let f = Foo(a: 0, b: 1, c: 2)
let b = Bar(a: 0, b: 1, c: 2)

This fails to compile.

[![enter image description here][5]][5]

The error message it gives is a little misleading:

> Extra argument 'b' in call

But the point is, `Bar` doesn't inherit any of `Foo`'s `init` methods because it hasn't satisfied either of the two special cases for inheriting `init` methods from its parent class.

If this were Objective-C, we'd inherit that `init` with no problem, because Objective-C is perfectly happy not initializing objects' properties (though as a developer, you shouldn't have been happy with this). In Swift, this simply will not do. You can't have an invalid state, and inheriting superclass initializers can only lead to invalid object states.


[1]:

[To see links please register here]

[2]:

[To see links please register here]

[3]:

[4]:

[5]:
Reply

#3
From an Apple employee on the Developer Forums:

> "A way to declare to the compiler and the built program that you really
> don't want to be NSCoding-compatible is to do something like this:"

required init(coder: NSCoder) {
fatalError("NSCoding not supported")
}
If you know you don't want to be NSCoding compliant, this is an option. I've taken this approach with a lot of my SpriteKit code, as I know I won't be loading it from a storyboard.

----------
Another option you can take which works rather well is to implement the method as a convenience init, like so:

convenience required init(coder: NSCoder) {
self.init(stringParam: "", intParam: 5)
}
Note the call to an initializer in `self`. This allows you to only have to use dummy values for the parameters, as opposed to all non-optional properties, while avoiding throwing a fatal error.


----------
The third option of course is to implement the method while calling super, and initialize all of your non-optional properties. You should take this approach if the object is a view being loaded from a storyboard:

required init(coder aDecoder: NSCoder!) {
foo = "some string"
bar = 9001

super.init(coder: aDecoder)
}
Reply

#4
Why has this issue arisen? Well, the plain fact is that it has _always_ been important (i.e. in Objective-C, since the day I started programming Cocoa back in Mac OS X 10.0) to deal with initializers that your class is not prepared to handle. The docs have always been quite clear about your responsibilities in this regard. But how many of us bothered to fulfill them, completely and to the letter? Probably none of us! And the compiler did not enforce them; it was all purely conventional.

For example, in my Objective-C view controller subclass with this designated initializer:

- (instancetype) initWithCollection: (MPMediaItemCollection*) coll;

...it is crucial that we be passed an actual media item collection: the instance simply cannot come into existence without one. But I have written no "stopper" to prevent someone from initializing me with bare-bones `init` instead. I _should_ have written one (actually, properly speaking, I should have written an implementation of `initWithNibName:bundle:`, the inherited designated initializer); but I was too lazy to bother, because I "knew" I would never incorrectly initialize my own class that way. This left a gaping hole. In Objective-C, someone _can_ call bare-bones `init`, leaving my ivars uninitialized, and we are up the creek without a paddle.

Swift, wonderfully, saves me from myself in most cases. As soon as I translated this app into Swift, the whole problem went away. Swift effectively creates a stopper for me! If `init(collection:MPMediaItemCollection)` is the only designated initializer declared in my class, I can't be initialized by calling bare-bones `init()`. It's a miracle!

What's happened in seed 5 is merely that the compiler has realized that the miracle doesn't work in the case of `init(coder:)`, because in theory an instance of this class could come from a nib, and the compiler can't prevent that — and when the nib loads, `init(coder:)` will be called. So the compiler makes you write the stopper explicitly. And quite right too.
Reply

#5
add

required init(coder aDecoder: NSCoder!) {
super.init(coder: aDecoder)
}
Reply



Forum Jump:


Users browsing this thread:
1 Guest(s)

©0Day  2016 - 2023 | All Rights Reserved.  Made with    for the community. Connected through