Over a million developers have joined DZone.
{{announcement.body}}
{{announcement.title}}

Tune Your Header File, Part I: Designated and Convenience Initializers

DZone's Guide to

Tune Your Header File, Part I: Designated and Convenience Initializers

Header files are a well-known construct in the world of Objective C and iOS development. It's worthwhile to have a look at optimizing them to make sure they perform well.

· Performance Zone
Free Resource

Transform incident management with machine learning and analytics to help you maintain optimal performance and availability while keeping pace with the growing demands of digital business with this eBook, brought to you in partnership with BMC.

Let’s talk about the header (.h) files. You probably know that what is defined in the header file has a public visibility. Any method or property declared here can be accessible to all of the other classes that are importing this file. That is one of the reasons why we need to carefully plan what is going to be in this file, which is sometimes referred as the public API of the class. There are two important things I wanted to discuss in this series. One is the designated initializer and the other is the nullable/notnull keywords. Usually, you will use both of them in the Objective-C header files.

Designated Initializer

You probably have heard about them, but let me recap. Before we can use any object, first we need to allocate a memory address to it, and after the allocation, we need to initialize it. Usually, it is done by [[MyClass alloc] init] form. Obviously, you are able to overwrite the default init method in the implementation file (MyClass.m), but there are other reasons not to do so.

One of the reasons is that your object probably needs to have some other objects or variables handed over and set up when the object created, with its initial state. For example, let's say MyObject has two properties: (BOOL)isNightMode and (MyOtherObject *)theObjectDoingSomethingElse. If you want to instantiate an object from code and set up those properties, you probably will do it in this way:

MyObject *myObject =[[MyObject alloc] init];
myObject.isNightMode = YES;
myObject.theObjectDoingSomethingElse = alreadyExisitingObject;

The problem with that solution is whether you or any of your developer colleagues can easily forget to set the isNightMode and theObjectDoingSomethingElse right after initializing the object. So, the best practice is to pass those variables at the same time, when we instantiate the class, like this:

MyObject *myObject =[[MyObject alloc] initWithTheOtherObjectDoingSomethingElse:alreadyExisitingObject 
                     isNightModeOn:isNightMode];

As you can see, we are passing the values at the time of init, which means that we need to create a public method with the same signature (same count and type of parameters), and if we forget to do so, we will have a build error when you compile your project. We will have a build error if we want to use the defined method without all of the required parameters. As a best practice, we also want this method to be the default initializer, which Apple calls Designated initializer. There is a macro defined for that purpose, which is the NS_DESIGNATED_INITIALIZER. Don’t forget: You can have only one designated initializer, usually the one which called [super init].

If we want to be fully covered, we also need to take care of the default init method since we don’t want anybody to use this anymore. There is a very nice trick with using the macro NS_UNAVAILABLE. Just simply put the following code into your header file and the default init won’t be available anymore.
- (nullable instancetype)init NS_UNAVAILABLE;

Let’s put everything together in code, then. My header file (MyObject.h) will look like this:

@interface MyObject : NsObject

//1
- (instancetype)initWithTheOtherObjectDoingSomethingElse:(MyOtherObject *)alreadyExisitingObject isNightModeOn:(BOOL)isNightMode NS_DESIGNATED_INITIALIZER;

  /**
   Removing the default initalizer
   */
   - (nullable instancetype)init NS_UNAVAILABLE;
@end

My implementation file will look like this:

@interface MyObject()
@property (nonatomic) BOOL isNightMode 
@property (strong, nonatomic) MyOtherObject *theObjectDoingSomethingElse
@end

@implementation MyObject

#pragma mark - Initializers
//2
- (instancetype)initWithTheOtherObjectDoingSomethingElse:(MyOtherObject *)alreadyExisitingObject isNightModeOn:(BOOL)isNightMode {
self= [super init];
if (self) {
_isNightMode = isNightMode;
_theObjectDoingSomethingElse = alreadyExisitingObject;
}
}

@end

//1 As you might have noticed, I moved the properties from the header file to the implementation file (opening a new section, with @interface). In this way, they won’t be part of the public API anymore, so they will be set at the init time and cannot be changed. On the other hand, we lose the opportunity to change those properties from outside of the class. If you want to still change any of them, the easiest way is to move it back to the header file. Nevertheless, don’t forget the encapsulation principle: always try to challenge your decision when you want to expose a property to the publicity.

//2 Here is our new designated initializer. As you can see, the parameters passed in the method parameter list have been assigned to the private variables (starting with underscore).

Convenience Initializers

I think we did good work here so far, but what if we need an initializer that won’t require the nightMode for our convenience? No worries. We can handle this; add the initWithTheOtherObjectDoingSomethingElse:alreadyExisitingObject method to the MyObject.h:

@interface MyObject : NsObject

//1
- (instancetype)initWithTheOtherObjectDoingSomethingElse:(MyOtherObject *)alreadyExisitingObject isNightModeOn:(BOOL)isNightMode NS_DESIGNATED_INITIALIZER;

/**
 Removing the default initalize
*/
- ( instancetype)init NS_UNAVAILABLE;

/**
  Convenience initializer
*/
- (instancetype)initWithTheOtherObjectDoingSomethingElse:(MyOtherObject *)alreadyExisitingObject;
@end

We also need to add the implementation to the MyObject.m:

@interface MyObject()
@property (nonatomic) BOOL isNightMode 
@property (strong, nonatomic) MyOtherObject *theObjectDoingSomethingElse
@end

@implementation MyObject

#pragma mark - Initializers
//2
- (instancetype)initWithTheOtherObjectDoingSomethingElse:(MyOtherObject *)alreadyExisitingObject isNightModeOn:(BOOL)isNightMode {
self= [super init];
if (self) {
_isNightMode = isNightMode;
_theObjectDoingSomethingElse = alreadyExisitingObject;
}
}

- (instancetype)initWithTheOtherObjectDoingSomethingElse:(MyOtherObject *)alreadyExisitingObject {
return [self initWithTheOtherObjectDoingSomethingElse:alreadyExisitingObject isNightModeOn:NO];
}
@end


That’s it. The only drawback to this solution is that we made the isNightmode as a constant NO, but if we are doing it intentionally, then it’s OK. Usually, this approach is used when we need a convenient way to initialize the object, keeping some of the parameters hard-coded. This is why it is called convenient initializer. I would suggest to have the designated initializer including all of the injected variables and to create convenience initializers which serve your needs.

The technique above is similar to dependency injection, but in order to have a proper dependency injection, we need to involve protocols.

Let me know your thoughts!

Evolve your approach to Application Performance Monitoring by adopting five best practices that are outlined and explored in this e-book, brought to you in partnership with BMC.

Topics:
headers ,objective c ,performance ,ios development

Published at DZone with permission of Peter Molnar, DZone MVB. See the original article here.

Opinions expressed by DZone contributors are their own.

THE DZONE NEWSLETTER

Dev Resources & Solutions Straight to Your Inbox

Thanks for subscribing!

Awesome! Check your inbox to verify your email so you can start receiving the latest in tech news and resources.

X

{{ parent.title || parent.header.title}}

{{ parent.tldr }}

{{ parent.urlSource.name }}