Extension: Rotation

In this post we’ll talk about how to handle rotating a UI. We’ll start by using existing constructs to allow our views to support rotation, and then discuss complications and their solutions. Start with a new Single View Application and call it AutoRotate. As usual, I’ll be using ARC. Open the main view controller’s implementation.

Enabling Rotation in Code

First, we have to tell the system that the view controller supports rotation and that it should rotate to a specific orientation. We do this by implementing an existing method on UIViewController:

#pragma mark - Rotation
- (BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)toInterfaceOrientation {
	return (toInterfaceOrientation != UIInterfaceOrientationPortraitUpsideDown);
}

The method might already exist in the file, provided by the template. In that case, simply change the method contents.

In this method, we’re returning a boolean value that tells the system whether to rotate to a specific orientation. We return YES for all supported orientations. Note though that we are not supporting UIInterfaceOrientationPortraitUpsideDown. Apple’s guidelines state that the upside-down orientation should not be supported unless necessary, because might end up being confused about which way is up, an important feature of phones. Of course, this distinction isn’t made on the iPad, and Apple strongly recommends to support all four orientations on iPad. But for now, we’ll support all except upside-down.

Now we’ll build the interface and implement the actual rotation logic—open the XIB. Design an interface like this one. It doesn’t matter exactly what you use, but keep it simple with some of the basic UI elements. The bar at the bottom is a UIToolbar. I put three UIBarButtonItems on it and two Flexible Spacers in between.

Initial View

Initial View

We don’t need to hook up any of the elements, because we’re just concerned with rotating the view. Build and run the app.

Initial View in Simulator

Initial View in Simulator

You can rotate the iPhone Simulator by 90 degrees at a time. Go to the Hardware menu, and then select Rotate Left or Rotate Right. You can also use Command-LeftArrow or Command-RightArrow. The rotation will be accompanied by a corresponding animation, and you’re left with a view that look like this:

Mangled Rotation

Mangled Rotation

