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.

Objects (Part 1): Splitting Classes Into Multiple Files


This is the first in a multi-part series that will talk about the fundamentals of objects. If you’re still keeping track, I suppose these would all fall under Lesson 6: Objects.

In Lesson 3, we began a simple overview to object-oriented programming. Now, we’ve covered the major portions of the basic language—what is “plain C.” There are other topics, such as structs, arrays, and a slew of other rather obscure topics. Some of these are enclosed or remade in the code the Apple provides for free; others will be discussed as necessary. For now, we will start venturing into the exciting world of objects—and they really are exciting.

As part of our calculator, we will allow the calculator to operate on regular values, as well as fractions. For the sake of demonstration, we will begin by creating Fraction objects.

Creating the Test Code

Create a new Xcode project. In the New Project window, choose “Application” under Mac OS X from the left side, then choose “Command Line Tool” from the top right, and make sure the “Type” is set to Foundation. Click Choose, and save it as “FractionDemo” anywhere you choose. In the File List, load FractionDemo.m, and enter the following code:

#import "Fraction.h"
#import 

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];

	// Display the value of myFraction
	NSLog(@"myFraction has a value of: ");
	[myFraction display];

	[myFraction release];
    [pool drain];
    return 0;
}

Here, you can see that in our main code body, we create an object of type Fraction, and call it myFraction. Ignore the asterisk (* for now; it is a sign that you are actually creating a pointer…that’s an advanced topic that will be further explored later). You set myFraction to the value returned from the standard alloc/init method call.

Next, we call the methods setNumerator: and setDenominator: and pass both integer values; these methods will set the appropriate values of the Fraction object we’ve created. We then send another message call for myFraction to display itself (or so we assume, based on the method name—make sure to write descriptive method names). Then we release our object—don’t worry about the memory management for now.

Now, we will create the actual Fraction object itself.

Creating the Fraction Class

In Xcode, Go to File > New File (⌘N). From the left, choose Cocoa Class under Mac OS X, select Objective-C class from the top left, and make it a Subclass of NSObject. Click Next, and set the following parameters:

  1. Set the File Name to be “Fraction.m”. Note that the “.m” part is automatically added; just type “Fraction”, without the quotes, making sure that the “F” is capitalized. By convention, class names are capitalized.
  2. Make sure that ‘Also create “Fraction.h”‘ is selected.
  3. Leave the default settings for the other fields. Add to Project should display “FractionDemo,” the name of your project, and in your Targets window, the checkbox to the left of the “FractionDemo” entry should be selected.

Click “Finish”. In your File List, you will see two new files: “Fraction.h” and “Fraction.m.” Select Fraction.h, and enter the following code:

#import <Foundation/Foundation.h>

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

@end

Here we define two instance variables, called “numerator” and “denominator”. They are defined as type NSInteger; this is identical to the standard int type; there’s really no difference. My personal preference is NSInteger.

Next, we define the methods that we call in the main() method. There should not be anything new here, if you’ve read through Lesson 3.

In Fraction.m, enter the following code:

#import "Fraction.h"

@implementation Fraction
- (void)setNumerator:(NSInteger)value {
	numerator = value;
}

- (void)setDenominator:(NSInteger)value {
	denominator = value;
}

- (void)display {
	NSString *numeratorString = [[NSString alloc] initWithFormat:@"%d", numerator];
	NSString *denominatorString = [[NSString alloc] initWithFormat:@"%d", denominator];
	NSLog(@"%@/%@", numeratorString, denominatorString);
	[denominatorString release];
	[numeratorString release];
}

@end

Chewing through this code, the first two methods should not present anything new. The last method does warrant some discussion, however.

If you refer back to Lesson 3, you’ll notice that we defined the display method (we called it showResults there) as

- (void)showResults {
	NSLog(@"This is a fraction with a value of %d/%d", numerator, denominator);
}

Our display method shows a different way to do this. First, we highlight NSString’s initWithFormat: method, which works like NSLog() does, except that instead of outputting, it is saved as a string. In the actual NSLog(), we use the %@ format specifier, which takes an NSString as an argument. Finally, we have to release the NSStrings that we’ve created…again, don’t worry about the memory management at the moment.

