Saturday, April 16, 2016

Convenient Swift

I am not talking about how convenient Swift is, in this blog, though it is obviously convenient. No. This is about  the keyword "convenience". In Swift init() is the inbuilt function keyword to initialize a class's instance. More or less like a C++ constructor. I say more or less  because Swift has its own set of sweet rules for its initializer.

Lets dig a little bit deeper into that.

C++: 
In C++03, there are constructors, obviously, and there is a rule that one constructor cannot call upon another. So all the member values have to be initialized again. Or a common function has to be built and both the constructors have to call that common function with filled in parameters if required. This seems like a minor inconvenience for smaller classes. But imagine that you have to type out the same for a huge list of members, as is the case in many commercial applications and games.

C++ Gurus understood the inconvenience and broke that rule in C++11. Now one constructor can call another constructor.  Whew!! Thank you ISO committee.

Since C++11 you can call other constructors and the term is coined as delegate constructors, what we call convenience initializers in Swift.

This is a convenience for C++11, because default parameters need not be added to the declaration of the delegate constructors, but only to the implementation.
for e.g

class MyPhone
{
  std::string ContactNumber;

  MyPhone(std::string Number):     ContactNumber(Number){ } 
   
  MyPhone(): MyPhone("000-000-000"){ }

}

If you had to use default value for the Number string argument, then you would have to do

MyPhone(std::string Number  = "000-000-000") in the declaration. This is inter-tangled with the project that might use this library code. Hence every time and anytime the default value changes, the client project has to be rebuilt.

But now with delegate constructors, it will not rebuild client code whenever default value changes. Because the default value is provided in the implementation in the form of delegate constructors. You might wonder, how might this make a difference in huge code base, where anyways, major changes only will be made which would require client project to be rebuilt. Well, every small step towards optimization counts rite? These small drops of performance boosters make up for the faster iteration cycles during development or later.

There are few caveats we have to remember.

1. In C++03, since there are no delegate constructors, when one constructor finishes execution, compiler considers the object to have been initialized. But in C++11 when any constructor finishes execution, compiler considers the object to have been initialized.

2. After C++11 you cannot delegate and initalize at the same time. For e.g if you call

MyPhone(): MyPhone("000-000-000") you cannot initialize other members in the same delegate constructor like this:

MyPhone(): MyPhone("000-000-000"), SecondMember("second"){ } //not allowed. 

3. Try catch is allowed in the delegate constructor.

MyPhone(): try MyPhone("000-000-000"){ 
   ------
  }
  catch(...){ 
    //handle exceptions

  }

Swift: 

So now coming to Swift, convenience init has to call atleast one

Swift has some seemingly out of the blue rules about its init, and the use of convenience in init() to create initializers. So after reading the official doc, and the tutorials, this is what pops out.

1. A convenience initializer must call any other initializer from the same class
2. A designated initializer can only call another designated initializer from its base class.
3. A convenience initializer must eventually call a designated initializer, otherwise compiler complains.
4. You cannot call one init() from another without making the calling init as convenience init, unlike C++ where extra keyword is not required to indicate delegation

init(ModelNumber: Int, color: BlockColor){
self.color = color;
self.ModelNumber = ModelNumber;
}

convenience init(ModelNumber: Int){
self.init(column: ModelNumber, color: BlockColor.random())
}


So basically almost the same semantics, except for a few. Initializer list in the constructor is not allowed in Swift, and swift initializers cannot fail and return nil. Seriously they are not allowed to fail as per Swift 2.0, instead they should throw the error out for the callee to handle explicitly.

convenience init(ModelNumber: Intthrows
  //handle ... 

}

//In the calling function

do {
    let str = try MyPhone(3433)
}
catch let error as NSError {
    print(error.localizedDescription)
}


There it is. tip of the iceberge. I am sure many more implementation mammoths are there related to the delegate constructors and the convenience initializers. For now this is a quick glance into both.


No comments: