Extension 13: Complex Initializers


The subject of initializer methods deserves a bit of special attention. Most importantly, in addition to the default initializer method that is inherited from NSObject, most classes also have other initializers that take certain arguments. All initializers, however, follow the same pattern. Let’s look at an example.

Convenience Initializers

Additional initializers are often known as convenience initializers because you can initialize the object and set some or all of its instance variables (and perhaps other values) in one method call. To begin, let’s take a look at some of NSString‘s initializers. These include initWithString:, which takes an NSString as an argument and creates a new string from that, initWithFormat:, which takes a formatted string and a corresponding number of arguments, and plugs in the proper values for each format specifier, and returns the completed string, and initWithContentsOfFile:encoding:error:, which creates a string out of any text file. The latter example is a perfect example of the convenience nature of these methods—doing the same with C library functions would be a lot more difficult. Note also that NSString has class methods that do the same thing (except that according to the memory management rules, the values are autoreleased).

If we were to create a Rectangle class, you might have an initializer like this:

-(id)initWithWidth:(float)w andHeight:(float)h {
    if(!(self = [super init]))
        return nil;
    self.width = w;
    self.height = h;
    return self;
}

Not too much that’s new there. After the initialization, we assign proper values to our instance variables and return. Let’s add another initializer:

-(id)initWithWidth:(float)w andArea:(float)a {
    if(!(self = [super init]))
        return nil;
    self.width = w;
	self.height = (a / w);
	return self;
}

Not much that’s new here. We’ve simply created another convenience initializer.

The Designated Initializer

The interesting bit comes when we decide to subclass our Rectangle class. Suppose we create a ColoredRectangle, or (and this is perhaps a bad example) ColoredCube. We would be adding new instance variables—at least a color ivar, and maybe a further depth measurement. Normally, the subclass would have to override all of those methods, and if the superclass added another initializer, that would mean that all subclasses would have to override that as well.

Fortunately, Cocoa has a solution—the designated initializer. Typically the one with the most arguments, all the parallel initializers call that one, and the subclasses all use the designated initializers in their calls to super. Choose an initializer to be the designated initializer, and follow through with it:

// Rectangle's initializers
-(id)initWithWidth:(float)w andHeight:(float)h {
    if(!(self = [super init]))
        return nil;
    self.width = w;
    self.height = h;
    return self;
}

-(id)initWithWidth:(float)w andArea:(float)a {
    if(!(self = [self initWithWidth:w andHeight:(a / w)]))
        return nil;
	return self;
}

// ColoredCube's initializers
-(id)initWithWidth:(float)w andHeight:(float)h {
    if(!(self = [super initWithWidth:w andHeight:h]))
        return nil;
    self.depth = 1.0;    // Default value
    self.color = [UIColor redColor];    // Ditto
    return self;
}

-(id)initWithWidth:(float)w height:(float)h depth:(float)d andColor:(UIColor *)c {
    if(!(self = [super initWithWidth:w andHeight:h]))
        return nil;
    self.depth = d;
    self.color = c;
    return self;
}

Note here that we only have to override the default initializer in the subclass, because all the other inherited initializers will then call on that overridden initializer. We can also add more initializers as necessary.

By creating a default initializer (note that there is not specific syntax to denote the designated initializer—it’s up to the programmer to determine which one is the designated and stick with it), it makes subclassing a lot easier.