While the background rotated (along with the bottom toolbar, which is handled by the system, the rest of the view didn’t change. We can fix that with a few different ways.

Struts and Springs

Struts and springs are a simple IB construct that gives you a few options to stretch and position views. Select the “1″ button and go to the Size Inspector (the one with the Ruler icon). You’ll see a section called Autosizing. If you mouse over the Example area to the right, it’ll animate to show you the changes.

Autosizing UI

Autosizing UI

The autosizing area is where you make the changes. You’ll see a square with I-beams (struts) on the outside and double arrows (springs) on the inside. The I-beams on the outside acts as “anchors” to the sides of the containing view. The arrows on the inside tell the subview to expand with the containing view. Behavioral conditions:

  • If all the I-beams are enabled, the subview will stay the same size and anchored near (0,0) in the containing view. On the iPhone, that would be the top-left corner.
  • If no I-beams or double arrows are enabled, the subview will stay in the same size in the center of the containing view.
  • If all the double arrows are enabled but no I-beams, the subview will expand proportionally to the containing view.
  • If all the double arrows and I-beams are enabled, the view will expand with the subview, keeping the same distance around all the edges.

You can see all of this happening in the Example.

We can use these struts and springs to position some of the UI. All the buttons and the label should have both springs enabled. The progress view and slider should have the horizontal spring enabled. Button 1 should have the top and left struts enabled; button 2 should have top and right. Button 3 should have just left; button 4 should have just right. The label should have no struts enabled.

The progress view should have just the left strut; the slider just the right strut. The textview at the bottom should have both springs, the bottom, and left and right struts enabled.

Build and run again, and we see something like this:

Struts & Springs UI

Struts & Springs UI

It’s almost perfect. Springs and struts give you some basic flexibility—it moved our buttons and label nicely—but for more complex situations, like the lower part of our view, we need something more robust.

Swapping Views

Swapping views as necessary gives you the flexibility to structure your views any way you want using the convenience of Interface Builder. Begin by adding two outlets to the view controller’s header:

@property (strong, nonatomic) IBOutlet UIView *portraitView;
@property (strong, nonatomic) IBOutlet UIView *landscapeView;

Synthesize the properties and go over to the XIB. Drag out a new view and go to the Attributes Inspector. Under Orientation in Simulated Metrics, select “Landscape”. Build a view similar to this:

Manual Landscape View

Manual Landscape View

Connect the new view as landscapeView, and the old view as portraitView. Go to the implementation file, where we will handle the swap. Add the following code to the bottom of the file, before the @end:

#define degreesToRadians(x) (M_PI * (x) / 180.0)
- (void)willRotateToInterfaceOrientation:(UIInterfaceOrientation)toInterfaceOrientation duration:(NSTimeInterval)duration {
	if (toInterfaceOrientation == UIDeviceOrientationPortrait) {
		self.view = self.portraitView;
		self.view.transform = CGAffineTransformIdentity;
		self.view.transform = CGAffineTransformMakeRotation(degreesToRadians(0)); 
		self.view.bounds = CGRectMake(0.0, 0.0, 320.0, 460.0);
	}
	else if (toInterfaceOrientation == UIDeviceOrientationLandscapeRight) {
		self.view = self.landscapeView;
		self.view.transform = CGAffineTransformIdentity; 
		self.view.transform = CGAffineTransformMakeRotation(degreesToRadians(-90)); 
		self.view.bounds = CGRectMake(0.0, 0.0, 480.0, 300.0);
	}
	else if (toInterfaceOrientation == UIDeviceOrientationLandscapeLeft) {
		self.view = self.landscapeView;
		self.view.transform = CGAffineTransformIdentity; 
		self.view.transform = CGAffineTransformMakeRotation(degreesToRadians(90)); 
		self.view.bounds = CGRectMake(0.0, 0.0, 480.0, 300.0);
	}
	else
		return;
}

We start with a pre-processor macro that converts degrees to radians. iOS uses radians in its graphics work, but it’s easier for us people to think in degrees. Note that in this case, we will have to do some custom graphics work, because we will have to transform the view to match the rotation.

Inside the delegate method, we check for the corresponding orientation and swap the view in the first line of each condition. Then we reset the view’s transformation. We’ll cover transformations in a future post. We use a provided function to make a rotation transformation and apply it to the view. We also change the size of the view to fit the screen. All of these changes happen in the will method, so they are complete before the actual rotation happen, and the correct view will be displayed in time. Note that animating all aspects of the transition would require additional code, which is beyond the scope of this post.

Rotating Tips

On the iPhone, not all apps support all orientations, or even rotation at all. On iPad, apps should support as many orientations as possible—at least both variants one orientation; preferably all four orientations.

Note that landscape and portrait views don’t necessarily have to present the same information, or even the same appearance. The Music app on the iPhone displays a UIKit-based tab bar and table interface in portrait view, but a custom coverflow interface in landscape.

If you’re doing some custom views/drawing, make sure the view animates when you rotate, especially if you’re displaying content such a grid of icons or text. Otherwise, it is a very disorientating experience for the user and might discourage use of your app.

Finally, make sure there is some meaningful change when the user rotates. If you’re just stretching the UI, consider whether it makes sense to rotate, or if rotation is worth the effort. If you have text input, the larger keyboard might be worth it—but you also loose a large portion of the rest of the content. Otherwise, rotating might not be necessary.

Download AutoRotate here.

Extension: Advanced Tables

In this post we’re going to take a step away from our existing project and look at other things UITableView will allow us to do. We’ll load in data from a plist, add some more elements to our table view, including images, subtexts, and allowing editing. These features allow us and the user to customize table views beyond the default appearance. Table views are a very important part of the iOS SDK and are found in many apps; fortunately, they are easy to customize—you can even create your own cells in anyway you’d like!

Open Xcode and create a new Single View application. Call it “AdvancedTables”, set the class prefix to “AT”, Device Family to iPhone, and Use Automatic Reference Counting. Save the project somewhere and create it.

Next, click here to download a file which contains a list of 51 cities and their population. The file is a basic XML-based plist, which is a file type used throughout iOS to store simple data structures like this. Add the file into the Xcode project.

Setting up the View Controller

Open ATViewController.h and have it adopt UITableViewDelegate and UITableViewDataSource. In ATViewController.xib, drag out a Table View from the Library and place it inside the existing view. Control-Drag from the table back to File’s Owner, connecting the table’s data source and delegate outlets.

Next, go to ATViewController.h. Create two strong properties of type NSMutableArray; call them names and populations. In the .m file, synthesize them. We’ll load in data from the plist in the viewDidLoad method:

- (void)viewDidLoad {
    [super viewDidLoad];
	// Do any additional setup after loading the view, typically from a nib.
	NSString *filePath = [[NSBundle mainBundle] pathForResource:@"Cities" ofType:@"plist"];
	NSData *data = [NSData dataWithContentsOfFile:filePath];
	NSPropertyListFormat format;
	NSString *error;
	id fileContents = [NSPropertyListSerialization propertyListFromData:data mutabilityOption:NSPropertyListImmutable format:&format errorDescription:&error];
	self.populations = [[fileContents objectForKey:@"City Population"] mutableCopy];
	self.names = [[fileContents objectForKey:@"City Names"] mutableCopy];
}

Notice that we don’t do any sort of checking on the fileContents result. It would may seem like a good idea to at least check if the dictionary had the two keys; if it only had one, the app would crash when trying to access one or both of them. However, this is a special design consideration. The data source is the driving force of the entire app; it wouldn’t make much sense if some of this data doesn’t exist. We don’t really want the app to continue if the data isn’t valid, so letting it crash might be a good idea in this case.

Next, we implement the data source methods like we did in the last post. Our table will only one section; with a more robust data source such as Core Data, it becomes much easier to implement multiple sections and an index down the side like you’d see in the Music app. For now though, we’ll settle for one section. The number of rows will be determined by the number of elements in either one of the data arrays, as they should correspond—and they do!

- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView {
	return 1;
}

- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section {
	return [self.names count];
}

- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
	static NSString *cellID = @"CellID";
	UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:cellID];
	if (!cell) {		// Create new cell
		cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleSubtitle reuseIdentifier:cellID];
		cell.showsReorderControl = YES;
	}
	cell.textLabel.text = [self.names objectAtIndex:indexPath.row];
	cell.detailTextLabel.text = [NSString stringWithFormat:@"Population: %@", [self.populations objectAtIndex:indexPath.row]];
	cell.imageView.image = [UIImage imageNamed:@"CaliforniaIcon.png"];
	cell.imageView.highlightedImage = [UIImage imageNamed:@"CaliforniaIconPressed.png"];
	return cell;
}

Notably here, we create a cell with a different style, one with a subtitle. The iOS SDK comes with four styles, shown below (click for larger version):

iOS Table View Cell Styles

iOS Table View Cell Styles

The default style doesn’t contain any detail text; nothing will happen if you set the detailTextLabel property.

The showsReorderControl property is a boolean; if true, it will display a reordering control that appears in editing mode. We’ll get into that in a little bit.

Adding an Image to Cells

If you want to add an image to the left of the cell (as with album art in the Music app, or video previews in the YouTube app), it takes very little work. Download the icons and add the following lines within the if (!cell) block:

cell.imageView.image = [UIImage imageNamed:@"CaliforniaIcon.png"];
cell.imageView.highlightedImage = [UIImage imageNamed:@"CaliforniaIconPressed.png"];

The image property is what gets displayed normally; the highlightedImage is swapped in if the cell is highlighted.

If you want the image anywhere else in the cell, you’ll have to create your own cells, which will be a topic for another post—there’s a lot involved!

Editing Table Views

First we’ll need some UI to enable editing. Go into the XIB, lower the top margin of the table view, and drag out a normal Navigation Bar and place it at the top of the view, filling the gap. You can have a navigation bar without a nav controller; in that case, it just becomes an “anchor” of sorts for a few commands. You use nav bars at the top of the screen and toolbars at the bottom. Drag out a Bar Button Item and place it on the left of the nav bar; a “well” will appear as you drag over the location. In the Attributes Inspector, set the Title to “Edit”. Connect the button to a new property called editingToggle. In addition, create an outlet for the table view; call it tableView. Wire it up.

Create a new method called toggleEdit and wire it up to the button. First, we’ll set the table’s editing mode to whatever it’s currently not—if it’s not in editing, make it enter editing mode and vice versa. Then we’ll adjust the button to reflect this change in state. In iOS, the Done button has a different tint; we can use a system-defined parameter rather than having to approximate it with our own.

- (IBAction)toggleEdit:(id)sender {
	[self.mainTable setEditing:!self.mainTable.isEditing animated:YES];
	if (self.mainTable.isEditing) {
		[self.editingToggle setStyle:UIBarButtonItemStyleDone];
		[self.editingToggle setTitle:@"Done"];
	}
	else {
		[self.editingToggle setStyle:UIBarButtonItemStyleBordered];
		[self.editingToggle setTitle:@"Edit"];
	}
}

Next we implement a few data source methods to allow editing, then to handle the edits.

- (BOOL)tableView:(UITableView *)tableView canEditRowAtIndexPath:(NSIndexPath *)indexPath {
	return YES;
}

- (BOOL)tableView:(UITableView *)tableView canMoveRowAtIndexPath:(NSIndexPath *)indexPath {
	return YES;
}

- (void)tableView:(UITableView *)tableView moveRowAtIndexPath:(NSIndexPath *)fromIndexPath toIndexPath:(NSIndexPath *)toIndexPath {
	NSUInteger fromRow = [fromIndexPath row];
	NSUInteger toRow = [toIndexPath row];
	id name = [self.names objectAtIndex:fromRow];
	id pop = [self.populations objectAtIndex:fromRow];
	[self.names removeObjectAtIndex:fromRow];
	[self.populations removeObjectAtIndex:fromRow];
	[self.names insertObject:name atIndex:toRow];
	[self.populations insertObject:pop atIndex:toRow];
}

- (void)tableView:(UITableView *)tableView
commitEditingStyle:(UITableViewCellEditingStyle)editingStyle forRowAtIndexPath:(NSIndexPath *)indexPath {
	NSUInteger row = [indexPath row];
	[self.names removeObjectAtIndex:row];
	[self.populations removeObjectAtIndex:row];
	[tableView deleteRowsAtIndexPaths:[NSArray arrayWithObject:indexPath] withRowAnimation:UITableViewRowAnimationFade];
}

The first two methods tell the table that all the rows can be edited (in this case, deletion is allowed; the alternative is None or Insertion), and that they can be moved. Then we declare the methods that handle the move or delete (in the latter case, it falls under the commitEditingStyle: method). In those methods, we remove (and insert) objects from our backing arrays as necessary.

These edits will remain until the memory is cleared (when the app quits). We’ll look at persistence—saving these changes back to the file—in a later extension.

Other Actions

The UITableViewDelegate declares some methods to support some other actions, including accessory views (views on the side of the cell, which you can wire up to trigger additional actions). Now, we’ll handle the selection, and allow you to put a check mark next to the cell that the user selects.

First, we’ll need to create a new property of type NSIndexPath that will hold the current selection.

@property (strong, nonatomic) NSIndexPath *lastIndexPath;

Next, we need to do some checks in the cellForRow… method—because the method will recycle cells as you scroll, we don’t want the checkmarks to get recycled as well. We check to see if a selection has been made, and if the rows are the same. If they are, then we display the checkmark (this is useful when you scroll back to your selection). Else, we display no checkmark (this is useful if you scroll down or up past your existing selection).

We handle the selection like this:

- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath {
	NSUInteger row = indexPath.row;
	NSUInteger oldRow = lastIndexPath.row;
	if (oldRow != row) {
		UITableViewCell *newCell = [tableView cellForRowAtIndexPath:indexPath]; 
		newCell.accessoryType = UITableViewCellAccessoryCheckmark;
		UITableViewCell *oldCell = [tableView cellForRowAtIndexPath:lastIndexPath];
		oldCell.accessoryType = UITableViewCellAccessoryNone;
		lastIndexPath = indexPath;
	}
	[tableView deselectRowAtIndexPath:indexPath animated:YES];
}

If the selections are different, we put a checkmark on the new cell and put nothing on the old cell. If they’re the same, nothing changes. In either case, we deselect the cell to prevent it from being highlighted. Build and run, and you can see the checkmark appearing as you click on each cell.

Row Heights

You can change the height of one or more rows using a simple delegate method:

- (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath {
	return 88;
}

The default height is 44; this method would make the cells twice as high.

Indenting Rows

You can control the indent of each row with a delegate method:

- (NSInteger)tableView:(UITableView *)tableView indentationLevelForRowAtIndexPath:(NSIndexPath *)indexPath {
	return indexPath.row;
}

This example would create a cascade of cells being indented further with each row. Going beyond a level of 5 or 6 looks really weird, so don’t go too far.

That’s the primary abilities that standard table views can offer. The data source and delegate protocols declare a few other features; we’ll touch upon some of them including sections and the index when we start working with files and persistence.

Download the project here.

Objective-C Lesson 12: Exception Handling

Because of Objective-C’s dynamic runtime there are cases where errors go unnoticed until runtime. These might include sending messages an object doesn’t respond to, or going out of bounds of an array. Sometimes you may not even catch these errors while you’re testing—for example, say you had an application that allowed users to access a value in an array by entering an index value. If the user entered ’20′ when there are only 5 values in the array, the index would be out of bounds; in your testing, you may never have thought to check such numbers.

Granted, the above example is rather contrived, but you should still try to anticipate such issues and do something about them, so your program doesn’t crash. Resolving these issues generally includes logging them and informing the user—an incorrect value, in the above example, or if it’s an error the user can’t fix (sending the wrong message), notify the user and then gracefully exit.

Runtime issues such as these are typically exceptions. If uncaught, your program will quit. Let’s look at an example.

#import <Foundation/Foundation.h>
int main(int argc, char *argv[]) {
	NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
	NSArray *array = [[NSArray alloc] initWithObject:@"A string"];
	[array nonExistantMethod];
	NSLog(@"Object at index 5 is %@", [array objectAtIndex:5]);
	[array release];
	NSLog(@"No issues!");
	[pool drain];
	return 0;
}

Building the program nets you a warning that your array may not respond to nonExistantMethod (obviously). If we run it, we get a whole bunch of info:

2011-05-02 19:18:59.492 Exceptions[760:707] -[__NSArrayI nonExistantMethod]: unrecognized selector sent to instance 0x100113e90
2011-05-02 19:18:59.525 Exceptions[760:707] *** Terminating app due to uncaught exception 'NSInvalidArgumentException', reason: '-[__NSArrayI nonExistantMethod]: unrecognized selector sent to instance 0x100113e90'
*** First throw call stack:
(
	0   CoreFoundation                      0x00007fff8f509406 __exceptionPreprocess + 198
	1   libobjc.A.dylib                     0x00007fff941a09ea objc_exception_throw + 43
	2   CoreFoundation                      0x00007fff8f584ade -[NSObject doesNotRecognizeSelector:] + 190
	3   CoreFoundation                      0x00007fff8f4db8a3 ___forwarding___ + 371
	4   CoreFoundation                      0x00007fff8f4d8178 _CF_forwarding_prep_0 + 232
	5   Exceptions                          0x0000000100000e33 main + 195
	6   Exceptions                          0x0000000100000d64 start + 52
	7   ???                                 0x0000000000000003 0x0 + 3
)
terminate called throwing an exception

Wading through that, we see some indications that array does indeed not respond to nonExistantMethod. For education’s sake, we’re going to keep that line, and instead use the @try…@catch() blocks. This is what our program now looks like:

#import 

int main(int argc, char *argv[]) {
	NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
	NSArray *array = [[NSArray alloc] initWithObject:@"A string"];
    @try {
        [array nonExistantMethod];
    }
    @catch (NSException *exception) {
        NSLog(@"Caught exception %@", exception);
    }
	NSLog(@"Object at index 5 is %@", [array objectAtIndex:5]);
	[array release];
	NSLog(@"No issues!");
	[pool drain];
	return 0;
}

The code in the @try block is your usual program code. The code in the @catch block is your error handling code—here, we just log the exception. In fact, the next NSLog will also cause an exception—an out of bounds exception. We can therefore move that line into the @try block.

int main(int argc, char *argv[]) {
	NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
	NSArray *array = [[NSArray alloc] initWithObject:@"A string"];
    @try {
        [array nonExistantMethod];
        NSLog(@"Object at index 5 is %@", [array objectAtIndex:5]);
    }
    @catch (NSException *exception) {
        NSLog(@"Caught exception %@", exception);
    }
	[array release];
	NSLog(@"No issues!");
	[pool drain];
	return 0;
}

The results are now

2011-05-02 19:33:09.308 Exceptions[872:707] -[__NSArrayI nonExistantMethod]: unrecognized selector sent to instance 0x100113e90
2011-05-02 19:33:09.312 Exceptions[872:707] Caught exception -[__NSArrayI nonExistantMethod]: unrecognized selector sent to instance 0x100113e90
2011-05-02 19:33:09.313 Exceptions[872:707] No issues!

Note now that our program no longer crashes, but instead catches the exceptions, and continues. Note that the @try…@catch block is exited after the first exception is caught, regardless of if there are any others.

You can also append a @finally { } block that executes regardless of whether there has been an exception or not.

Throwing Exceptions

Unlike in Java, Objective-C exceptions should not be thrown whenever there is an error—try to handle the error, or use something like NSError or the NSAssert() method.

As Apple states, “You should reserve the use of exceptions for programming or unexpected runtime errors such as out-of-bounds collection access, attempts to mutate immutable objects, sending an invalid message, and losing the connection to the window server. You usually take care of these sorts of errors with exceptions when an application is being created rather than at runtime.
If you have an existing body of code (such as third-party library) that uses exceptions to handle error conditions, you may use the code as-is in your Cocoa application. But you should ensure that any expected runtime exceptions do not escape from these subsystems and end up in the caller’s code. For example, a parsing library might use exceptions internally to indicate problems and enable a quick exit from a parsing state that could be deeply recursive; however, you should take care to catch such exceptions at the top level of the library and translate them into an appropriate return code or state.”

Throwing exceptions is a rather “expensive” procedure, and of course uncaught exceptions will cause your program to crash. That being said, throwing an exception is straightforward:

NSException* myException = [NSException
        exceptionWithName:@"FileNotFoundException"
        reason:@"File Not Found on System"
        userInfo:nil];
@throw myException;
// [myException raise]; /* equivalent to above directive */

A more popular usage is in the case where you can’t recover from the issue (invalid data, for example)—you could catch the exception to perform some cleanup, then throw it back to the system for something else to catch, or to quit at that point.

Exceptions provide a powerful and flexible way to handle issues in your code. Use them wisely—don’t make the runtime system a massive juggling round.

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 accessor methods. 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.

Supplement: CS193P

Stanford University has offered the CS193P class for a few quarters now. It has become the ubiquitous source of beginning iPhone programming material for many people. The lectures are filmed and uploaded to iTunes U, and they are now available in 720p HD video. However, the lectures, which are the only option for non-enrolled students, are rather impersonal, and sometimes move too quickly (in my opinion). I have learned a lot from them, but I don’t think they’re the only source. In fact, I find that they are more helpful once I got the basics down. Nevertheless, there are real gems, and so I will be occasionally referring links as supplementary material.

To access the lectures:

  1. Launch iTunes and head into the iTunes Store (don’t worry, all the lectures are free).
  2. Click on iTunes U on the black bar near the top of the storefront.
  3. At the time of this writing, the lectures were under Staff Favorites. Click on the link, and you’ll be led to the main listing.

    CS193P Fall 2010 Lectures Listing

    iTunes Listing

Direct link (will open iTunes, if it is not already running):
http://itunes.apple.com/WebObjects/MZStore.woa/wa/viewPodcast?id=395605774

The lectures are available for streaming; alternatively, you can download them just like regular songs from iTunes. Note that file sizes are around 500MBs for the SD version and around 700MBs for the (linked) HD version.

The slides can be downloaded from the main website, listed above, as well as the sample code.

Extension 7: Loop Aids

In a loop, you can use the keywords continue; and break; to control the execution of the loop.

The continue Keyword

In a loop, continue tells the program to begin the next iteration, ignoring any code that comes after it. For example, if we had a simple program that reads in input and checks if the input is 5, we could use the following code:

for (int times = 1; times <= 10; times++) {
	int input;
	scanf("%d", &input);
	if (input != 5)
		continue;
	NSLog(@"Five!");
} 

In this loop, if the input is not 5, then the word “Five” is not printed. The program moves on to the next iteration of the loop.

The break Keyword

The break statement is used in the same context as continue, except that the break statement immediately ends the execution of the loop and moves on to the code after the loop. For example:

for (int times = 1; times <= 10; times++) {
	int input;
	scanf("%d", &input);
	if (input != 5)
		break;
}
NSLog(@"Not Five!");

“Not Five” will be printed as soon as the user enters something that is not five.

Pitfalls

Keep in mind that continue and break only apply to the “closest” loop—that is, in the case of nested loops, they will only apply to the “deepest” loop.

Extension 6: Incrementation

In the last Lesson, I used the following expression on Line 11 of the first example:

n = n + 1

This is perfectly valid code. The associativity of the equals operator (=) is right-to-left—this means that whatever is on the right of the operator is evaluated first, then the result is assigned back to the variable or identifier on the left side. So in this case, the program first determines the value of n + 1, and assigns this new value back to n. This method is fine, but there are more elegant solutions.

Operator-Equals

The following is completely equivalent to the above example:

n += 1;

What this is saying is to evaluate the result of n + 1 and assign it back to n—the identical meaning to the above example. The difference is that in this case the code is clearer, and much more concise. The following are all valid:

n += 3;    // Same as n = n + 3;
n -= 3;    // Same as n = n - 3;
n *= 3;    // Same as n = n * 3;
n /= 3;    // Same as n = n / 3;
n %=3;     // Same as n = n % 3;

In each case, the value of n operator 1 is evaluated, then the result is assigned back to n. This construct is highly recommended, and is used almost exclusively in place of the original.

++ and ––

That should be two minus signs; WordPress is over-optimizing the typography.

Returning to the concept of loops, most loops execute a specific number of times; this number is often determined by an upper limit value, and a counter is used to keep track of the increments until the maximum is reached. In these cases, the counter would be incremented by 1. In the original example, the code to do this was

n = n + 1;

As described above, n += 1; is also valid.

But incrementing (or decrementing) by 1 is such a popular thing to do in code (believe it or not) that Objective-C (and plain C) has a special construct to do just that:

n++;     // Increment by 1
n--;     // Decrement by 1 

As with the above construct, the double-plus or double-minus is used almost exclusively when the program needs to increment or decrement by 1. At the very least, it saves you typing; at best, compilers can produce more concise and efficient code when the double-plus or double-minus is used (Prata, C Primer Plus, 5th Edition, SAMS: Page 146). Note that these operators increment by exactly 1, and can only be used with integers.

One thing to note is that the increment and decrement operators have a prefix and postfix version.

Prefix versus Postfix

Compare the following statements:

int n = 4;
if (n++ < 5)     // Case 1
   NSLog(@"n is %d", n);
n = 4;           // "Reset" value of n
if (++n < 5)     // Case 2
   NSLog(@"n is %d", n);
NSLog(@"n is %d", n);     

The output is:

n is 5
n is 5 

There are three NSLogs in the code, yet only two lines of output. This reflects a subtlety in the increment/decrement operators. Why does this happen?

In Case 1, the value of n is compared with 5 first, and then incremented. n was initially set to 4; 4 is less than 5, so the initial NSLog executes. However, by that point, n had been incremented to 5.

In Case 2, the value of n is first incremented, then compared to 5. So n first becomes 5; 5 is not less than 5, and so the second NSLog does not execute. However, the final NSLog displays the value of n, which is now 5.

In the majority of cases, where only the end result is concerned, using the prefix or postfix does not matter—in either case, the end result is 5. However, for intermediate cases, such as the code within the if() statements, the choice does matter.

Finally, happy holidays to all my readers! Thanks for stopping by, and I hope you’ll come back for more. We’ll be getting into the good stuff (objects and more advanced programs) soon—probably before 2011 rolls around. Merry Hanukkah (and Kwanzaa, and whatever else is politically correct)! :D

Objective-C Lesson 5: Loops

A computer is really good at executing the same thing over and over again. A lot of programming involves doing the same thing over and over again (and no, that’s not meant to be a programming joke, although it could certainly be taken as one). Objective-C allows for two main ways of looping. We’ll start with a rather general problem.

The for() Loop

The Problem

Create a program that displays the sum of all the numbers from 1 to a user-specified value. Note that to get a user-specified value, we are going to be using a function call to scanf(), which is similar to NSLog, except that it reads input, rather than exporting it. A brief explanation will follow, but scanf() is not a function that you will regularly use on the iPhone. It is just a “place-holder” way to get input until we can use better options.

As for the problem itself, we are adding all the numbers between 1 and the input. So if the input was 5, we would output the sum as 1 + 2 + 3 + 4 + 5, or 15.

The (Inefficient) Solution

We could just take all the values and simply add them up:

int sum = 1 + 2 + 3 + 4 + 5;

sum would equal 15, just as expected. But although this method is rather simple and the most logical at this point, it is not very efficient, or scalable. If the user had entered a large value, such as 1000, writing the code for that would be torturous. But there will be a user who will enter 1000. You will almost never be able to anticipate every single type of input your user might give or do to your program, and you should probably have a solution to handle every case. So how are we going to handle the case of the user inputting 1000—or indeed, any value other than 5 (in this case)?

The Best Solution

We would use a loop. Here is the code to accomplish the problem above, using a for() loop:

#import <Foundation/Foundation.h>int main (int argc, const char * argv[]) {
    NSAutoreleasePool * pool = [[NSAutoreleasePool alloc] init];

	int sum = 0;	// Make sure we start at zero, to avoid issues later

	// Allow user input
	NSLog(@"Please enter a value.");
	int times;
	scanf("%d", &times);

	// for() loop construct:
	for(int n = 1; n <= times; n = n + 1)
		sum = sum + n;

	NSLog(@"Sum of all values from 1 to %d is %d", times, sum);

    [pool drain];
    return 0;
}

The output shows that for an initial value of 1000, the sum is 500500.

Code Demystified

The for() loop is basically three statements in one line. Within the parentheses following the “for”, you have the initial expression, condition, and the loop expression. These are separated by semicolons.

Initial Expression

This statement is usually used to initialize variables used in the loop and assign them values. Usually this will be a counter of some sort which will not be used elsewhere in the program. In this case, I like to declare the variable and assign it a value at the same time, as I did above, rather than having int n; and then n = 1; in the loop. It just makes the code more concise.

You can initialize and declare more than one variable at a time: int a = 0, b = 3, c = 5;. For the most part though, you will only need some form of a (integer) counter variable, and you won’t need multiple variables. You could still use that to your advantage, though.

It is important to note that any variables you initialize here (initialize means that you have not made any mention of this variable before, and you are creating a new variable here) are only valid within the loop—as soon as you exit the loop (described below) the variable is no longer accessible.

Condition

This is a boolean condition that determines if the loop is still valid. If this expression becomes untrue (usually due to the loop expression), then the program exits out of the loop without running through the loop again. The program then moves on to the next line after the loop.

Remember that when comparing integers, as you usually will when using a loop, the following two expressions are equivalent: n <= 1000; and n < 1001;. But in this case, because you are only checking up to 1000 (and because the user entered 1000, and you should avoid modifying the input if possible; in addition, having to add one to the input just to use a less-than construct is not a good choice), the first expression makes more logical sense.

