I'm building an iOS app in Swift and drawing on the Lister sample project Apple provides.
Lister uses two model objects: List and ListItem. I found that both of them do not call super.init() in their initializers even though they subclass NSObject.
However, in the Objective-C version of Lister, both model objects (AAPLList and AAPLListItem) do call [super init].
The Swift Programming Language clearly states that “designated initializers must call a designated initializer from their immediate superclass.” (Rule 1 of Initializer Chaining in Initialization)
What's going on here? Why is this an exception and if you shouldn't always call super.init() in a subclass, what rules do apply?
Even though I can't find a place in the documentation where this is described, what happens is that the default superclass initialiser is called at the end of the subclass initialiser if that is the only initialiser of the superclass, and it wasn't called explicitly.
NSObject only has the default initialiser (init()); you can see that the superclass initialiser is called at the end of the subclass initialiser by attempting to reference self (eg. println(self)) in a constructor that does not call super.init(): You are not allowed to do it because the class is not fully initialised at that point.
If you want to use self somewhere in the constructor, the object needs to be fully constructed at that point, so you need to call super.init() manually before then.
Related
The required modifier is "to indicate that every subclass of the class must implement that initializer" :
class SomeClass {
required init() {
// initializer implementation goes here
}
}
This entails that the subclasses don't have to inherit the superclasses' initializers. In fact, this Stackoverflow answer says that if a subclass has a designated initializer of its own, then it doesn't have to inherit the superclass' initializer.
However, the Swift documentation says that every class has to have a at least one designated initializer that "calls an appropriate superclass initializer to continue the initialization process up the superclass chain."
My questions are:
Can a subclass not inherit the superclass' initializer and, if so, in what cases it is beneficial to not inherit it?
What exactly does the required initializer do and in what case is satisfying the initializer inheritance requirement necessary?
Try and think about it in terms of what initialization does for an object. It sets values to parameters that do not have values set to them yet that need values set before use. See: https://docs.swift.org/swift-book/LanguageGuide/Initialization.html#ID228. So each class needs to have a way to initialize it and if necessary deal with setting variables or passing that responsibility along the subclass chain. The required init() function should be used as a unique case where somewhere in the initialization chain a special property is computed/set in required init() of a super class that makes it a requirement to call required init() in a subclass/subclasses of it. You do not need to write override required in this case.
I'm trying to add a color picker in my add and I use this https://github.com/gizmosachin/ColorSlider library which is only written in swift and I use objective-c. I have followed this guide How to call Objective-C code from Swift on how to add swift libraries in objective-c projects. I'm 99% sure that I have properly configured xcode because I can import the swift library and run my app without an error, it's only when I try to instantiate the swift class that the app crashes and I see that the init method for the swift class is called infinitely. The source code for the library is one file and listed for reference just in case (https://github.com/gizmosachin/ColorSlider/blob/master/Source/ColorSlider.swift)
Here is one of the init methods (the other inits are overrides)
// MARK: Initializers
convenience init() {
println("hi there swift")
self.init()
backgroundColor = UIColor.clearColor()
}
in my log I see "hi there swift" print out many times. This is how I initiate the swift class
ColorSlider *colorSlider = [[ColorSlider alloc] init];
I know that the function containing the line of code above is only being called once because I used NSLog(#"output") to see how many times this shows up and the output looks like this
output
hi there swift
hi there swift
hi there swift
hi there swift
hi there swift
etc...to infinity or until app crashes
Am I instantiating the swift class correctly? I'm not sure why the swift class's init method is called infinitely
----UPDATE-----
It looks as if the init methods below also use super.init?
As in Objective-C a class can have convenience (secondary) initializers and designated (primary) initializers.
The path of code execution should be:
A convenience initializer should call a designated initializer with self.designatedInit().
A designated initializer should call a super's designated initializer with super.designatedInit().
In your code, init() is a convenience initializer (convenience init()). calling self.init() would lead to an infinite loop, because this is the actually running function itself as Dan said.
If you change it to super.init() a convenience initializer is calling a super's initializer, what is illegal because of the above rule #1.
What to do?
Check, whether init() is really a convenience initializer.
If so, call self.designatedInit() instead of self.init().
If not so, change the classification of init() and call super.init() (or whatever is the designated initializer of the super class.
Remove self.init. The method is calling itself.
UIViewController class defines a single designated initializer, init(nibName nibNameOrNil: String?, bundle nibBundleOrNil: NSBundle?) and no convenience initializers, however, it is possible to write the following line of code and have it compile (Xcode 6.1.1)
let vc = UIViewController()
How is this possible?
According to the Swift book, here are the rules of initializer inheritance
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.”
Excerpt From: Apple Inc. “The Swift Programming Language.” iBooks.
https://itun.es/us/jEUH0.l
Therefore UIViewController couldn't have inherited the init() method from its ancestor superclass NSObject, then where does the initializer come from?
On a different note, because every swift class must ultimately call its designated initializer, doesn't that mean initWithCoder: will also end up calling init(nibName nibNameOrNil: String?, bundle nibBundleOrNil: NSBundle?)? However that doesn't seem to be the case in practice.
The rules in the Swift book apply to classes defined in Swift. UIViewController is a class imported from ObjC, so its behavior is subject to ObjC rules (or lack thereof).
In ObjC, the designated initializer chaining pattern is something you should do (unlike in Swift where the compiler enforces that it's the only thing you can do). If you call an inherited initializer instead of the designated initializer, you'll get back an instance... but there are no guarantees that said instance will be in a sensible state. (In fact, you're pretty much guaranteed it won't be.) Authors of ObjC classes sometimes practice defensive coding by implementing the initializers you're not supposed to call and having them throw exceptions, but that doesn't seem to be the case here.
This particular case is arguably a bug of some sort — it wouldn't be a bad idea to file one with Apple. Either Swift should enforce initializer rules when creating objects from imported ObjC API (in which case UIViewController would need to declare its designated initializers in ObjC with NS_DESIGNATED_INITIALIZER), or UIViewController should be implemented to guard against improper initialization. (Though it's possible there could be compatibility side effects to "fixing" either of those.)
The comment block above that init function contains the following text:
/*
The designated initializer. If you subclass UIViewController, you must call the super implementation of this
method, even if you aren't using a NIB. (As a convenience, the default init method will do this for you,
and specify nil for both of this methods arguments.) ...
*/
It is my understanding that because this is an Objective-C class, it is allowed to inherit methods implicitly. If you create a subclass of this class in swift, you will not have access to super.init(). If you create your subclass in Objective-C code without implementing your own -[ init], you will be able to construct your subclass with ().
Because your project drops into Objective-C land with the class that you're calling, it is able to find the selector. A swift subclass of an Objective-C class would follow the swift inheritance rules only.
What is the purpose of the initializer pattern other then in order to assure that the object is properly initialized by its superclass before the init method performs its initialisation.
Why is this necessary?
and
when we have a class which has more than one initialization method, why others shoud call the dedicated initialiser instead of superclass initialiser?
I'm coming from java background so don't fully understand this - the closest i could match in java was singleton pattern but the part others calling dedicated initialiser didn't make sense to me as in java you have a choice rather then you "should".
can any one elaborate....thx
The initializer pattern is necessary, because the super class whose initializer you are calling, can return any kind of object, not necessarily the instance of that class. That's how, for example, NSString works, it's actually a cluster of classes implementing different kinds of strings optimized for different usage patterns. So calling self = [super init] for NSString descendant makes self, for example, an NSCFString instance.
There's a pattern called Designated Initializer in Objective C. If the class has many initializer, one of them is chosen as designated, and all the other should be implemented by calling it, not the super. This is important for correctly overriding initializers in child classes, you should initialize only the designated one, and it will be called under all circumstances (assuming your code is well-written and takes advantage of designated initializers, of course :)
Reading through Apple's documentation on Tips and Techniques for Framework Developers, I came across this statement about designated initializers:
A designated initializer is an init method of a class that invokes an
init method of the superclass. (Other initializers invoke the init
methods defined by the class.) Every public class should have one or
more designated initializers.
(Emphasis added.)
Based on my understanding—and indeed, the very use of the word "designated"—a class should have only one designated initializer. But according to the documentation, multiple designated initializers are acceptable.
Assuming that you have two (or more) designated initializers, their role is to call the superclass's designated initializer in order to guarantee proper object initialization. But if both designated initializers are calling the same superclass's designated initializer, then why was there the need for more than one in the first place? Shouldn't the class be refactored to funnel all the other init methods to the singular designated initializer?
I'm just a bit confused as to what use case or design pattern would call for multiple designated initializers?
You would do this when you want to have a different initialization for different objects of the same class. One example is class clusters, like NSNumber. It has quite a few initializers for the different types of numbers they can hold. To provide the most accurate representation, the class should hold its value in the same format it received it in, instead of casting. This means the initializers can't simply call a common initializer and return. They need to do some custom work. This makes them a designated initializer.
Another example would be a document class which needs to do some initialization only for new files and some other initialization only for documents being opened. Both of these initializers will call their super implementation, which in turn calls the plain init method to do common initialization. However, since they do more than simply calling another initializer with a default value, they are considered designated initializers.