Objects (Part 5): Introspection


  • Introspection is that ability to ask questions about a class and find out more of its properties. This is a fundamental concept of a group of concepts known as polymorphism, dynamic typing, and dynamic binding, which form a central tenet of object-oriented programming. Before we begin, there is another part of the language that we have to cover. 

    Selector (@selector())

    Remember how methods are also known as selectors (because a method is selected at runtime)? Turns out, you can select a method yourself, and pass it as an argument to a method. The syntax is simple:

    @selector(methodName)

    Remember that a method name includes its colons, such as setNumerator:overDenominator: (if appropriate).

    You can declare a selector as a variable to be used in multiple places with the following syntax:

    SEL action = @selector(doSomethingWithObject:)

    Note that a selector is of type SEL.

    Introspection Methods

    Here is a list of most of the introspection methods. A few deal with some more advanced topics, such as protocols; they will be introduced in context. Note that the classType argument refers to an object’s class, which is returned by invoking the class method, as in [Fraction class];.

    • -(BOOL)isKindOfClass:classType
      Is the object a member of that class or a subclass?
    • -(BOOL)isMemberOfClass:classType
      Is the object a member of that class?
    • -(BOOL)respondsToSelector:aSelector
      Does an object (class or instance) respond to the selector’s method?
    • +(BOOL)instancesRespondToSelector:aSelector
      Do instances of the class respond to the selector’s method?
    • +(BOOL)isSubclassOfClass:classType
      Is this class a subclass of the specified class?
    • -(id)performSelector:aSelector
      Ask the class to perform the method specified by the selector, returning whatever the method is intended to return.
    • -(id)performSelector:aSelector withObject:someObject
      Performs the selector, passing in someObject as the first (and hopefully only) argument, returning whatever the method is designed to return.
    • -(id)performSelector:aSelector withObject:someObject withObject:anotherObject
      Perform the selector, passing in the two arguments in that order to the (assumed) two parameters defined by the selector, returning whatever the method is designed to return.

    Working off our Fraction and MixedNumber classes, we can have the following routine:

    int main (int argc, const char * argv[]) {
    	NSAutoreleasePool * pool = [[NSAutoreleasePool alloc] init];	Fraction *frac = [[Fraction alloc] init];
    	MixedNumber *mn = [[MixedNumber alloc] init];
    
    	if ([frac isMemberOfClass:[Fraction class]])
    		NSLog(@"frac is member of Fraction class");
    	if ([mn isMemberOfClass:[Fraction class]])
    		NSLog(@"mn is member of Fraction class");
    	if ([mn isMemberOfClass:[NSObject class]])		// Remember that everything
    		NSLog(@"mn is member of NSObject class");	// inherits from NSObject
    
    	if ([frac isKindOfClass:[Fraction class]])
    		NSLog(@"frac is kind of Fraction class");
    	if ([mn isKindOfClass:[Fraction class]])
    		NSLog(@"mn is kind of Fraction class");
    	if ([frac isKindOfClass:[NSObject class]])
    		NSLog(@"frac is kind of NSObject class");
    
    	if ([frac respondsToSelector:@selector(add:)])
    		NSLog(@"frac responds to add:");
    	if ([mn respondsToSelector:@selector(add:)])
    		NSLog(@"mn responds to add:");
    	if ([Fraction respondsToSelector:@selector(alloc)])
    		NSLog(@"Fraction class responds to alloc");
    
    	if ([Fraction instancesRespondToSelector:@selector(subtract:)])
    		NSLog(@"Instances of Fraction respond to subtract:");
    
    	if ([MixedNumber isSubclassOfClass:[Fraction class]])
    		NSLog(@"MixedNumber is a subclass of Fraction");
    
    	[frac release];
    	[mn release];
        [pool drain];
        return 0;
    }

    The output is

    frac is member of Fraction class
    frac is kind of Fraction class
    mn is kind of Fraction class
    frac is kind of NSObject class
    frac responds to add:
    mn responds to add:
    Fraction class responds to alloc
    Instances of Fraction respond to subtract:
    MixedNumber is a subclass of Fraction

    Introspection really isn’t a difficult topic—the only potential source of error stems from confusing the functionality of isKindOfClass: and isMemberOfClass:. Somehow, just remember that isKindOfClass: involves descendants (subclasses), while isMemberOfClass: does not.

  • Extension 9: Pointers


    You’ve probably noticed that when we declare objects,

    Fraction *myFraction;

    We put an asterisk between the object type and the name of the object (it doesn’t matter if you place it after the type, like Fraction*, in the middle, like Fraction * myFraction, or as above). This is not a typo; we’re saying that we are creating a pointer to an object.

    Why Pointers?

    At the very basic level, objects are simply a collection of data (and methods). This data has to reside somewhere, and for the most part, this data is located in your computer’s RAM. But some objects can contain a lot of data, and start to take up a lot of RAM. This problem is further augmented if you have many objects. Of course, it’s always a good idea to minimize RAM usage, especially on a platform like iPhone where RAM can be scarce. Pointers, rather than storing an actual copy of an object, simply store the memory address of an object—it points to an object. Therefore, a pointer takes up a lot less RAM than an object itself would. This also makes code more efficient.

    You’ll probably notice that in our methods, we also pass pointers around rather than actual objects (as specified by the asterisk following any class type). This means, first of all, that we are only passing small blocks of data around, not a large amount of data that corresponds to an object itself. This also means that you’re not passing in a copy of an object—you’re passing in a copy of a pointer. This will have significant effects, as described below. But first,

    What are Pointers?

    Pointers point to objects in memory

    Pointers "point" to actual objects in memory

    A pointer is simply a value that stores an in-memory location. They are generally written in hexadecimal, and look something like 0x0ab23ef8. You don’t have to worry about what they look like, or how they work. They are generally treated as objects, but they aren’t. There only exists one copy of the actual object in memory (unless you explicitly make a copy), but multiple pointers can point to that same object.

    Multiple pointers can point to the same object

    Multiple pointers pointing to the same object

    Pitfalls

    The most important consideration here is memory management. Given the diagram above, consider what would happen if you wrote this line:

    anotherFrac = myFraction;

    This is a rather innocent line, and it does assign the value of myFraction to anotherFrac. But remember that myFraction contains a memory address, not an actual Fraction object. This means that now anotherFrac points to the same object as myFraction, and, more importantly, nothing points to the original anotherFrac:

    If you just assign pointers, you could leak memory

    Don't do this

    Now, you have no way to get to the original anotherFrac, and so that object will reside forever in memory (or until the application is quit or the system is turned off). THis object is considered to be leaked; if you leak too many objects, you’ll run out of memory, and on the iPhone the system will force your app to quit. Regardless of where you are, leaking is not a good thing.

    The other thing to consider is the case of multiple pointers to the same object, as you might have when you pass a pointer to an object as an argument to a method (as you usually do). Any changes that you make to one of the pointers will also be reflected in the other (because they point to the same object in memory, after all). This can have undesired consequences; you could also use this to your advantage. For example, if you wrote a class method that reduced a Fraction, you could have the method directly affect the original fraction you pass in; you could also make a copy of that fraction. On the other hand, you might mess up a data set from the user, forcing you prompt the user again for the same data…

    Pointers are a central tenet of Objective-C (and all of object-oriented programming). All objects are represented as pointers—the original object is implicitly created. Used properly, they are a great tool.

    Extension 8: Methods (in Detail)


    The majority of the following text is derived from Apple’s authoritative guide on the Objective-C language—The Objective-C Programming Language: Chapter 1: Objects, Classes, and Messaging.

    Message Syntax

    You send a message to an object to have it do something. As the caller, you don’t really care how the object does the work (if you wrote the class in the first place, you assure yourself that the method will work, and you don’t worry about it when you call the method later). To call a method, you enclose the call in square brackets, and use the following format:

    [receiver message];

    The receiver must be an object; the method is an instruction, possibly along with arguments. At compile time, a method is simple a name of a set of instructions. The compiler simply checks that the method exists. Only at runtime, the correct method is selected from the receiver object’s repertoire, and follows the instructs. The method name “selects” a method implementation (this will be furthered explored with the concepts of polymorphism and dynamic binding. Methods are sometimes referred to as selectors; in fact, there are entities that represent a method variable (that is, a data type that can be assigned a method), which you can pass to certain methods. The Objective-C language defers as much as possible to runtime rather than compile time; this will lead to some subtle matters and issues that will be further explored in later lessons.

    If you have any experience in procedural languages (such as the venerable C), methods are similar to functions. However, a function is a global sequence of steps that can be called anywhere to do something. A function is also an entity that is defined at compile time—it’s like a machine that (potentially) takes an input and returns an output, or at least does some (useful) work. In contrast, a method is bound to a class; it’s like asking a co-worker to do something—every object has a list of methods it can perform, and no other. In short, functions are global machines; methods are bound to a class, and constitute a portion of the class.

    Arguments

    Arguments to a method are parameters or values passed to a method so that the method gets access to those values without breaking encapsulation and having a massive collection of global values.

    A method with one argument includes a color at the end of the method name and the argument immediately follows the colon (yes, you can put a space there, but nothing else). For example, the following method is an example of argument usage:

    [myFraction setNumerator:2];

    The argument can be a constant, as in the previous example, or a variable of the corresponding type, as declared in the method declaration. The declaration for the setNumerator: method would be

    - (void)setNumerator:(int)value;

    In general, methods with arguments are declared like this:

    A method declaration has a type, return type, name, and arguments

    Method Declaration Syntax

    Notice how the arguments are interspersed within the method name. This is very different from other languages, where the arguments are in a comma-separated list after the method name. One of Objective-C’s design goals is to make the code read like English; this is effectively accomplished by directly relating the method arguments to the portion of the method. For longer methods where you may have half a dozen arguments, this can be extremely helpful.

    When calling such methods, the syntax is the same as above; simply follow each colon with a variable or constant for the argument.

    The name of such methods include the colons. Therefore, the selector name for the method above would be setNumerator:overDenominator:.

    These parts of the method are not optional, nor can their order be varied. The final portion of the method must also take an argument:

    http://stackoverflow.com/questions/4479967/why-must-the-last-part-of-an-objective-c-method-name-take-an-argument-when-there

    Note, too, that you can nest message calls; these work like parentheses in math—the innermost method is evaluated, and then its return value (if the inner method(s) don’t return a value, you really can’t nest them) is used as the receiver or argument to another message. For example,

    [[myDatabase generateNewDatabase] addObject:[NSString stringWithFormat:@"Hello, %@", myPerson.name]]];

    Is a perfectly valid statement. Try to trace through the method calls (remember that the use of the dot syntax is also a method call). Xcode will automatically keep track of those brackets for you; as you close one, it’ll highlight the corresponding one, so you’ll know where you are. The compiler will warn you too.

    One final point about arguments: methods can take more than one argument, but it’s rare. These methods are rather difficult to implement (you don’t want to skip any arguments, but you don’t want to read more arguments than you’re provided with—you’d be reading in garbage values), and are beyond the scope of where I’d like to venture. However, Matt Gallagher at his blog Cocoa with Love details using methods with multiple arguments:

    http://cocoawithlove.com/2009/05/variable-argument-lists-in-cocoa.html

    Messages to nil

    It is perfectly valid to send a message to nil in Objective-C.

    To clarify, nil is a defined value, not just garbage. In a system’s memory, you never know what’s in a specific block of memory until you actually assign it anything. This is known as “garbage” memory. Nil (which is simply a redefinition of the classic NULL) is (typically) defined as zero. The statement

    NSString *s;

    Simply says that there will be an object of type NSString called “s”; it does not imply any value. If you tried to access “s” at the moment, barring any compiler issues or warnings, you will get garbage values. Run it a couple of times, and you will get different values back. That’s because the system is reading a block of memory that still contains data from whatever used that block before. However, if you had said

    NSString *s = nil;

    , “s” still wouldn’t have a meaningful value, but it is no longer garbage.

    In many cases, especially when you are loading a (potentially) large block of data in memory, you want to check if the object has already been loaded. If not, load it; otherwise, just use it. To do this, you could use the following code:

    if (myBigDictionary == nil)
         myBigDictionary = [[NSDictionary alloc] init];

    You could then call a method on myBigDictionary. However, if you had failed to do the above check, and simply called a method on myBigDictionary, you would not crash (assuming that myBigDictionary had actually been set to nil). Most methods that read data will return nil if there is no data, rather than a garbage value.

    A few rules apply here:

    • If the method returns an object, then a message to nil returns nil:
      Person *motherInLaw = [[myself spouse] mother];

      If myself‘s spouse is nil, motherInLaw would also be nil.

    • If the method returns any type of int, float, double, long double, or long long, a message sent to nil returns 0.
    • If the method returns a struct (an advanced data type which we will touch upon later), all the fields will be set to 0.0.
    • If the method returns anything else, the return value is undefined. In that case, do a check for whatever valid values you would be expecting.

    Objective-C methods are not complicated to understand. They soon become an indispensable portion of any program.

    As an interesting read, Matt Gallagher has a great article on method names in Objective-C:

    http://cocoawithlove.com/2009/06/method-names-in-objective-c.html

    Objective-C Lesson 3: Object-Oriented Programming


    When object-oriented programming arrived in the programming world, it was considered a savior of software by some and yet another foolish experiment by others. Regardless of the perspective, we need to define some basic concepts and terminology before we begin

    A Thing

    An object is a thing. In the real world, you have “things,” all of which have certain properties. For example, you probably have a car—a “thing.” The car has properties, such as paint color, total mileage, make, model, and year. You can also do things with the car, or have something done to it—for example, you can drive twenty miles in the car, or wash the car.

    In an object-oriented program, your specific car is an instance of a class, generally called Car. A class is the definition of the object, and in most cases can be thought of as a “factory” for its instances. A class can create many instances of the same type of object, although they have to have different names. An object is simply a variable, but it can be (and usually is) of a custom type.

    Instance Variables and Methods

    As mentioned before, your car, and any other car, has certain properties that are potentially unique to that specific instance. These properties are known as instance variables, or ivars for short. Instance variables are variables, of either the standard data types, or of any other object, but they are private to the object—that is, every single instance of Car has its own identical set of instance variables; although the variables may be of the same type and number, they can (and usually do) contain different values across different instances.

    A method is the proper term for a function, or simply a piece of code that is called upon to perform some action. In Objective-C, there are two types of methods—class methods and instance methods. Class methods are performed directly on the class that an object belongs to—for example, a class methods might ask the car factory how many cars it produced the previous year. Instance methods are performed on specific instances of a class—for example, you might ask one specific car to drive twenty miles, or you might wash one specific car; you wouldn’t do those things to the entire factory. Instance methods affect the state or condition of the object. Just like two identical cars adopt different characteristics over their lifetime, different instances of Car can take on different traits as well.

    Message Objects

    In Objective-C, there is a specific syntax for calling methods:

    [ClassOrInstance method];

    The method call begins with an open bracket. This is followed by the name of the class or instance, depending on whether the method belongs to a class or instance. This is followed by a space, and then the name of the method. This is followed by a closing bracket, and the all-important semicolon.
    In more formal terminology, the following message call might be described as

    [receiver message];

    The meaning is the same.

    Sample Program

    Now, with the theory cleared up, we can move on to our first program involving classes.

    Program 3.1

    // A simple implementation of a Fraction
    #import <Foundation/Foundation.h>
    
    int main (int argc, const char * argv[]) {
    	NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
    
    	int numerator = 2;
    	int denominator = 5;
    	NSLog(@"This is a fraction with a value of %d/%d", numerator, denominator);
    
    	[pool drain];
    	return 0;
    }
    

    The output is

    This is a fraction with a value of 2/5

    In Program 3.1, we defined a numerator and denominator variable, and set them to integer values. We then printed the result of this simple fraction.

    This program is short and condensed, and seems to be pretty simple—and it is. But what if we had a hundred fractions that we wanted to store? We’d have to keep track of two hundred variables. And performing operations on those variables would not be any easier.

    As a matter of fact, Program 3.1 is not a true object-oriented program. It is still written using C-style code. We have not created any objects in the code; our Fraction object still exists only psychologically.

    Program 3.2 presents the exact same functionality, except that we actually define this object in code. We then refer to the object as Fraction throughout the program. Don’t worry if it looks daunting; an explanation follows.

    Program 3.2

    // A Fraction class
    #import <Foundation/Foundation.h>
    
    //———————— Interface ————————
    
    @interface Fraction : NSObject
    {
    	int numerator;
    	int denominator;
    }
    
    - (void)showResults;
    - (void)setNumerator:(int)n;
    - (void)setDenominator:(int)d;
    
    @end
    
    //———————— Implementation ————————
    
    @implementation Fraction
    
    - (void)showResults {
    	NSLog(@"This is a fraction with a value of %d/%d", numerator, denominator);
    }
    
    - (void)setNumerator:(int)n {
    	numerator = n;
    }
    
    - (void)setDenominator:(int)d {
    	denominator = d;
    }
    
    @end
    
    //———————— Main Program ————————
    
    int main (int argc, const char * argv[]) {
        NSAutoreleasePool * pool = [[NSAutoreleasePool alloc] init];
    
        // Create an instance of Fraction
    	Fraction *myFraction;
    
    	myFraction = [[Fraction alloc] init];
    
    	// Set myFraction to 2/5
    	[myFraction setNumerator:2];
    	[myFraction setDenominator:5];
    
    	// Display the value of myFraction
    	[myFraction showResults];
    
    	// Clear up memory
    	[myFraction release];
    
        [pool drain];
        return 0;
    }
    

    The output is

    This is a fraction with a value of 2/5

    Code, Demystified

    Program 3.2 is divided into three logical sections. These are known as the interface, implementation, and program section, respectively.

    The interface section (delineated by @interface in the code) provides basic program-wide definitions, the implementation section (delineated by @implementation) provides the implementation of the methods, and the program section contains the main code that does something.

    The @interface Section

    Every class in Objective-C has a single parent, or superclass, from which the class inherits. Next, you have to declare the instance variables that belong to the class. Finally, you declare the methods, or actions, that the class can perform. This is all done in the @interface section, which looks like this:

    @interface ClassName : ParentClassName
    {
    	instanceVariableDeclarations;
    }
    methodDeclarations
    @end
    

    Remember that the compiler ignores spaces, so you may put line breaks and spaces anywhere within the code above.
    By convention, the name of a class begins with a capital letter, as in the Fraction class.

    You can tell that our Fraction class is a subclass of NSObject, which is the root object that almost everything else inherits from, directly or indirectly. NSObject is declared in the Foundation files which you imported.

    Fraction contains two instance variables; both are ints. They are called instance variables because every instance of Fraction creates a new set of these variables, unique to that specific instance. So, FractionA’s numerator is a different variable than FractionB’s numerator. Objective-C automatically keeps track of this for you.

    Currently, our Fraction class declares (and later implements) three methods. All are instance methods, as noted by the minus sign () preceding each method name. A class method is delineated by a plus sign (+).

    Before we discuss the method declaration, we must remember than an object is considered a “black box.” Ideally, objects encapsulate, or hide, their instance variables, from anything except itself. The object exists as a single entity, and anything that works with the object does not need to know, and should not know, about the object’s inner workings. Therefore, we need to declare specific methods to set the variables, because the program should not directly manipulate them. We also need a separate method to print the values, because, once again, the program should not be able to directly access the variables. This is a fundamental concept in Objective-C, and is observed throughout the libraries and code.

    The Fraction class defines three methods, all of which return nothing. The value in parentheses before the method name specifies the return type of the method. void is programming parlance for “nothing,” or nil. Any data type or object can be a return value; a method might return a float, int, or another Fraction, as illustrated by the following methods:

    - (int)numerator;
    - (float)divide;
    - (Fraction *)randomFraction;
    

    The asterisk after the Fraction is used to delineate a pointer; this is not an important concept at the moment. For now, simply remember that anytime you create an instance of a custom object, the name of the instance should be prefixed with an asterisk.

    All of these methods must return a value somewhere in the method. Just as main returns 0 at the end, these methods must return an int value, a float value, and another instance of Fraction, respectively. The compiler will complain if you do not return a value.

    The latter two methods declared in Fraction return no value, but they do each take an integer value as an argument. In the case of setNumerator:, the name of the argument is n. This name is arbitrary, as long as it follows the standard naming conventions, and is used only within the method to refer to the argument. The argument itself exists only within the method; you cannot access n anywhere else in Fraction.

    Arguments are values that are passed into a method, which the method would not be able to obtain otherwise. For example, setNumerator: requires a value to set to the numerator, but it would not receive a custom value without an argument.

    Note the syntax of these method declarations. The method name begins with the minus or plus, signifying the method type. This is followed by the return type in parentheses, the name of the method, and a colon. This colon is followed by an argument type in parentheses, a name for the argument, and a semicolon. In a method that takes an argument, the colon is considered part of the method name.

    A method can take multiple arguments; they are delineated by a space, a new section of the method name, another colon, an argument type, and name. This will be covered in a later chapter.

    The @implementation Section

    In the implementation section, you define the code that each method implements. The general appearance of the @implementation section is similar to this:

    @implementation ClassName
    	methodDefinitions;
    @end
    

    In the implementation of Fraction, you also declare the method types and names, followed by an open brace. Between the opening and closing brace, you insert the code that the method will execute when it is called.

    The program Section

    In this program, the main program section contains the all-important main function that is the first thing to be executed.

    In Program 3.2, we first create an instance of Fraction:

     Fraction *myFraction;

    Again, don’t worry about the asterisk. From a technical perspective, it says that myFraction is a pointer to a Fraction.

    Now that you’ve created an object “container” to store a Fraction, you have to actually create a Fraction to fill that space. You do that in the next line,

    myFraction = [[Fraction alloc] init];

    In this line, you call the alloc method on Fraction; alloc is a class method. Although you never defined the alloc method, it is inherited from NSObject. Inheritance will be discussed in a later lesson.

    After you allocate a Fraction, you have to actually initialize it. Therefore, you pass the value of the alloc method to NSObject’s init method, and the resulting Fraction is assigned to myFraction. This nested sequence of messages is used almost every time you create a new object.

    Next, you set the values of myFraction to 2 and 5 by using the proper method calls.

    [myFraction setNumerator:2];
    [myFraction setDenominator:5];
    

    You pass the integers 2 and 5 as arguments to the method. The method then takes these arguments and sets the numerator and denominator variables of myFraction.

    You then call myFraction’s showResults method to print the value of the fraction.

    We then free up the memory that Fraction uses by calling the release method:

    [myFraction release];
    

    This is a critical part of programming for iOS. Whenever you create a new object through alloc/init, you have to release it—in other words, every alloc/init call is balanced by a release call. Otherwise, the now-useless object will still sit in memory, taking up space; in a memory-constrained environment such as the iPhone, you want to use as little memory as you can.

    Messaging Receivers

    In every message, you send the message to a specific receiver—the object or class which implements the method. However, instance methods are sent to specific instances, so that the same method call to two different Fractions will result in two Fractions with potentially different values. For example,

    [fraction1 setNumerator:2];
    [fraction1 setDenominator:5];
    
    [fraction2 setNumerator:3];
    [fraction2 setDenominator:4];
    

    Will result in two separate fractions, one with a value of 2/5 and another with a value of 3/4. Although you are calling the same methods on both, they are received by different objects, with their own set of numerator and denominator variables.

    Data Encapsulation

    As mentioned before, you shouldn’t directly access the instance variables of an object. Data encapsulation provides some security against people working with the class, to prevent them from tinkering with the inner workings. So in the main function, how would you access these values?

    Generally, you access these values by writing special methods, known as getter methods (the set methods that we’ve already wrote for Fraction are known as setter methods; they are generally written in pairs).

    The getter methods for the Fraction class look something like this:

    - (int)numerator;
    - (int)denominator;
    
    - (int)numerator {
    	return numerator;
    }
    - (int)denominator {
    	return denominator;
    }
    

    It doesn’t matter if the method name is the same as the variable name; in fact, it is common practice.

    Program 3.3 contains the main function that tests these new methods.

    Program 3.3

    int main (int argc, const char * argv[]) {
        NSAutoreleasePool * pool = [[NSAutoreleasePool alloc] init];
    
        // Create an instance of Fraction
    	Fraction *myFraction;
    
    	myFraction = [[Fraction alloc] init];
    
    	// Set myFraction to 2/5
    	[myFraction setNumerator:2];
    	[myFraction setDenominator:5];
    
    	// Display the value of myFraction
    	NSLog (@"The value of myFraction is %d/%d", [myFraction numerator], [myFraction denominator]);
    
    	// Clear up memory
    	[myFraction release];    [pool drain];
        return 0;
    }
    

    The output is

    The value of myFraction is 2/5
    

    Objective-C 2.0 introduces properties, which provide an easy way to create getter and setter methods. This will also be covered in a later lesson.

    • Welcome

      My goal is to make CupsOfCocoa into a beautiful source for beginners to the iPhone platform to get started. Subscribe below for more, and stay tuned!

    • Contact Me

      If you need to contact me for any reason, feel free to send me an email.
    • The Giving Spirit

      If you've found this site helpful, would you consider donating a little sum? Any amount is appreciated...Thanks so much!

    • Roadmap

    • Enter your email address to follow this blog and receive notifications of new posts by email.

      Join 220 other followers

    • Back to the Past

      May 2020
      S M T W T F S
       12
      3456789
      10111213141516
      17181920212223
      24252627282930
      31  
    • Time Machine

    • You count!

      • 622,650 views
    • Worldwide Stats

      free counters
    %d bloggers like this: