Objective-C LanguageClasses and Objects

Syntax

  • Cat *cat = [[Cat alloc] init]; // Create cat object of type Cat
  • Dog *dog = [[Dog alloc] init]; // Create dog object of type Dog
  • NSObject *someObject = [NSObject alloc]; [someObject init]; // don’t do this
  • XYZObject *object = [XYZObject new]; // Use new to create objects if NO arguments are needed for initialization
  • NSString *someString = @"Hello, World!"; // Creating an NSString with literal syntax
  • NSNumber *myFloat = @3.14f; // Another example to create a NSNumber using literal syntax
  • NSNumber *myInt = @(84 / 2); // Create an object using a boxed expression

Creating classes with initialization values

#import <Foundation/Foundation.h>
@interface Car:NSObject {
    NSString *CarMotorCode;
    NSString *CarChassisCode;
}

- (instancetype)initWithMotorValue:(NSString *) motorCode andChassisValue:(NSInteger)chassisCode;
- (void) startCar;
- (void) stopCar;

@end

@implementation Car

- (instancetype)initWithMotorValue:(NSString *) motorCode andChassisValue:(NSInteger)chassisCode{
    CarMotorCode = motorCode;
    CarChassisCode = chassisCode;
    return self;
}

- (void) startCar {...}
- (void) stopCar {...}

@end

The method initWithMotorValue: type andChassisValue: type will be used to initialize the Car objects.

Singleton Class

What is a Singleton Class?

A singleton class returns the same instance no matter how many times an application requests it. Unlike a regular class, A singleton object provides a global point of access to the resources of its class.

When to Use Singleton Classes?

Singletons are used in situations where this single point of control is desirable, such as with classes that offer some general service or resource.

How to Create Singleton Classes

First, create a New file and subclass it from NSObject. Name it anything, we will use CommonClass here. Xcode will now generate CommonClass.h and CommonClass.m files for you.

In your CommonClass.h file:

#import <Foundation/Foundation.h>

@interface CommonClass : NSObject {
}
+ (CommonClass *)sharedObject;
@property NSString *commonString;
@end

In your CommonClass.m File:

#import "CommonClass.h"

@implementation CommonClass

+ (CommonClass *)sharedObject {
    static CommonClass *sharedClass = nil;
    static dispatch_once_t onceToken;
    dispatch_once(&onceToken, ^{
        sharedClass = [[self alloc] init];
    });
    return sharedClass;
}

- (id)init {
    if (self = [super init]) {
        self.commonString = @"this is string";
    }
    return self;
}

@end

How to Use Singleton Classes

The Singleton Class that we created earlier will be accessible from anywhere in the project as long as you have imported CommonClass.h file in the relevant module. To modify and access the shared data in Singleton Class, you will have to access the shared Object of that class which can be accessed by using sharedObject method like following:

[CommonClass sharedObject]

To read or modify the elements in Shared Class, do the following:

NSString *commonString = [[CommonClass sharedObject].commonString; //Read the string in singleton class

NSString *newString = @"New String";
[CommonClass sharedObject].commonString = newString;//Modified the string in singleton class

The "instancetype" return type

Objective-C supports a special type called `instancetype that can only be used as type returned by a method. It evaluates to the class of the receiving object.

Consider the following class hierarchy:

@interface Foo : NSObject

- (instancetype)initWithString:(NSString *)string;

@end

@interface Bar : Foo
@end

When [[Foo alloc] initWithString:@"abc"] is called, the compiler can infer that the return type is Foo *. The Bar class derived from Foo but did not override the declaration of the initializer. Yet, thanks to instancetype, the compiler can infer that [[Bar alloc] initWithString:@"xyz"] returns a value of type Bar *.

Consider the return type of -[Foo initWithString:] being Foo * instead: if you would call [[Bar alloc] initWithString:], the compiler would infer that a Foo * is returned, not a Bar * as is the intention of the developer. The instancetype solved this issue.

Before the introduction of instancetype, initializers, static methods like singleton accessors and other methods that want to return an instance of the receiving class needed to return an id. The problem is that id means "an object of any type". The compiler is thus not able to detect that NSString *wrong = [[Foo alloc] initWithString:@"abc"]; is assigning to a variable with an incorrect type.

Due to this issue, initializers should always use instancetype instead of id as the return value.

Specifying Generics

You can enhance your own classes with generics just like NSArray or NSDictionary.

@interface MyClass<__covariant T>

@property (nonnull, nonatomic, strong, readonly) NSArray<T>* allObjects;

- (void) addObject:(nonnull T)obj;

@end

Difference between allocation and initialization

In most object oriented languages, allocating memory for an object and initializing it is an atomic operation:

// Both allocates memory and calls the constructor
MyClass object = new MyClass();

In Objective-C, these are separate operations. The class methods alloc (and its historic sibling allocWithZone:) makes the Objective-C runtime reserve the required memory and clears it. Except for a few internal values, all properties and variables are set to 0/NO/nil.

The object then is already "valid" but we always want to call a method to actually set up the object, which we call an initializer. These serve the same purpose as constructors in other languages. By convention, these methods start with init. From a language point of view, they are just normal methods.

// Allocate memory and set all properties and variables to 0/NO/nil.
MyClass *object = [MyClass alloc];
// Initialize the object.
object = [object init];

// Shorthand:
object = [[MyClass alloc] init];