If we Build and Run now, we get the following output:

myFraction has a value of:
2/5

This is exactly what we’re looking for.

You’ll notice that we haven’t really done anything new since Lesson 3. In fact, that is true, except that we have created a Fraction class in separate files. Looking back over the code entries, the bold lines show where the dependencies lie—in a class’s .m file, it #imports the correspond .h (known as the ‘header’) file. In any file that uses the class you’ve created, you also need to import the header. Here, you’ve made it easier to maintain a larger project; as we add more classes, you won’t be working with one gigantic file.

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)! 😀

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.

Objective-C Lesson 4: if() statements and Booleans


As mentioned before, boolean values are simply true-or-false. In Objective-C, unlike many other languages, they are represented as YES or NO:

BOOL trueOrFalse = YES;
BOOL gameOver = NO;

Internally, however, they are stored as zero and one.

if() Statements

The if() statement is used to check for conditions. Just like we use if in normal English, if() in code is used to test for a condition—they test for the value of a boolean (or any int—in this case, a zero is considered false; any non-zero value is true).

Here is a simple example of booleans:

#import <Foundation/Foundation.h>

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

	BOOL trueOrFalse = YES;
	if (trueOrFalse)
		NSLog(@"trueOrFalse is true.");
	if (1)
		NSLog(@"1 is considered to be true.");
    [pool drain];
    return 0;
}

The output is:

trueOrFalse is true.
1 is considered to be true.

Simple enough, and quite logical.

Obviously, if the condition was false, the statements following would not be executed. The following example demonstrates:

#import <Foundation/Foundation.h>

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

	BOOL trueOrFalse = YES;
	if (trueOrFalse)
		NSLog(@"trueOrFalse is true.");
	if (1)
		NSLog(@"1 is considered to be true.");	// If false, statement following is not executed
	trueOrFalse = NO;
	if (trueOrFalse)
		NSLog(@"I lied. trueOrFalse is not true.");
	if (0)
		NSLog(@"This line should not appear.");
    [pool drain];
    return 0;
}

Unsurprisingly, the results are the same as above.

One important point to make here: if you only have one statement after the if(), you can just leave it like it is above. But if you have more than one statement after the if(), you must enclose them within braces:

if (trueOrFalse) {
	NSLog(@"trueOrFalse is true.");
	NSLog(@"This second line must be within the braces.");
}

Otherwise, the second (or additional) statements will be executed, regardless of if the condition was true or not.

Extending the if() Statement

In real life though, there are often alternatives: if something is true, do “action1“; else, do “action2“. Objective-C lets you model that quite simply:

trueOrFalse = YES;
if (trueOrFalse == YES)          // The double-equals sign is a comparison; versus a single equals, which is an assignment. More on this in the next Extension.
	NSLog(@"If true, print this");     // This gets printed
else
	NSLog(@"Else, print this");        // This does not get printed
trueOrFalse = NO;
if (trueOrFalse)
	NSLog(@"If true, print this");    // This does not get printed
else
	NSLog(@"Else, print this");       // This gets printed
 

Output is:

If true, print this
Else, print this

This makes logical sense.

You can also extend this by using else if(). This is easier to explain with code:

int value = 5;
	if (value > 0)
		NSLog(@"value is greater than zero.");
	else if (value == 0)
		NSLog(@"value is equal to zero.");
	else
		NSLog(@"value is less than zero.");

Output is:

value is greater than zero.

You can have as many else if()s as you want; they simply follow each other:

int value = 5;
if (value == 6)
	NSLog(@"value is equal to 6.");
else if (value == 0)
	NSLog(@"value is equal to 0.");
else if (value == 2)
	NSLog(@"value is equal to 2.");
else if (value == 10)
	NSLog(@"value is equal to 10.");
else
	NSLog(@"value does not equal 6, 0, 2, or 10");     // This line is the output.

Pitfalls

There are a few issues that may arise with if()statements.

  • 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.
  • Using too many instances of if(): If you want to have a collection of related paths (if…else if…else), you must remember to use else if(). Using a chain of if()s is a completely different thing, logically. Think about it.
  • Forgetting the last else: The final else is a “catchall” statement that is executed if none of the previous if() or else if() statements are true. Don’t forget the else; otherwise, you may never get any output. For example, in the last code example, the last else statement was needed; otherwise, it might have appeared that the code was broken.

Conclusion

As you can see, the if() statement is quite simple, but very powerful—it defines “paths” down which your code can travel, based on the value of a condition. In the next Extension, we will be looking at what these conditions can be.

Oddities: Conditional Operator


The conditional operator is rather strange, but is very useful. (By the way, an operator is something that acts upon two or more values, such as the addition ‘+’ operator.) In many cases (or at least frequent enough for them to make up a new operator), programmers have had to determine the largest or the smallest of two values, and use the corresponding (largest or smallest) value. Normally, the code would be like this:

if (number1 > number2)
     return number1;
else
     return number2;

This became cumbersome. Introducing, the conditional operator:

return (number1 > number2) ? number1 : number2;

This does the same thing as the code above. The parts in bold are the required syntax; any valid expression, constant, or variable can be placed in between.

There are three parts to the conditional operator: the condition, and the two “paths”.

The condition is a boolean condition, one that would otherwise trigger an if-statement. If that is true, number1 is returned; otherwise number2 is returned.

That’s all there is to it.

Extension 3: ASCII, Booleans, Characters


Type char

A variable of type char is used to store a single character. It is denoted as a character, flanked by two single quotes. (Be careful—single quotes are used for characters, double quotes signify a string.) ‘a’, ‘;’, and ‘8’ all represent character types. The first is simply the letter a. The second is the semicolon symbol; it is not the same as the one used to mark the end of a line. The last example is the character 8, not the integer 8.

This Photograph is created using only the ASCI...

ASCII Art—Image via Wikipedia

Escape sequences, when enclosed in single quotes, are also considered to be char, even though they are composed of more than one symbol.

To print a character or escape sequence through NSLog, use %c.

ASCII Values

In C, chars are represented as 8-bit ASCII values. Internally, characters are stored as numbers, with a range from 0-127. This makes for interesting uses, including the following simple cypher program:

#import 

int main (int argc, const char * argv[]) {
    NSAutoreleasePool * pool = [[NSAutoreleasePool alloc] init];    // A simple cypher program
    NSLog(@"Hello");	// Original message
	NSLog(@"%c%c%c%c%c", ('H' + 1), ('e' + 1), ('l' + 1), ('l' + 1), ('o' + 1));		// Cypher 1
	NSLog(@"%c%c%c%c%c", (('H' * 2) % 127), (('e' * 2) % 127), (('l' * 2) % 127), (('l' * 2) % 127), (('o' * 2) % 127));	 /* Cypher 2...
																															 note the use of the modulus operator
																															 to limit the domain */
	[pool drain];
    return 0;
}

The output of this program is:


Hello
Ifmmp
 KYY_

One thing to note before we analyze this program—the last output only displays four characters because the ASCII value of capital H is 72. The operation results in an ASCII value of 17, which is a “special”, non-printing character (see Wikipedia link above).

Code, Demystified

The first important line prints out the NSString constant @”Hello”. Not much to it. The next line prints out five characters, as represented by the escape sequences. Looking at the arguments to NSLog, we see that we are simply taking the characters in @”Hello” and adding one to their ASCII values. If you check the output, you’ll find that Ifmmp are just the letters of Hello, but shifted “over” by one. Note that ASCII values define all capitals before lowercase, not all letters in alphabetical order; the letters A-Z come numerically before a-z.

We do the same thing on the Cypher 2 line, except that this time there is a little bit more involved. We take the ASCII value and multiply it by 2. To keep our code neat and prevent warnings, we use the modulus operator to limit the range of the result—in this case, the result can only be between 0 and 127, exactly the range of ASCII. This is another practical use of the modulus operator—to limit the range of a result to a specific set of numbers.