Loop Expression

This is an expression that is executed after the main body of the loop; it is usually a way to increment the initialized variable(s). In this case, the counter variable n is incremented by 1, so that eventually the loop condition will become false.

General Operational Procedure

Excerpted from Stephen Kochan’s Programming in Objective-C 2.0, 2nd Edition published by Addison-Wesley, &copy2009:

  1. The initial expression is evaluated first. This expression usually sets a variable that is used inside the loop, generally referred to as an index or counter variable, to some initial value (often 0 or 1).
  2. The looping condition is evaluated. If the condition is not satisfied (the expression is FALSE or NO), the loop immediately terminates. Execution continues with the program statement that immediately follows the loop.
  3. The program statement(s) that constitutes the body of the loop is executed.
  4. The looping expression is evaluated. This expression is generally used to change the value of the index variable, frequently by adding or subtracting 1.
  5. Return to step 2.

The while() Loop

This is another type of loop that Objective-C supports. Although most tutorials and textbooks would consider them (roughly) equivalent, I use for() loops whenever I know how many times I want to go through, whether it is by a hard-coded value or by a the value in a variable. The while() loop is better suited for cases where the program does not know how many times it is to be run, and instead checks for a different condition. This can be better illustrated with an example.

The Problem (Revised)

Calculate the sum of all the integers that the user enters; the user input is terminated by any negative value.

The Solution

This would be very difficult to do with a for() loop, as you do not know how many times to execute. You would have to do an ugly check in the loop condition. A while() loop makes this more elegant.

#import <Foundation/Foundation.h>

int main (int argc, const char * argv[]) {
    NSAutoreleasePool * pool = [[NSAutoreleasePool alloc] init];

	NSLog(@"Input a list of integers. Hit Return after each one. End the list with a negative value.");
	int sum = 0;
	int input = 0;
	scanf("%d", &input);

	while (input >= 0) {	// "Greater-than or equal to zero" is the same as "not negative"
		sum = sum + input;
		scanf("%d", &input);
	}

	NSLog(@"The sum of all those values is %d", sum);    [pool drain];
    return 0;
}

Here is a sample run:

Input a list of integers. Hit Return after each one. End the list with a negative value.
5
2
18
30
37
69
-29
The sum of all those values is 161

Code Demystified

The expression for the while() loop is a simple boolean condition (the middle part of the for() loop). This gets evaluated, and if it is true, the body is executed. If not, the program moves on. This means that in the loop, you have to make the value untrue somehow; otherwise you will get an infinite loop.

The do-while Loop

