Design Patterns: Key-Value Observing


Key-Value Observing (KVO) is heavily used in OS X development in bindings and form a significant portion of UI design. However, with iOS, it takes on somewhat less significance and is mostly used as necessary to simplify program design. KVO “broadcasts” a notification whenever a property is modified, and any class can receive those notifications and handle changes as necessary. This helps to decouple your classes from each other, allowing more flexible and re-usable code, especially as your code grows large and you have many sets of MVC objects.

KVO Layout

KVO Structure

It is primarily used when you have one model object backing multiple controllers, in which case you can use KVO to update the model when any controller changes its data.

Preparing for Key-Value Observing

KVO is set up for you when you use properties. Whenever you use dot-notation or the setter methods to change a property, the corresponding KVO method is called and all receivers will be notified. If you do not use properties or write your own setters, you may want to manually call change notification methods; see the section below to do so.

Key-Value Observing Compliance

To be KVO-compliant, your class must first be KVC compliant. This means that you should have proper, working implementations for valueForKey: and setValue:forKey:. In addition, you should emit the proper notifications when changes are made.

As stated above, if you use standard setters, the notifications are called for you, and you don’t have to worry about them.

Manually Call Change Notifications

You may need to manually call notifications if you don’t use standard setters, if you want to minimize notifications for certain situations, or to package multiple changes into a single notification.

First, you have to override the implementation of automaticallyNotifiesObserversForKey:, which is a class method declared in the NSKeyValueObserving protocol. Given a key, if you want manual notification, return NO; otherwise, return YES. If the key is not recognized, call the super implementation. The default implementation in NSObject simply returns YES.

+ (BOOL)automaticallyNotifiesObserversForKey:(NSString *)theKey { if ([theKey isEqualToString:@"openingBalance"]) return NO; else return [super automaticallyNotifiesObserversForKey:theKey]; }

Then, to call the actual notifications, you call willChangeValueForKey: before changing the value, and didChangeValueForKey: after the change.