Note that when you write chars in your code, you must delineate them with single quotes. Those quotes do not get printed in the final output.

Type BOOL

Boolean values are very simple—they are literally a YES or NO. In other languages, including Java and plain C, they are represented as true or false. Although initially the latter may seem more reasonable, in usage the former makes more grammatical sense. For example, in the following method call,

[self presentModalViewController:mvc animated:YES];

using the value YES makes much more sense from a reader’s perspective than true.

Booleans are simple true or false values; they are usually used to determine if a condition is true or not, and used to determine whether some code should be executed (for() statements). In Objective-C, the integer values 0 and 1 are synonymous, and can be used interchangeably (with a few exceptions). Their usage will be detailed in the next Lesson.

Extension 2: Floating-Point Operations


Not all numbers are integers. Therefore, Objective-C lets you define floating-point values—numbers with a fractional portion. There are two basic types—float and double.

Floating-point values do not follow the rules of integer division—that is, dividing by floating-point values produces floating-point results.

Type float

In certain programming languages (Java comes to mind) the float type is almost never used. In Objective-C, it is the more commonly used of the two—both for practical and memory reasons.

A floating point number must contain a decimal portion, but you can omit digits before or after the decimal point—obviously, not both. The entire number ca be prefixed by a negative sign. Therefore, 3., 1.8, .295, and -.59 are all valid floating point numbers. To display floats in an NSLog call, use %f.

Scientific Notation

As you may recall from a high-school math class, scientific notation is a method of writing absurdly large or small numbers. It takes the form 5.925×102, where the general notation is of a floating-point value followed by a multiplication, a number (generally a power of 10), and an exponent. This number is written in code with the form 5.925e4. The e, formally known as the mantissa, can be written as a capital or lowercase. The mantissa can be either positive or negative; a negative value, such as 2.25e-3, would correspond to a value of 2.25×10-3, or 0.00225.

To display scientific notation, use %e. Alternatively, you can use %g to have NSLog decide whether to display the usual value or the scientific notation—if the exponent is less than -4 or greater than 5, the scientific notation is used; otherwise, the standard floating point notation is used.

Type double

A double value is a more precise float value—the former stores twice as many digits, and on most systems it uses 64 bits.

Like Java, all floating point constants in Objective-C are double. To force a float, append either f or F to the end of the floating point value. Unlike Java, however, floats are used as a general data type for floating-point variables, due to the fact that they require less memory. The distinction is that of constants versus that of variables.

The same format specifiers apply to doubles, as well as scientific notation.

Extension 1: Integer Operations


Extensions provide a more in-depth overview of various topics. Here, I present a more in-depth discussion behind primitive data types.

Type int

As mentioned before, integers consist of individual digits. You can prefix it with a minus sign (a hyphen) to make it negative. You cannot put spaces, commas, or periods inside ints.

When dividing an integer by another, integer division occurs. In this case, the remainder of the division is dropped. Therefore, the result of 8/5 is 1, not 1.6. A practical use of this is as follows:

int number = 1234;
int thousands = number / 1000;
NSLog ("@The thousands place of %d is %d.", number, thousands);

The output of the code above is

The thousands place of 1234 is 1.

In contrast, the modulus operator (%) is used to derive the exact remainder of integer division. Therefore, 8 % 5 = 3.

Extending the above example,

int number = 1234;
int thousands = number / 1000;
NSLog ("@The thousands place of %d is %d.", number, thousands);
int hundreds = (number % 1000) / 100;
NSLog ("@The hundreds place of %d is %d.", number, hundreds);
int tens = ((number % 1000) % 100) / 10;
NSLog (@"The tens place of %d is %d.", number, tens);
int ones = (((number % 1000) % 100) % 10);
NSLog (@"The ones place of %d is %d.", number, ones);

The output of the code is

The thousands place of 1234 is 1.
The hundreds place of 1234 is 2.
The tens place of 1234 is 3.
The ones place of 1234 is 4.

The code above is rather involved, but try to figure it out. Remember, the modulus operator extracts the remainder of the division that could have been placed in its place.

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