In a regular for() or while() loop, the condition is checked first, then the loop is executed. This means that if the initial condition was false to begin with, the body of the loop would never ever get executed; the code would skip directly over the loop. The do-while loop is guaranteed to perform one iteration of the loop first, before checking the condition. From that point on, as long as the condition remains true, the loop will continue to execute. The syntax is as follows:

int n = 1;
do {
	sum = sum + n;
	n = n + 1;
} while (n <= 5);

The above example is a re-writing of the previous loop examples, using the do-while construct. A more compelling example might look like this:

int sum = 0;
int input = 0;
do {
	sum = sum + input;
	NSLog(@"Sum is %d", sum);
	scanf("%d", &input);
} while (input != -1);

In the above example, the program will continue to display the sum of all the values the user enters until negative 1 is entered. The loop in this case ensures that the NSLog is printed at least once, and the user input is gathered; in this case, the input is placed in the loop, rather than having to have an additional scanf() outside of the loop. This makes the code a little easier to read and understand.

Pitfalls

A few things that people get tripped up on:

  • Forgetting braces: If you have more than one statement that you want to execute given a certain condition, you must enclose them within curly braces. Otherwise, they can stand alone as one line, without braces.
  • The Infinite Loop: The loop condition must be made false, somehow. (It’s also a really good idea to actually have a loop condition). Otherwise, your loop will never end, and it keep running until your computer crashes, or until the power goes out.

Conclusion

Loops are relatively easy to implement, yet they can save you a lot of coding. For a big loop, like in the first example, you truly begin to leverage your computer’s processing power, and save yourself a lot of work. When combined with if() statements, they can become quite powerful.

Extension 5: The switch Statement

The switch() statement is used in place of a chain of if()else if()else if()else if()else construct, where such a chain does not lead to very readable, follow-able code. The switch() statement is designed for this situation. Here is the general format:

switch(expression or variable)
{
     case value1:
          // Program statement
          // Program statement
          ...
          break;
     case value2:
          // Program statement
          // Program statement
          ...
          break;
     case value3:
          // Program statement
          // Program statement
          ...
          break;
     ...
     case valueN:
          // Program statement
          // Program statement
          ...
          break;
     default:
          // Program statement
          // Program statement
          ...
          break;
}

The expression value in the parentheses at the beginning of the statement is compared to each of the values, and when one of them matches, the following code (note the colon, and the lack of braces around each case), the code below is executed. It then breaks out of the rest of the switch, to avoid executing the rest of it (“overflowing” into the code for the next condition). This can sometimes be used intentionally, but for most of the time make sure to use the break; statement.

If none of the conditions match (the else of an if()else block), the code under the default condition is executed.

The following is an example of a switch() statement and the equivalent if()else block:

// This is an example of a switch statement.
#import <Foundation/Foundation.h>

int main (int argc, const char * argv[]) {
    NSAutoreleasePool * pool = [[NSAutoreleasePool alloc] init];

	NSString *operator = [[NSString alloc] init];     // We will talk in depth about NSString later on down the road.
	// Assume operator has been initialized to a valid value
	// Valid values for operator are @"+", @"-", @"*", and @"/"

	switch (operator) {
		case @"+":
			NSLog(@"Operator is for addition.");
			break;
		case @"-":
			NSLog(@"Operator is for subtraction.");
			break;
		case @"*":
			NSLog(@"Operator is for multiplication.");
			break;
		case @"/":
			NSLog(@"Operator is for division.");
			break;
		default:
			NSLog(@"Unknown operator.");
			break;
	}

    [pool drain];
    return 0;
}

This code should be rather self-explanatory; operator is compared to the values after each case, and if one of them matches, the executes the corresponding NSLog and breaks out of the switch. If none of them match, then it hits the default statement.

The equivalent if()else block follows:

// This is an example of a switch statement.
#import <Foundation/Foundation.h>

int main (int argc, const char * argv[]) {
    NSAutoreleasePool * pool = [[NSAutoreleasePool alloc] init];

	NSString *operator = [[NSString alloc] init];
	// Assume operator has been initialized to a valid value
	// Valid values for operator are @"+", @"-", @"*", and @"/"

	/* Note that NSString actually has a comparison method, and that
	   this method of comparison is not guaranteed to work. We are
	   overlooking that at the moment for the sake of education. */
	if (operator == @"+")
		NSLog(@"Operator is for addition.");
	else if (operator == @"-")
		NSLog(@"Operator is for subtraction.");
	else if (operator == @"*")
		NSLog(@"Operator is for multiplication.");
	else if (operator == @"/")
		NSLog(@"Operator is for division.");
	else
		NSLog(@"Unknown operator.");    [pool drain];
    return 0;
}

This code does the same thing as the switch statement above (bar the comment in bold).

The switch() statement is makes more logical sense, but some people find it annoying. It all comes down to a matter of personal preference. No big deal.

BTW less than a month after the previous milestone we are at 2000 views! Thanks to everyone who stopped by, and I hope to see you back!

Postscript: Wikipedia has a nice article on controlling program flow. It is a bit dense, but for anyone who is interested, go check it out! And while you’re there, you might as well see the article on the switch statement as well.