There are two parts of code, there result seems same in the *.playground, So that make me a little confused, so what the exactly difference between designated and convenience init in this code below, and how could I know when should I use a convenience init rather than a designated init?
Part 1
class Food {
var name: String
init(name: String) {
self.name = name
}
convenience init() {
self.init(name: "[Unnamed]")
}
}
let food = Food()
let food2 = Food(name: "Rice")
Part 2
class Food {
var name: String
init(name: String) {
self.name = name
}
init() {
self.name = "[Unnamed]"
}
}
let food = Food()
let food2 = Food(name: "Rice")
In part 1 and part 2 the different part are
//part 1
convenience init() {
self.init(name: "[Unnamed]")
}
//part 2
init() {
self.name = "[Unnamed]"
}
The difference arises in subclasses; in what you've shown there is no usage difference.
“Convenience initializers must always delegate across” meaning in class A a convenience initializers calls a designated initializers in class A. “Designated initializers must always delegate up” meaning class B, a subclass of A, calls designated initializers in A.
Thus, in your 'part 2' both of the initializers in Food are available to subclasses of Food. But, in your 'part 1', only one initializer can be used in subclasses.
For example (Food in the following is your 'part 1'):
21> class Fat : Food {
22. init () {
23. super.init()
24. }
25. }
repl.swift:23:9: error: must call a designated initializer of the superclass 'Food'
super.init()
^
Quotes are: Excerpt From: Apple Inc. “The Swift Programming Language.” iBooks. https://itun.es/us/jEUH0.l
Related
I'm working through Swift apprentice 3rd Edition.
The book quickly skips through required initialisers.
If I have a super class :
class Animal {
let animalType: String
required init(animalType: String) {
self.animalType = animalType
}
convenience init(animal: Animal) {
self.init(animalType: animal.animalType)
}
}
and then subclass like:
class WildAnimal: Animal {
let name: String
required init(animalType: String) {
name = ""
super.init(animalType: animalType)
}
}
The only way I can initialise the subclass is to assign placeholder values in the required init. i.e. I had to put name = "".
Is there no way to initialise a subclasses parameters during initialisation if the parent class has a required init?
You can have more than one initializer. You just also have to "override" the required parent initializers.
class WildAnimal: Animal {
var name: String
required init(animalType: String) {
name = ""
super.init(animalType: animalType)
}
init(animalType: String, name: String) {
self.name = name
super.init(animalType: animalType)
}
}
Forcing subclasses to define de required initializers is the point of required.
Write the required modifier before the definition of a class
initializer to indicate that every subclass of the class must
implement that initializer
Source
I keep getting this error and I don't understand why.
error: delegating initializers in structs are not marked with 'convenience'
This is what I have (as an example), a DeprecatedCurrency and a SupportedCurrency.
struct DeprecatedCurrency {
let code: String
}
struct SupportedCurrency {
let code: String
}
I then want to add a convenience init function for converting from the deprecated currency object to the new currency object. And this is what I have:
struct DeprecatedCurrency {
let code: String
}
struct SupportedCurrency {
let code: String
convenience init(_ currecny: DeprecatedCurrency) { // error!!
self.init(code: currecny.code)
}
init(code: String) {
self.code = code
}
}
What does this error even mean and how do I fix it?
I know that if we don't provide a default initializer, a initializer with signature init(code: String) will be automatically generated for us with struct in Swift. So by the end of the day, what I am really looking for is (if possible):
struct SupportedCurrency {
let code: String
convenience init(_ currecny: DeprecatedCurrency) { // error!!
self.init(code: currecny.code)
}
}
Just remove the convenience, it is not required for struct.
From Swift documentation.
Initializers can call other initializers to perform part of an instance’s initialization. This process, known as initializer delegation, avoids duplicating code across multiple initializers.
They haven't mentioned using convenience. It is convenience in semantic but doesn't require the keyword.
struct DeprecatedCurrency {
let code: String
}
struct SupportedCurrency {
let code: String
init(_ currency: DeprecatedCurrency) { // error!!
self.init(code: currency.code)
}
init(code: String) {
self.code = code
}
}
structs don't need the word convenience
Try this:
struct SupportedCurrency {
let code: String
init(_ currency: DeprecatedCurrency) { // error!!
self.init(code: currency.code)
}
init(code: String) {
self.code = code
}
}
The question is not why don't we put convenience for structs but why do we put convenience for classes. The reason is that classes have inheritance. With a class you need to call the super class's designated constructor (not sure if that is the correct terminology, it comes from Objective-C's initialsers.. The word convenience marks the constructor as "not the designated constructor".
One option is to add the new init in an extension of your struct. This way, you wont loose the default auto generated memberwise initialiser.
struct SupportedCurrency {
let code: String
}
extension SupportedCurrency {
init(_ currency: DeprecatedCurrency) {
self.init(code: currency.code)
}
}
From Apple docs
NOTE
If you want your custom value type to be initializable with the
default initializer and memberwise initializer, and also with your own
custom initializers, write your custom initializers in an extension
rather than as part of the value type’s original implementation. For
more information, see Extensions.
Assume a protocol defined below:
protocol Identifiable {
static var identifier: String { get }
}
extension Identifiable {
static var identifier: String { return "Default Id" }
}
What is the best way to reference the static variable? The example below illustrates two ways to access the variable. What is the difference, and is the type(of:) better?
func work<I: Identifiable>(on identifiable: I) {
let identifier: String = I.identifier
print("from Protocol: \(identifier)")
let identiferFromType: String = type(of: identifiable).identifier
print("using type(of:): \(identiferFromType)")
}
struct Thing: Identifiable {
static var identifier: String { return "Thing" }
}
work(on: Thing())
In the example you show, there is no difference. Because identifier is a protocol requirement, it will be dynamically dispatched to in both cases, therefore you don't need to worry about the wrong implementation being called.
However, one difference arises when you consider the value of self inside the static computed property when classes conform to your protocol.
self in a static method/computed property is the metatype value that it's is called on. Therefore when called on I, self will be I.self – which is the static type that the compiler infers the generic placeholder I to be. When called on type(of: identifiable), self will be the dynamic metatype value for the identifiable instance.
In order to illustrate this difference, consider the following example:
protocol Identifiable {
static var identifier: String { get }
}
extension Identifiable {
static var identifier: String { return "\(self)" }
}
func work<I : Identifiable>(on identifiable: I) {
let identifier = I.identifier
print("from Protocol: \(identifier)")
let identiferFromType = type(of: identifiable).identifier
print("using type(of:): \(identiferFromType)")
}
class C : Identifiable {}
class D : C {}
let d: C = D()
// 'I' inferred to be 'C', 'type(of: d)' is 'D.self'.
work(on: d)
// from Protocol: C
// using type(of:): D
In this case, "which is better" completely depends on the behaviour you want – static or dynamic.
I'm trying to access a default method implementation defined in a protocol extension that's constrained in implementation to a class. A 'regular' declaration works fine, however when I try to cast to the protocol I can not access the default defined method on the protocol, though the type satisfies the where clause.
Please consider this example:
class Person {
var name: String
init(name: String) {
self.name = name
}
}
class Hero: Person {
var ability: String
init(name: String, ability: String) {
self.ability = ability
super.init(name: name)
}
}
class Pilot: Person {
var callSign: String
init(name: String, callSign: String) {
self.callSign = callSign
super.init(name: name)
}
}
class Programmer: Person {
var favoriteLanguage: String
init(name: String, favoriteLanguage: String) {
self.favoriteLanguage = favoriteLanguage
super.init(name: name)
}
}
// define a protocol
protocol PersonPresenter: class { }
// extend it where the conformer is a type of Person
extension PersonPresenter where Self: Person {
func displayName() {
print(name.uppercased())
}
}
// conform subclasses of Person to PersonPresenter
extension Hero: PersonPresenter { }
extension Pilot: PersonPresenter { }
extension Programmer: PersonPresenter { }
let myHero = Hero(name: "Hiro", ability: "Bend time & space")
myHero.displayName() // prints 'HIRO'
let myPilot = Pilot(name: "Pete", callSign: "Maverick")
myPilot.displayName() // prints 'PETE'
let myProgrammer = Programmer(name: "Chris", favoriteLanguage: "Swift")
myProgrammer.displayName() // prints 'CHRS'
let people: [Person] = [myHero,myPilot,myProgrammer]
if let presenter = people[0] as? PersonPresenter {
presenter.displayName() // Errror, PerseonPresenter is not a subtype of 'Person'
}
I would like to find a way to cast to PersonPresenter while satisfying the where constraint so as not to be forced to try and cast to each specific sub-class to access the default implementation of the protocol extension. Or to not have to conform the super class (which may be used in many other places) to the protocol.
The real problem is that you are extending an empty protocol.
By casting to Hero, the compiler knows it is a subclass of Person and then it satisfies the extension constraint.
But when casting to the protocol PersonPresenter itself, the compiler doesn't know if the constraint (being a Person) can be satisfied.
If you declare the requirement in the protocol, it will work:
protocol PersonPresenter: class {
func displayName()
}
I have a class called Letter
class Letter
{
init() {}
}
And I have an extension for this class:
extension Letter
{
convenience init(file_path:String) {
self = Letter.loadFromFile(file_path)
}
class func loadFromFile(file_path:String)->Letter {...}
}
I need to create and init with path to file and when i call Letter(file_path) I need a new object that returned by a func loadFromFile. How to assign in an init method or to return a new object?
It gives the error:
Cannot assign to value: 'self' is immutable
Class functions that return instances of that class seems to be an anti-pattern in Swift. You'll notice that the "with" Objective-C class methods like [NSString stringWithString:#"some other string"] made the transition as "with"-less convenience initializers: NSString(string: "some other string").
Furthermore, you'll need to delegate to a designated initializer from within a convenience initializer.
Also, since you're 1) defining the original class and 2) don't need the convenience initializer scoped differently than the designated initializer, I don't see any reason to place it in an extension.
Putting those together:
class Letter {
init() { … }
convenience init(filePath: String) {
self.init()
loadFromFile(filePath)
}
func loadFromFile(filePath: String) { … }
}
let letter1 = Letter()
letter1.loadFromFile("path1")
let letter2 = Letter(filePath: "path2")
In summary, the analogy for assigning to self in Swift is calling an initializer.
Let me know if this works for you!
Convenience initializer must delegate up to designated initializer
It says that convenience init(file_path:String) should call other initialiser
convenience init(file_path:String) {
self.init()
//here can set other properties
}
Convenience initialiser usually provide some default parameters
Convenience initialiser are designed to make creation of class instance less complicated. It means that you don't need to pass all arguments to constructor. In your example the class should look like this
Designated initializer takess all possible arguments.
Convenience provide default value
Code example
// Create instance of a Letter
Letter()
Letter(file_path: "path.txt")
Letter(file_path: "path.txt", option: 0, other: 0)
//Class Implementation
class Letter
{
init(file_path: String , option: Int, other: Int) {
// Instansiate class
}
}
extension Letter {
convenience init() {
self.init(file_path:"a")
}
convenience init(file_path:String) {
self.init(file_path: file_path , option: 0, other: 0)
}
class func loadFromFile(file_path:String) -> Letter {
return Letter()
}
}
Now you can create instance of Letter this way -
You can't assign to self. What about something like this:
class Letter {
}
extension Letter {
convenience init(filePath: String) {
self.init()
// code to load a Letter from a file goes here.
}
class func loadFromFile(filePath: String) -> Letter {
return Letter(filePath: filePath)
}
}