- (void)setBalance:(double)newBalance { // Should not compare double directly, // But means the same as if (balance == newBalance) if (fabs(balance - newBalance) < 0.0001) return; [self willChangeValueForKey:@"balance"]; balance = newBalance; [self didChangeValueForKey:@"balance"]; }

Note that to minimize redundant notifications, you can check to see if the value has actually changed.

Finally, you can call willChangeValueForKey: and didChangeValueForKey: multiple times with different keys to send multiple notifications if multiple values get changed.

Registering for Key-Value Observing

To register a class for Key-Value Observing, the observed class must be KVO-compliant for the properties that you want to observe (obviously), you must register as an observer, and implement the observing method.

Note that not all classes are KVO-compliant for all properties. Ensure compatibility as necessary in your classes, but note that properties in Apple’s code are only KVO-compliant of the documentation says so.

Register an Observer

The observed object must be made aware of the observer by sending the addObserver:forKeyPath:options:context: method. In your view controller, you might have the following method, which registers a model object as an observer for the balance property:

- (void)registerObserver { [modelObject addObserver:self forKeyPath:@"balance" options:(NSKeyValueObservingOptionNew | NSKeyValueObservingOptionOld) context:NULL]; }

The options argument takes one or more of the NSKeyValueObservingOptions values. You will most often use the constants in the example above, which tells the system to return both the original and new values. You specify both using the bitwise OR operator (|), the vertical bar.

The context is a pointer to anything you wish, and is provided as-is to the observer. You can use it as an identifier to determine the change, or to provide any other data. It is your responsibility to retain and/or release it as necessary; it is not retained by the system. The context can be used to identify your notifications. A problem may arise because you can register the same keyPath on multiple objects. You can therefore use a distinguishing context to determine the calling class. See this link for more information on how to solve this issue.

Receiving Notifications

All observers must implement the observeValueForKeyPath:ofObject:change:context: method. The observer is provided the original object and key path that triggered the change, a dictionary containing the details of the actual change, and the user-specified context described above.

The dictionary has a value accessed through the NSKeyValueChangeKindKey, which is a standard NSDictionary change key. It provides information about the change, returning an NSKeyValueChangeSetting. The exact values are defined in the link. You can also access the old or new values (depending on whether neither, one, or both were requested) through the NSKeyValueChangeOldKey and NSKeyValueChangeNewKey keys. If the property is an object, it is returned directly; scalars or C structs are wrapped as an NSValue object.

- (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary *)change context:(void *)context { if ([keyPath isEqual:@"balance"]) { // Handle change } else // Unrecognized keypath [super observeValueForKeyPath:keyPath]; }

Removing an Observer

To clean up after yourself, you should remove yourself as an observer by calling the removeObserver:forKeyPath: method.

- (void)unregisterForKVO { [modelObject removeObserver:self forKeyPath:@"balance"]; }

If you received a context from the notifications, you should only release it after removing the observer.

Design Patterns: Model-View-Controller


As programs get larger, there is a greater need for a paradigm to keep everything in order. At the simplest level, any program is a means to store data and present it in a meaningful way. With only a few views and a good data store, this might be rather easy in a simple app for iPhone. But with Pages-caliber apps, especially on the iPad, a formal solution is needed. Cocoa adopts the Model-View-Controller paradigm (MVC) to better organize projects.

What is MVC?

MVC Overview

MVC Overview

Under the MVC system, your classes are one of three types—a model, a view, or a controller. Models store data in an organized form. For a simple app, the model could just be the actual data store, either in-memory (maybe as an NSArray or NSDictionary), or to-and-from disk. In a more complex app, you may choose to use a SQLite database or Core Data, and your model would be a simple instance or one piece of data. More importantly to MVC though, your model must not describe how to store the data—it must not contain any information about padding in the UI, for example, or the position (frame) of any item. Models are usually specific to your application, so they usually are not reusable, unless you have a “template” method to store data.

Views are what you actually see on-screen. They have functions to draw to screen or to other contexts. The view should not store the data it is displaying though—a label should not keep its text around; a table view should not store a copy of its data. Instead, along with MVC there are the patterns of delegation and the language features of KVC and KVO. Many of the classes from UIKit, including UILabel, UITableView, UITextView, and indeed UIView are views. Obviously, views, because they just display (any) data, can be very easily reused. Conversely, views of should not be bound to specific data. Some views may be suited to certain types of data—table views are more suited to text; the iPhone’s home screen is more suited for icons and short text—but you should not impose other restrictions, such as a maximum text length or that the text should only be of one case (that’s so 1980s…).

The view and the model should never interact. This preserves reusability. If UITableView had an outlet to your MainDataModel, it could it only be used with MainDataModel. If you had a model class called Person, it wouldn’t work with UITableView (and the Address Book app would never exist). The controller serves as a median. As shown in the image above, the controller controls interaction. If the model changes, the controller is notified and the view is changed accordingly (for example, you could call [tableView reloadData]). If user interaction changes the model (for example, deleting a row from a table view), the controller is also notified, and from there the change gets propagated to the data store. Therefore, the view can just tell the controller that some data at this location got deleted; the view does not have to worry about what to do or how to handle the deletion so it actually gets deleted. This goes back to the concept of abstraction, one of the fundamentals of object-oriented programming. Similarly, the model does not have to be concerned with exactly how the data got deleted, and just delete the data. In this way, the classes are kept clean and perform one function—the point of a class in the first place. Because the controller is the median and has to deal with specific views and data models, it is typically the least reusable. UIViewController implements so little functionality (from a functionality perspective—it actually does a lot behind the scenes) because controllers are very specific to each application. Most of your app logic goes into the controller.

Another compelling reason to use a controller is to make decisions. Obvious, but there are situations where changes to the model or view should not or need not propagate to the other. Without a controller, a change in the data at row 586 would affect the view even if the view was only showing rows 5–10, an unnecessary operation (which may even cause the UI to slow down for a moment). Other times, data should not be deleted, or deleted at a later time—if a file is being written to from another thread, a delete command from the UI should not be executed immediately. The write should be stopped or allowed to finish before the delete occurs.

Mediation

Hand-in-hand with MVC comes the concept of delegation (and data sources). Data sources are obvious—protocols such as UITableViewDataSource make it so a table view can get data from an id type, making the table view very reusable. The controller implements the delegate, and asks its data model for data as necessary (or as the data source methods are called). More interestingly though, the controller can return data that does not correspond to the data model; in fact, the controller could calculate values and not have a data model at all. The table view does not have to know about this though, and does not need to do any extra handling in these cases.

The data source protocol “pushes” data to the view; the delegate goes in the other direction and informs the controller of changes to the view. There is a naming convention involved, which will be the topic of a future post. Again, the controller can notify the model, or not, depending on the situation.

KVC and KVO are not really used in simple applications. However, with multiple controllers that need to interact with each other, KVC and KVO can be used to great benefit. KVO, which stands for Key-Value Observing, registers a class for notifications when a key-value is changed in any other class. This is a more advanced topic, and will form the basis of another post. It is an effective way to allow multiple controllers to communicate without resorting to a tangle of protocols.

Resources

WWDC 2010 had a great lecture on MVC and the “10 Best MVC Tips Ever.” It is highly recommended. Note that you will need to be a registered developer, including free developers.
Video Link | iTunes Link | Slides (9.7MB)

Extension 14: Structs and Objects


Objective-C is an extension to regular C. So of course, at some point there is a place where all the new stuff in Objective-C ties into regular C. This Extension covers this happy meeting place. Some of the concepts may be a bit foreign, but they will be further explained in later Lessons.

Objects are Represented as Structs in Memory

You may have noticed that accessing a member of a struct uses dot notation, the same way you could access instance variables in your objects. This is because the ivars are actually stored inside a struct, which holds your instance variables plus any inherited ones. The alloc method reserves enough memory space to hold one of these structs.

Speaking of inherited instance variables, every class inherits a variable called isa (is-a) which identifies the object’s class—that is how the runtime system figures out the identity of the object, even if it’s assigned to a generic id type.

id is a Generic Pointer Type

The id type can be used for any object type. The id type is a generic pointer, which means that it is simply a memory address. The system at compile time does not know about the type of the object (that is what the isa variable is for), but simply that there is a location of memory involved. Because of this, and the fact that all objects reside in memory, the id pointer can be reassigned to different object type variables as needed.

An Object is Always Used as a Pointer

The asterisk associated with every object type is a pointer—a variable that actually holds as its value a memory address. A declaration such as

Fraction *myFract;

means that you’re defining a pointer, which will point to something in memory of type Fraction.

The reason everything is passed as pointers is one of performance. A memory address is a much smaller amount of storage than the actual object. On a 32-bit system, a memory address takes up 32 bits—usually the same size as an int. On a 64-bit system, an address would take up 64 bits. While it may seem a bit redundant at first to store memory addresses in memory, it starts to make more sense when you realize that you could have an object that holds several kilobytes of data in one object—it’s a lot faster an easier to copy 64 bits than several thousand of them.

The alloc method returns an object pointer of type id. You can then assign this pointer value to any other pointer value—in this case, perhaps a type Fraction.

When you assign one pointer to another, as in

fract1 = fract2;

You’re still assigning the value of fract2 to fract1—but remember that the value of fract2 is a memory address. So you’re making fract1 refer to the same location in memory as fract2. Therefore, anything you do to fract2 will be reflected in fract1—because of course they now refer to the same actual object in memory.

The Dual-Nature of Objects

An Objective-C object consists of instance variables and methods. The ivars are stored as a struct, and referred to using more efficient pointers. Methods are not stored in memory. Instead, methods (which are conceptually different from functions—the former is specific to a class, while the latter is global) are placed in a pool of methods, associated with the class name, and “selected” as needed.

Objective-C Lesson 11: Object Initialization


By this point we’ve covered the basics of the Objective-C language itself. With this knowledge and a willingness to learn, everything else can be picked up from Apple’s developer portal and the documentation. However, there are still topics which are used consistently through the language and are certainly worth talking about. This week’s topic is the creation of objects in your code—proper practices and syntax.

Allocation

Creating a new object in Objective-C is usually a two-step process. First, memory has to be allocated for the object, then the object is initialized with proper values.

The first step is accomplished through the alloc class method, inherited from NSObject and rarely, if ever, overridden. This method literally allocates a chunk of memory sufficient to hold your object (all of its instance variables). It then sets all of these values to zero or nil—this is so that you won’t get garbage values left behind by whatever had last used that chunk of memory. This means that all objects start out as nil, BOOLs start as NO, ints as 0 and floats are set to 0.0. These values are guaranteed after the alloc method is called.

Fraction *frac = [Fraction alloc];
// numerator and denominator are both 0

However, that object is not quite ready to use yet—it has to be initialized first (otherwise strange bugs are mostly guaranteed). This is accomplished by the init instance method, which is often overridden. These methods return an object of type id—the same object that is being initialized.

Fraction *frac = [[Fraction alloc] init];

It is important that you nest the alloc and init methods—there are instances where the initialization method may not return the same object as was allocated. This is true when working with classes such as NSString, which are in fact a public interface for a variety of classes. In these cases, the initialization method may choose to return an object that is not quite the originally requested class (for example, NSString‘s initialization methods might return a subclass of NSCFString.)

An initialization method for our Fraction class might look like this:

- (id)init {
	if (!(self = [super init]))	// Location 1
		return nil;		// Location 2
	// Initialize fraction to 3/5	// Location 3
	numerator = 3;
	denominator = 5;
}

Let’s look at this code. At Location 1, we have…well, a source of controversy, actually. In general, the recommended practice is to do it as shown, for a detailed breakdown of the issue, check out this link. Basically, this line states that if there is an issue with super’s init method and it returns nil, then your init method should also stop and return nil. Note that when a method hits a return statement, the method ends, even if there’s code left. This is why an else… block is not necessary. Why might [super init] not work? One case is if the object was being initialized with data from the internet. If there isn’t an internet connection, or if the internet resource can’t be found, [super init] might return nil, rather than some made-up or garbage data. At Location 2, we return nil, if necessary.

The method then assigns initial values to the instance variables at Location 3. In general, they should be set to reasonable, “empty” values if possible. Many programmers would (redundantly) set these values to zero, just to make their intentions clear. Here is a design decision you have to make. Assume you have a large class that has a lot of instance variables—a Car, for example. In the init method, you can choose to set default values for everything, if the intended use is to create a basic Car and use it. However, if the user is expected to extensively customize the Car, you might want to simply leave containers for the values, and not set default values.

Convenience Initializer

Most classes also have convenience initializers, which take arguments with which to set the ivars.

-(id)initWithNumerator:(NSInteger)num overDenominator:(NSInteger)denom {
	if (!(self = [super init]))
		return nil;
	numerator = num;
	denominator = denom;
}

The code is quite self-explanatory. The method assigns the values in the arguments to the class’s instance variables.

In the case of multiple initializers, it is advised that one of them be the designated initializer—generally it is the most complex initializer. This topic will be covered in next week’s Extension.

Constructors

Languages such as Java have a concept of constructors, which are language-level constructs that are automatically invoked through the new keyword. They combine the process of allocation and initialization, although most of the custom code is for the latter. They also have syntax restrictions—the method must not return anything (that’- how it becomes a constructor), it must have the same name as the class, any call to super must be the first statement… Objective-C has none of these restrictions. As stated above, initializers are not part of the language itself—there is no keyword to invoke them. As such, they are simply regular methods, which usually end up calling NSObject’s init method. In Java, calling the superclass’s implementation must be the first line, to ensure that you’re not dealing with garbage data. There is no such restriction in Objective-C—but any issues with garbage data are the responsibility of the programmer, not the compiler. This is one of C’s design goals—give responsibility to the programmer. But with great power comes great responsibility!

Objects (Part 6): Dynamic Typing


So, following up from last week’s post on introspection, an obvious question is why: why would anyone need or want to do that? The answer lies in a power OOP concept known as Dynamic Typing.

The id type

In developing any large program, or when using Apple’s provided code, you will have to confront a situation in which you don’t know the type of an object (typically an argument, although sometimes an ivar). How might this come to be?

Imagine that you had a class that processed some external data. You don’t know or don’t care where the data comes from, but you should be prepared to handle many different types. Your data might come from a text file, where the contents might be read and passed into your method as an NSString. You might have to process the data in your own program somewhere else, and the data then would be as an NSArray or NSSet. Alternatively, the data could be coming from the internet as a JSON response, which must be parsed into an NSDictionary (don’t worry if you don’t know what JSON is…there’ll be something about this later down the road). Or just maybe the data is coming in as a bag of bits, stored as an NSData. How would you handle all this?

To begin, we need to introduce a new data type: the id type:

id someObject;

The id type is designed to be a generic type which can hold any object type (in other words, id does not work with primitive types, such as ints and BOOLs). Note that you do not need the asterisk (*) after id—the asterisk is implied. The id type is typically typed (cast) to a specific type after using the introspection methods. To implement our previous hypothetical example:

- (void)processData:(id)someData {
	if ([someData isKindOfClass:[NSString class]])
		NSLog(@"input data is %@", someData);
	else if ([someData isKindOfClass:[NSArray class]]) {
		// Cast someData into an NSArray
		NSArray *dataArray = (NSArray *)someData;
		NSLog(@"First object in dataArray is %@", [dataArray objectAtIndex:0]);
	}
	else if ([someData isKindOfClass:[NSDictionary class]]) {
		// Cast someData into an NSDictionary
		NSDictionary *dataDict = (NSDictionary *)someData;
		NSLog(@"Keys in dataDict are %@", [dataDict allKeys]);
	}
	else if ([someData isKindOfClass:[NSData class]])
		NSLog(@"someData is a bag of bits.");
	else
		NSLog(@"someData is an unsupported type:\n%@", someData);
}

Type Checking and Polymorphism

Given the following method:

- (void)showObject:(id)anObject {
	[anObject display];
}

What would happen if anObject is a Fraction? What would happen if it was a MixedNumber? More importantly, what would happen if it was either (and you didn’t know which)? How would the system know which version of the method to call?

This is where polymorphism comes in.

The first issue is whether anObject even responds to the object. You might remember the respondsToSelector: method, which would hopefully solve this minor obstacle. If the type had not been id but instead been something like NSArray, the compiler would gladly tell you something along the lines of

warning: 'NSArray' does not respond to 'display'

But because the compiler doesn’t know that—it is an id type—there is no compile time checking. Therefore, the introspection method is a must, to avoid a runtime crash (although the build will not display any sort of warning or error about this).

But back to the larger issue at hand—the point of the id type is to allow you to not know what type the value is; that is eventually determined at runtime. But in fact, the issue is resolved a runtime without any more programmer or user intervention. Every object keeps track of what class it is through the isa variable that is inherited from NSObject. The runtime system will be able to determine which class the object is of, and call the appropriate method then. Keep in mind that it is part of Objective-C’s philosophy to defer as many decisions as possible from compile time to runtime, to allow a more fluid and dynamic coding style.

Dynamic versus Static Typing

Why wouldn’t you want to use the id type for all objects?

When you declare a variable to be of a particular class, such as in

NSArray *array = myArray;

You are using static typing, where “static” indicates that the type of the variable won’t (or shouldn’t—the compiler will warn you) change.

So what are the benefits?

  • The compiler will ensure that the variable is used consistently—that is, you’re only assigning proper objects of the same type to the variable, to avoid issues (with the object not respond to methods and such) later on. The compiler can check to see that an object actually implements a method, rather than having the program crash at runtime because a method implementation was not actually found.
  • It makes the code more readable and self-documenting. Given the following lines:
    Fraction *f1;

    or

    id f1;

    Which is more clear? Using proper type declarations (along with good variable names) will make the code a lot more meaningful later on.

  • It is better to fix issues at compile time than runtime. It’s difficult to test everything at runtime; the reason many commercial programs crash (assuming that they have actually been tested before they ship) is because the testers haven’t explored every possible user input. Remember that the user is trying to break your code; in a large program, you may not be the one using your program which has already shipped when a runtime issue is encountered, leading to all sorts of issues and negative reviews as you scramble to put out a patch. Do yourself a favor, and let the compiler help you catch issues before you ship.

One Final Rule

If you declare a method by the same name (including colons) in multiple classes, make sure that each declaration agrees on the type of each argument and the method’s return type. Otherwise, the compiler will have to make assumptions, especially when id types are involved, which could result in the compiler generating code that doesn’t work when the program is run. For example, if one version of an add: method took an integer and returned a float while another took a Fraction and returned void, you might have random issues (on the order of garbage output or values that simply don’t make sense) because the compiler has no way of knowing if myNumber (a hypothetical example) is of the former or latter type.

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.

  • Objects (Part 4): Inheritance—iVars & Methods


    Inheritance is a central tenet of object-oriented programming. In a nutshell, it refers to the creation of new classes by extending, or subclassing existing classes, and in doing so, inheriting their features.

    In Fraction‘s interface file, we have the following line:

    @interface Fraction : NSObject

    The part in bold indicates that Fraction is a subclass of NSObject, a class that Apple provides. Nearly every class you will create or interact with will have inherited from NSObject, directly or indirectly.

    Terminology

    A root class is at the top of the inheritance hierarchy. Almost all Cocoa Touch and Foundation classes inherit from NSObject. Incidentally, NSObject is also an abstract class, so called because it’s not designed to be actually used—rarely will you ever use an NSObject; you’ll usually create and use subclasses. An abstract class therefore is one that is created for the purposes of subclassing.

    A class that inherits from another is known as a child or subclass of its or superclass.

    What Does This Mean?

    Every subclass inherits certain properties of its superclass; exactly what is inherited can be controlled. By default, every class has access to all the instance variables of its superclass, and they work exactly as if you had defined those ivars in the subclass itself. In addition, a subclass will inherit any public methods that its superclass defines. As an example (kept in one file for brevity):

    @interface Superclass : NSObject {
    	int v;
    }
    
    - (void)initV;
    @end
    
    @implementation Superclass
    - (void)initV {
    	v = 20;
    }
    @end
    
    @interface Subclass : Superclass {
    	int w;
    }
    
    - (void)displayVars;
    @end
    
    @implementation Subclass
    - (void)initW {
    	w = 50;
    }
    
    - (void)displayVars {
    	NSLog(@"v is %d, w is %d", v, w);
    }
    @end
    
    int main(int argc, char *argv[]) {
    	NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
    
    	Subclass *sub = [[Subclass alloc] init];
    
    	[sub initV];		// Inherited method & ivar
    	[sub initW];		// Own method & ivar
    	[sub displayVars];	// Inherited method	[sub release];
    	[pool drain];
    	return 0;
    }

    Output:

    v is 20, w is 50
    Subclass inherits from Superclass, which inherits from NSObject

    The inheritance chain

    As you can see, we created an instance of an object of type Subclass. We then used it as any other class. Notice, however, that we called initV on the Subclass, and accessed the v ivar, neither of which were declared in Subclass. This is because these were already present in Superclass, and Subclass inherited them. The output proves that Subclass was able to access the inherited variables.

    Which Method?

    Remember that a method is simply a declaration of what a class can do; when it is invoked at runtime, it is chosen out of that “list.” How does the program figure out which method to call (especially if you have more than one method of the same name in the inheritance chain)? In fact, the procedure is very simple. The runtime environment searches for an explicit declaration of that method in the class of the object that you sent the message to (in the above example, the search for displayVars begins with the Subclass class. If it is found in that class, it invokes that method (in other words, it invokes the version closest to that class in the inheritance chain). If it isn’t found, it looks for the method in the superclass; if it’s found, it invokes that version; if not, the search continues, until either the method is found, or it isn’t. In the latter case, you get a compiler warning, that the class may not respond to the specific method. When your code tries to call this method during execution, your program will likely crash.

    Objects (Part 3): Extending the Fraction Class


    Welcome back, and welcome to 2011! Let’s dive right in—today, we’ll be extending our Fraction class, and covering some miscellaneous concepts of OOP.

    Implementing New Methods

    The last Extension, which described methods in detail, mentioned a method called setNumerator:overDenominator:. As you might have guessed, we are going to implement this functionality into our Fraction class, and see how we use a method with multiple arguments.

    Open up Fraction.h from the project we’ve been working on; alternatively, you can download the files by clicking here. Add the following line to the list of methods (it doesn’t matter exactly where you place it, as long as it’s between the @propertys and the final @end):

    - (void)setNumerator:(NSInteger)num overDenominator:(NSInteger)denom;

    In Fraction.m, add the following implementation for the method:

    - (void)setNumerator:(NSInteger)num overDenominator:(NSInteger)denom {
    	self.numerator = num;
    	self.denominator = denom;
    }

    Note that the names of the arguments in this method are num and denom are not the same as the instance variables numerator and denominator. This is because every instance method has access to the class’s instance variables (remember that a class is just an object that gets created at runtime—hypothetically, it would be weird if a car did not have access to its current speed (speedometer) or fuel level). Because of this, if your argument names are the same name as an instance variable, and you try to reference the argument or the instance variable, the compiler will get confused. At best, you’ll have runtime issues; at worst, the program will refuse to compile.

    Update the main function (FractionDemo.m) to have the following code:

    #import "Fraction.h"
    #import <Foundation/Foundation.h>
    
    int main (int argc, const char * argv[]) {
        NSAutoreleasePool * pool = [[NSAutoreleasePool alloc] init];
    
        Fraction *myFraction = [[Fraction alloc] init];
    	
    	// Set myFraction to value of 2/5
    	/* [myFraction setNumerator: 2];
    	[myFraction setDenominator: 5]; */
    	
    	/* myFraction.numerator = 2;
    	myFraction.denominator = 5; */
    	
    	[myFraction setNumerator:2 overDenominator:5];
    	
    	// Display the value of myFraction
    	NSLog(@"myFraction has a value of: ");
    	[myFraction display];
    	
    	[myFraction release];	
        [pool drain];
        return 0;
    }

    Just note that the main() function is where any program starts executing. Here, we are placing our actual program (where something is done) inside the main() routine; when we begin building iPhone apps, we will almost never modify main().

    Build and run your program. The output should be exactly as expected:

     myFraction has a value of: 
    2/5

    Fraction Operations

    Here, we will implement an instance method to add two fractions. As an exercise to you, try to implement subtraction, multiplication, and division! The code will be available for download at the end of this post, but try to do it on your own.

    (a/b) + (c/d) = (ad + bc)/(bd)

    Adding Fractions

    Our method looks like this:

    - (void)add:(Fraction *)newFraction;

    Note that we are passing in another Fraction object (technically, the asterisk symbolizes that we are passing in a pointer, or reference to a Fraction object). Also note that although we are only passing in one value to be added, the other (implied) value in the addition is the instance of Fraction that we’re calling this method on. Every method has access to its own instance variables, and as we’ll discover later in this post, every method can reference self.

    The implementation of the method is as follows:

    - (void)add:(Fraction *)newFraction {
    	// a/b + c/d = ((a * d) + (b * c)) / (b * d)
    	self.numerator = self.numerator * newFraction.denominator + self.denominator * newFraction.numerator;
    	self.denominator = self.denominator * newFraction.denominator;
    }

    The parentheses were not strictly necessary, but made it easier to read and understand.

    Let’s test this new method:

    #import "Fraction.h"
    #import <Foundation/Foundation.h>
    
    int main (int argc, const char * argv[]) {
        NSAutoreleasePool * pool = [[NSAutoreleasePool alloc] init];
    
        Fraction *aFraction = [[Fraction alloc] init];
    	Fraction *bFraction = [[Fraction alloc] init];
    	
    	[aFraction setNumerator:1 overDenominator:2];
    	[bFraction setNumerator:1 overDenominator:3];
    	
    	[aFraction display]; NSLog(@" + "); [bFraction display]; NSLog(@" = ");
    	[aFraction add:bFraction];
    	[aFraction display];
    	
    	[aFraction release];	
    	[bFraction release];
        [pool drain];
        return 0;
    }

    The result:

    1/2
    +
    1/3
    =
    5/6

    This is exactly correct.

    Variable Scope

    If we try our program with different numbers (say, 2/4 and 1/3, which do happen to have the same values), we get a result of 10/12. Our current add method simply adds, without reducing the value. Let’s implement a method that reduces any Fraction:

    - (void)reduce {
    	int u = self.numerator;
    	int v = self.denominator;
    	int temp = 0;
    	
    	// Euclid's procedure to find GCD (Greatest Common Denominator)
    	// Don't worry about how this works, exactly. 
    	
    	while (v != 0) {
    		temp = u % v;
    		u = v;
    		v = temp;
    	}
    	
    	self.numerator /= u;
    	self.denominator /= u;
    }

    This method declares three integer values called u, v, and temp. These are local variables—they only exist during the execution of the reduce method, they can only be accessed from the same method, and get cleared once the method is done. In more formal parlance, these variables’ scope is limited to this method. These variables must be initialized (given a value) before they are used; every time the method is invoked (called), these values are set to the default initial values.

    Incidentally, this scope also applies to variables defined within loops and if() blocks—those variables would only be accessible within the same block. As a general rule of thumb, a variable is always and only accessible in the same block as the declaration itself. A block is delineated by curly braces ({ }).

    The same scope is applied to method arguments. The arguments contain a copy of the value that you pass in, not the original. For primitive types, such as ints or floats, this means that you cannot modify the original value that you passed in, only the copy. Whenever you pass in an object (more precisely, an object pointer—more on that in the next Extension), you can, in fact, modify the original object, because a copy is made of the memory address, not of the object itself. Don’t worry if that last line was confusing; it’ll be quickly cleared up.

    Let’s test our reduce method:

    #import "Fraction.h"
    #import <Foundation/Foundation.h>
    
    int main (int argc, const char * argv[]) {
        NSAutoreleasePool * pool = [[NSAutoreleasePool alloc] init];
    
        Fraction *aFraction = [[Fraction alloc] init];
    	Fraction *bFraction = [[Fraction alloc] init];
    	
    	[aFraction setNumerator:2 overDenominator:4];
    	[bFraction setNumerator:1 overDenominator:3];
    	
    	[aFraction display]; NSLog(@" + "); [bFraction display]; NSLog(@" = ");
    	[aFraction add:bFraction];
    	[aFraction reduce];
    	[aFraction display];
    	
    	[aFraction release];	
    	[bFraction release];
        [pool drain];
        return 0;
    }

    The resulting fraction is the expected 5/6, not 10/12.

    Static variables

    A static variable is one that retains its value through multiple method calls to the same variable. Unlike a regular variable declared in a method, which gets cleared after the method is finished, a static variable does not get cleared. A static variable is automatically set to 0 at creation:

    static int counter;

    You do not need to set the variable to 0; that is already done.

    Although static variables retain their value through successive method calls, they are still only accessible in that method. Some static variables are placed outside any method, sometimes even before the @implementation line, so that they can accessed in any method.

    If a variable is a local static variable, it could be used anytime that method in which it was defined is called. If it were an instance variable, it would be able to be changed from any method. This makes it very similar to a static variable outside any method. The difference at this point is a matter of conventions. If you need to keep track of a value which does not necessary seem to “belong” to an object (for instance, the number of potholes a Car object has metaphorically gone over probably won’t be an instance variable of Car (because it is not a property of Cars), but could still be a static variable).

    self

    The self keyword is used to access the receiver of the method—the same object that sent the message in the first place. For instance, in our add: method, we could have reduced the value directly in the method:

    - (void)add:(Fraction *)newFraction {
    	// a/b + c/d = ((a * d) + (b * c)) / (b * d)
    	self.numerator = self.numerator * newFraction.denominator + self.denominator * newFraction.numerator;
    	self.denominator = self.denominator * newFraction.denominator;
    	
    	[self reduce];
    }

    This invokes the reduce method on the same object that sent the add: method (in this case, aFraction). We can remove the reduce line from the main() routine.

    Class Methods

    We can also write a class method that takes two Fractions, adds them, and returns a new Fraction. Look through the following code, and see if it makes sense.

    Fraction.h

    + (Fraction *)addFraction:(Fraction *)frac1 toFraction:(Fraction *)frac2;

    Fraction.m

    + (Fraction *)addFraction:(Fraction *)frac1 toFraction:(Fraction *)frac2 {
    	Fraction *result = [[[Fraction alloc] init] autorelease];	// Store result of addition
    																// Autorelease is memory management—
    																// Don't worry about it now. 
    
    	NSInteger resultNum = frac1.numerator * frac2.denominator + frac1.denominator * frac2.numerator;
    	NSInteger resultDenom = frac1.denominator * frac2.denominator;
    
    	[result setNumerator:resultNum overDenominator:resultDenom];
    	[result reduce];
    	
    	return result;
    }

    FractionDemo.m

    // Reset fractions
    	[aFraction setNumerator:2 overDenominator:4];
    	[bFraction setNumerator:1 overDenominator:3];
    	
    	NSLog(@"Using class method:");
    	Fraction *classAddition = [Fraction addFraction:aFraction toFraction:bFraction];
    	[classAddition display];
    

    The result is the same: 5/6.

    Source Download

    In this version, in addition to adding everything mentioned above, the Fraction class has been cleaned up. Our old setNumerator: and setDenominator: methods have been removed; the synthesized properties fill in their role. Download here. For bonus points, try implementing the commented out method (making a Fraction out of a decimal value). Good luck!

    Data Encapsulation


    A central tenet of object-oriented programming is that the objects act as “black boxes”—that is, they hide, or encapsulate, their contents. When you call a method, you don’t care how the object does what you are asking it to do; you simply expect that the object will get the task done. The same concept applies to instance variables. The use of properties, and the accessor methods in particular, help to hide the instance variables themselves. You are indirectly accessing them; you get to control their access (such as making them readonly). By using methods, you also don’t have to worry about how the instance variables themselves are set up; you simply access the methods that are provided. An additional benefit of data encapsulation is that the creator of the class can change the internal workings (change the way the instance variables are set up, or the way a method operates) without breaking code that relies on the class. It’s like a car—most people don’t care how the car works, whether it burns gas or runs on gerbil power, as long as it gets them to the grocery store and back.

     

    To be honest, it’s a difficult concept to explain; perhaps this page over at Stack Overflow (great site, BTW) does a better job.

    Objects (Part 2): Properties


    Every object is made up of instance variables (iVars) and methods. Our Fraction class, which we really began to build in the last post, contains two iVars, both NSIntegers, called numerator and denominator. Fraction also has two methods, setNumerator: and setDenominator:. These methods will set a value to the corresponding instance variable. In most cases, you would also have a method, with the same name as the instance variable (yes, this is permitted, and in this case, encouraged), which would return the value of the instance variable. These setter and getter methods are referred to as mutator and accessor methods, respectively. These methods are a major concept of object-oriented programming—data encapsulation, the notion that objects hide their instance variables.

    At this moment, our Fraction class defines and implements the setter methods (note that we simply have not needed the getter methods so far). However, this would be difficult to do when we start working with large classes, which have many instance variables. This is also not very scalable; the same code to retrieve values for a small block of data stored on an iPhone should probably not be the same code used to retrieve a long string of data from a powerful desktop Mac or even from the internet. Fortunately, there is a very convenient concept that Objective-C provides, and this construct should be used wherever possible.

    Properties

    Properties replace the accessor methods for an object. Quite simply, our Fraction class’s interface now looks like this:

    #import 
    @interface Fraction : NSObject { 
    	NSInteger numerator; 
    	NSInteger denominator; 
    }
    @property NSInteger numerator; 
    @property NSInteger denominator;
    - (void)setNumerator:(NSInteger)value; 
    - (void)setDenominator:(NSInteger)value;
    - (void)display; 
    @end

    The general form of the property statement is

    @property type name

    Note that the setter (and if we had them, the getter) methods are no longer needed. We will have the compiler synthesize these methods for us, but placing this line in our implementation, right after the @implementation line:

    #import "Fraction.h" @implementation Fraction
    @synthesize numerator, denominator 
    - (void)setNumerator:(NSInteger)value { 
        numerator = value; 
    } 
    - (void)setDenominator:(NSInteger)value {
        denominator = value; 
    } 
    - (void)display { 
        NSString *numeratorString = [[NSString alloc] initWithFormat:@"%d", self.numerator]; 
        NSString *denominatorString = [[NSString alloc] initWithFormat:@"%d", self.denominator]; 
        NSLog(@"%@/%@", numeratorString, denominatorString); 
        [denominatorString release]; 
        [numeratorString release]; 
    }
    @end 

    Of course, the identifiers after the @synthesize have to match the names you declared as properties in the interface. However, the identifiers that you declared after @property do not have the match the instance variables; instance variables will be created at compile time if this happens.

    You do not have to change your method calls in your main() routine; the methods still exist. However, there is an easier (and sometimes controversial) way to access these synthesized properties.

    The Dot Operator

    As previously mentioned, a getter method returns the value of an iVar; a setter sets a value to the iVar.

    [myFraction setNumerator: 2] // Set numerator to 2 NSInteger 
    numeratorValue = [myFraction numerator]; // returns the value of the numerator, and set to the new variable called numeratorValue

    The dot operator, introduced in Objective-C 2.0 (yes, there was a 1.0, but that was a long time ago), allows you to invoke the getter method by writing

    iVar.property

    and the setter by

    iVar.property = value

    Therefore, the above example can be re-written as

    myFraction.numerator = 2; // Set numerator to 2 
    NSInteger numeratorValue = myFraction.numerator; // set value of numerator to numeratorValue 

    C programmers might recognize this syntax as being used to access members of a struct—because that is exactly what you are doing. Behind the scenes, an object’s instance variables are stored as a struct. And if you have no idea what this line means, don’t worry—structs are a basic C data type that we may cover in a later lesson.

    Property Attributes

    In a property declaration, you can specify any number of (non-contradictory) attributes:

    @property (attribute1, attribute2, etc.) type name;

    A list of possible attributes follows (adapted from Apple’s developer documentation, The Objective-C Programming Language: Declared Properties)

    Accessor Method Names

    The default names for the getter and setter methods associated with a property are propertyName and setPropertyName: respectively—for example, given a property “foo”, the accessors would be foo and setFoo:. The following attributes allow you to specify custom names instead. They are both optional and may appear with any other attribute (except for readonly in the case of setter=).

    • getter=getterName: Specifies the name of a different (custom) getter method for the property. The method must return a value matching the iVar’s type, and it takes no arguments.
    • setter=setterName: Specifies the name of a different (custom) setter method for the property. The method must return void, and it takes one argument of the same type as the iVar.If you specify that a property is readonly then also specify a setter with setter=, you will get a compiler warning.

    Writability

    These attributes specify whether or not a property has an associated set accessor. They are mutually exclusive.

    • readwrite: This is the default; this indicates that the property will have read and write capabilities. When synthesized, both the getter and setter will be generated.
    • readonly This indicates that the property can only be read; no data can be assigned to it (through the property). When synthesized, only a getter will be generated; when you try to assign the property a value using dot syntax, a compiler error will be generated.

    Setter Semantics

    These attributes specify the semantics of the setter. They are mutually exclusive.

    • assign: This is the default; it tells the setter to directly assign the value. This is typically delineated with primitive data types (ints, floats, etc.)
    • retain: Specifies that a retain should be called on the object upon assignment; the previous value is released. This is only valid for objects. This has to do with memory management, a topic that will be fully explored in a later lesson.
    • copy: Specifies that the argument passed in should be copied, and the copy will be assigned. The previous value is released. This is only valid for objects. It is used in the case where the original property (perhaps stored on a shared server across the network) should not be modified by any other code, but when other code requests the value, for potential modification.

    Atomicity

    Atomicity means that the value is written to a temporary location in memory first, and then written to the intended location. This ensures that there is always a valid copy of the data somewhere. If an assignment was not atomic, and something happened during the writing period, the data could become fatally corrupt—the user would lose data. By writing data atomically, the data will either be the original, or the new value—never in a partially written state.

    • nonatomic: Specifies that the object should not be assigned atomically. By default, accessors are atomic (this is more beneficial in a multi-threaded environment, where multiple threads may be reading/writing value to a piece of memory.

    CS193P Material

    Properties are a very powerful notion, and used throughout the language. If this explanation was not clear enough, please see Lecture 3 of CS193P. The relevant content begins around the 16 minute mark, and ends with a demo around the 45 minute mark. Of course, feel free to view the whole lecture.

    The slides for the lecture are available on the CS193P website; for a direct link, click here.

    The code for the Fraction class at this moment can be downloaded by clicking here.

    • 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: