How Instances Are Created
Your class objects are created for you automatically as your program starts up, but instances must be created deliberately as the program runs. The entire question of where instances come from is thus crucial. Ultimately, every instance comes into existence in just one way: someone turns to a class and ask that class to instantiate itself.
But there are three different ways in which this can occur: ready-made instances, instantiation from scratch, and nib-based instantiation.
Ready-Made Instances
One way to create an instance is indirectly, by calling code that does the instantiation for you. You can think of an instance obtained in this indirect manner as a “ready-made instance.” (That’s my made-up phrase, not an official technical term.) For example, consider this simple code:
NSString* s2 = [s uppercaseString];
The NSString produced by the uppercaseString method is a ready-made NSString instance. Your code didn’t say anything about instantiation; it just sent the uppercaseString message. But clearly someone said something about instantiation, because instantiation took place; this is a newly minted NSString instance. That someone is presumably some code inside the NSString class. But we don’t have to worry about the details. We are guaranteed of receiving a complete ready-made ready-to-roll NSString,and that’s all we care about.
Instantiation from Scratch
The alternative to requesting a ready-made instance is to tell a class, yourself, directly,to instantiate itself. There is basically one way to do this: you send a class the alloc message. The alloc class method is implemented by the NSObject class, the root class from which all other classes inherit. It causes memory to be set aside for the instance so that an instance pointer can point to it.
You must never, never, never call alloc by itself. You must immediately call another method, an instance method that initializes the newly created instance, placing it into a known valid state so that it can be sent other messages. Such a method is called an initializer. Moreover, an initializer returns an instance — usually the same instance, initialized. Therefore you can, and always should, call alloc and the initializer in the same line of code. The minimal initializer is init. So the basic pattern, known informally as “alloc-init,” looks like Example 5-1.
Example 5-1. The basic pattern for instantiation from scratch
SomeClass* aVariable = [[SomeClass alloc] init];
The designated initializer
If a class does define initializers, one of them may be described in the documentation as the designated initializer. (There’s nothing about a method’s name that tells you it’s the designated initializer; you must peruse the documentation to find out.) For example, in the UIView class documentation, the initWithFrame: method is described as the designated initializer. A class that does not define a designated initializer inherits its designated initializer; the ultimate designated initializer, inherited by all classes without any other designated initializer anywhere in their superclass chain, is init.
Nib-Based Instantiation
A nib file (whose extension may be .nib or .xib) is where Xcode lets you "draw" parts of the user interface, and will then be loaded as the app runs. A nib file consists, in a sense, of the names of classes along with instructions for instantiating and initializing them. When the app runs and a nib file is loaded, those instructions are carried out -- those classes are instantiated and initialized.
The fact that nib files are a source of instances, and that those instances are brought into existence as the nib file is loaded.
Polymorphism
The compiler, even in the world of static typing, is perfectly happy for you to supply a subclass instance where a superclass type is declared. To see this, let’s start with the first line of the previous example:
UIButton* b = [UIButton buttonWithType:UIButtonTypeRoundedRect];
UIButton is a subclass of UIControl, which is a subclass of UIView. So it would be perfectly legal and acceptable to say this:
UIButton* b = [UIButton buttonWithType:UIButtonTypeRoundedRect];
UIView* v = b;
we treated a UIButton object as a UIView, yet we were still able to send it a UIButton message. We treated a UIButton as a UIButton, yet we were still able to send it a UIView message. What matters when a message is sent to an object is not how the variable pointing to that object is declared but what class the object really is. What an object really is depends upon its class, along with that class’s inheritance from the superclass chain; these facts are innate to the object and are independent of how the variable pointing to the object presents itself to the world. This independent maintenance of object type integrity is the basis of what is called polymorphism.
But it is not quite the whole of polymorphism. To understand the whole of polymorphism, we must go further into the dynamics of message sending.
Instance Variables
one of the main reasons there are instances and not just classes is that instances can have instance variables.
By default, instance variables are protected, meaning that other classes (except for subclasses) can’t see them. So if, somewhere else, I instantiate a Dog, I won’t be able to access that Dog instance’s number instance variable. This is a deliberate feature of Objective-C; you can work around it if you like, but in general you should not. Instead, if you want to provide public access to an instance variable, write an accessor method and make the method declaration public.
Within a class, on the other hand, that class’s own instance variables are global. Any But global variables can be confusing when you’re reading code; suddenly there’s a variable called number and you don’t understand what it is, because there’s no declaration for it (the declaration is stashed away in the interface section, which is in a different file). So I often use a different notation, like this:self->ivarName. The “arrow” operator, formed by a minus sign and a greater-than sign,
is called the structure pointer operator, because of its original use in C.
Key–Value Coding
Objective-C provides a means for translating from a string to an instance variable accessor, called key–value coding. Such translation is useful, for example, when the name of the desired instance variable will not be known until runtime. So, for example, instead of calling [fido number], we might have a string @"number" that tells us what accessor to call. This string is the “key.” To use key–value coding to get the value of the number instance variable from the fido instance, we would say:
int n = [fido valueForKey: @"number"];
Properties
A property is a syntactical feature of Objective-C 2.0 designed to provide an alternative to the standard syntax for calling an instance variable accessor.
To use a property within the class that declares that property, you must use self explicitly. So, for example:
self.number = 42;
Do not confuse a property with an instance variable. An expression like self->number = n, or even simply number = n, sets the instance variable directly (and is possible only within the class, because instance variables are protected by default). An expression like fido.number or self.number involves a property and is equivalent to calling a getter or setter method. That getter or setter method may access an instance variable, and that instance variable may have the same name as the property, but that doesn’t make them the same thing.
Objective-C uses dot-notation for properties, and C uses dot-notation for structs; these can be chained. So, for example, UIView’s frame is a property whose value is a struct (a CGRect); thus, you can say myView.frame.size.height, where frame is a property that returns a struct, size is a component of that struct, and height is a component of that struct. But a struct is not a pointer, so you cannot (for example) set a frame’s height directly through a chain starting with the UIView, like this:
myView.frame.size.height = 36.0; // compile error
Instead, if you want to change a component of a struct property, you must fetch the property value into a struct variable, change the struct variable’s value, and set the entire property value from the struct variable:
CGRect f = myView.frame;
f.size.height = 0;
myView.frame = f;
How to Write an Initializer
- (id) initWithNumber: (int) n
Our return value is typed as id, not as a pointer to a Dog, even though in fact we will return a Dog object. This is a convention that we should obey. The name is conventional as well; as you know, the init beginning tells the world this is an initializer.
- (id) initWithNumber: (int) n {
self = [super init];
if (self) {
self->number = n;
}
return self;
}
The parts of the convention are:
1.We send some sort of initialization message, calling a designated initializer. If this is our class’s designated initializer, this message is sent to super and calls the superclass’s designated initializer. Otherwise, it is sent to self and calls either this class’s designated initializer or another initializer that calls this class’s designated initializer.In this case, this is our class’s designated initializer, and the superclass’s designated initializer is init.
2.We capture the result of the initialization message to super, and assign that result to self. It comes as a surprise to many beginners (and not-so-beginners) that one can assign to self at all or that it would make sense to do so. But one can assign to self (because of how Objective-C messaging works behind the scenes), and it makes sense to do so because in certain cases the instance returned from the call to super might not be same as the self we started with.
3.If self is not nil, we initialize any instance variables we care to. This part of the code is typically the only part you’ll customize; the rest will be according to the pattern. Observe that I don’t use any setter methods; in initializing an instance variable not inherited from the superclass, you should assign directly to the instance variable (and if it’s an object, you’ll also have to do some memory management, to be explained in Chapter 12).
4.We return self.
But we are not finished. Recall from earlier in this chapter that a class that defines a designated initializer should also override the inherited designated initializer (in this case, init). And you can see why: if we don’t, someone could say [[Dog alloc] init] and create a dog without a number — the very thing our initializer is trying to prevent. Just for the sake of the example, I’ll make the overridden init assign a negative number as a signal that there’s a problem. Notice that we’re still obeying the rules: this initializer is not the designated initializer, so it calls this class’s designated
initializer.
- (id) init {
return [self initWithNumber: -9999];
}
All instance variables are set to a form of zero by alloc. Therefore, any instance variables not initialized explicitly in an initializer remain 0. This means, among other things, that by default a BOOL instance variable is NO and an object reference instance variable is nil. It is common practice to take advantage of these defaults in your program; if the default values are satisfactory initial values, you won’t bother to set them in your designated initializer.