I am trying to design a subclass of a SKShapeNode that takes on a specific width and height and only takes a position in it's initializer. Basically I want a class that makes identical boxes but allows me to initialize them with different starting positions.
When I tried to implement this I am getting an error that tells me Must Call a designated initializer of the superclass 'SKShapeNode'
I do not understnad how what I implemented does not called a designated initializer. Am I not allowed to set a general predetermined initializer for the subclass to call in the super?
When I attempt to make this a convenience initializer, like the message suggests, the self tag has no initializer properties to use. Honestly, what I have coded is exactly what I want to happen, I just need someone to help me make it do this thing.
SKShapeNode doesn't define a designated initializer, so it inherits SKNode's, which is init(). Your class does define a designated initializer: init and (unintentionally) init(coder:). By making a non-convenience initializer, you disabled initializer inheritance and made it impossible to use the convenience initializers of your superclass.
Instead, you want to just chain a new convenience init to SKShapeNode's convenience init. To do that, get rid of the "coder" init, and mark your init convenience. Then it can chain to other inherited convenience inits by calling self.init(...):
class DigitObject: SKShapeNode {
convenience init(x: CGFloat, y: CGFloat) {
self.init(rect: CGRect(x: x, y: y, width: CGFloat(9*3), height: CGFloat(9*5)))
}
}
For more on the rules, see Initializer Delegation for Class Types
. For more on why it works this way, see Why can’t we call superclass convenience initializer from subclass? And for a little fun about why Swift cares so much about this, you can watch James Dempsey sing about how less-strict rules burned us in Objective-C.
Related
I am trying to understand the syntax for initializers and how they work. I am reading the swift documentation and I am having a hard time understanding how they work in a specific example I am working on. I'm following a tutorial to learn about core data but I do not want to continue through the project until I understand how the initialization code works (or any other concept I do not understand).
https://docs.swift.org/swift-book/LanguageGuide/Initialization.html
Core Data: Note Entity Class
Core Data: Note Class Convenience Initializer
In the first image above I show the Note Entity Class that Core Data creates and in the second image I add a convenience init(). All the extra on the code in the extension of the Note class is my notes.
The first two comments on the extension of the Note class is what I have found out about how the entity class hierarchy which is that the super class is the NSMangedObject (super class) then the sub class is the Note entity class. To my understanding the NSMangedObject class has 3 initializers which are:
init() - Default Initializer
init(entity: NSEntityDescription, insertInto context: NSManagedObjectContext?) - Designated Initializer
convenience init(context moc: NSManagedObjectContext) - Convenience Initializer
Then Note entity class the only thing I have is the convenience init(title: String, context: NSManagedObjectContext). For this initializer I understand how the title and creationDate are initialized but my question is on the self.init(context: context).
Question: To my understand reading the Swift documentation a convenience initializer cannot point to another convenience initializer which is what I think it's happening here?? I think that the default initializer from the Note class is pointing to the initializers from the super class of NSMangedObject. Can anyone provide me with some insight to understand what is happening.
A convenience initializer must call another initializer in self. That is all it is required to do. It must do that before saying self for any other purpose.
In the Node extension, the convenience initializer is doing that; it is starting out by calling self.init(context:).
That is legal because Node is an NSManagedObject subclass (representing an entity), so it inherits that initializer:
https://developer.apple.com/documentation/coredata/nsmanagedobject/1640602-init
That's all there is to know. The fact that init(context:) is a convenience initializer is not interesting. It's an initializer of self, that's all that matters.
Apple's book "The Swift Programming Language (Swift 3.1)" states the following:
As with function and method parameters, initialization parameters can have both a parameter name for use within the initializer’s body and an argument label for use when calling the initializer.
However, initializers do not have an identifying function name before their parentheses in the way that functions and methods do. Therefore, the names and types of an initializer’s parameters play a particularly important role in identifying which initializer should be called. Because of this, Swift provides an automatic argument label for every parameter in an initializer if you don’t provide one.
I don't understand the last sentence, as I didn't notice any difference between functions/methods and initializers when it comes to parameters names/labels. How is the argument label automatically provided for an initializer?
The feature being described is this: Given a struct:
struct Point {
let x: Double
let y: Double
}
Swift will automatically generate Point.init(x: Double, y: Double). If you add an init method of your own in the main struct definition, then Swift won't create that automatic init. (If you add an init in an extension, then you will get an automatic init. This is why people often add convenience init in an extension for structs.)
The point the last paragraph is trying to make is that Point(x:y:) is preferable to Point(_:_:). The labels in an initializer are even more valuable than labels in method names, because all initializers have the same "base" name ("init"). They're just explaining why they didn't choose a more implicit default that some people might expect coming from other languages.
In short, sometimes unlabeled parameters make sense in methods depending on what the name of the method is and how unambiguous that makes the first parameter. But in init, unlabeled parameters should be eyed with strong suspicion.
Delegation is a new concept for me. To my understanding, it asks someone else to do some job for me. I then, delegate some tasks to him.
class Vehicle {
var numberOfWheels = 0
var description: String {
return "\(numberOfWheels) wheel(s)"
}
}
class Bicycle: Vehicle {
override init() {
super.init() //# Delegate up
numberOfWheels = 2
}
}
The code super.init() is a delegate up action in class initialization. The subclass initializer calls the initializer of the superclass first. The superclass' default initializer assigning the integer 0 to the variable numberOfWheels. This is Phase one initialization. After that, the overriding action of the subclass' initializer further customizes the variable by numberOfWheels = 2.
Question Is there any incorrectness of my understanding? and I hope the delegate up description I'm used here is correct.
Please correct any error and misunderstandings I have it here. Thanks
What you are depicting here has nothing to do with delegation-pattern at all, it's the concept of inheritance. Your bicycle class is inheriting from Vehicle. Vehicle has already implemented some code, so instead of writing it again you can use the code of the super-class (the class that is inherited from). Your super-class doesn't have a defined initializer, therefore the super.init() wont even do anything. You should read about inheritance and try to understand this concept better.
Here's what delegation does: You are right about the idea of delegation. It allows you to "outsource" some work to another class. This can be achieved with protocols. A delegate has to conform a delegation protocol to ensure that it has the methods you want to call on it. You are using protocols instead of inherited classes here, because you don't care about the implementation of the specific methods, you just want to tell your delegate to handle a situation, it's up to the delegate to know what to do.
Delegation is most commonly used in MVC applications for macOS and iOS. You can read more about delegation in the Apple Documentation. There are also dozens of tutorials like this one on the internet that show how delegation works in practice.
I'm trying to create a subclass in which I will add more functions and properties to the SCNParticleSystem object.
The SCNParticleSystem header file in Swift has only this init declared:
public convenience init?(named name: String, inDirectory directory: String?)
So, what I want to do is use the same parameters as the init but also add a custom parameter to the init like this and do some setup for my properties:
init how ? (particleSystemFileName:String, inDirectory:String, parentNode:SCNNode)
{
init who ?
<setup my stuff>
}
I just can't figure out how to do this?
thx
It's just not possible to create convenience init from subclass to call convenience init in superclass. Why? Initializers has to follow 3 rules:
A designated initializer must call a designated initializer from its
immediate superclass.
A convenience initializer must call another initializer from the
same class.
A convenience initializer must ultimately call a designated
initializer.
Diagram that shows what you can actually do with initializers & subclassing (more on that topic):
What you can do is some sort of setup() method that will customize your created instance of a subclass with given properties.
I thought both these classes conformed to the rules of inherited initializers:
class Butt1 : UIButton {}
class Butt2<T> : UIButton {}
let butt1 = Butt1() // ok
let butt2 = Butt2<Void>() // error: no accessible initializer
The docs say:
As mentioned above, subclasses do not inherit their superclass
initializers by default. However, superclass initializers are
automatically inherited if certain conditions are met. In practice,
this means that you do not need to write initializer overrides in many
common scenarios, and can inherit your superclass initializers with
minimal effort whenever it is safe to do so.
Assuming that you provide default values for any new properties you
introduce in a subclass, the following two rules apply:
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.
In the case of Butt2 the compiler doesn't think it's "safe to do so", but why isn't it safe?