Handy Little Tool for Errors


During the development of any application, there will be times when you have to track errors in your code. Having the errors be clearly expressed in the Console or in an alert will help with the debugging. As an example, rather than letting your application crash with an exception, it might make sense to log a clear error and defer the fix to a later time.

Because of this, I’ve made a simple little macro that formats the error in a nate way. This is the macro:

#define LOCATION_AS_NSSTRING [NSString stringWithFormat:@"Function: %s\nLine: %d\nFile: %s", __func__, __LINE__, __FILE__]
#define STRING_REPRESENTATION_OF_ERROR(x) [NSString stringWithFormat:@"\n==== ERROR ====\nError %d: %@\nSeverity: %@\n%@", x, x ## _STRING, x ## _SEVERITY, LOCATION_AS_NSSTRING]

It is actually two macros; the former is a “helper” macro and you interact with the latter. The macro works with three other preprocessor declarations. It’ll make the most sense to put this in context:

/******* Severity Levels ******/
#define SEVERITY_NO_ACTION @"No action required"
#define SEVERITY_LOW @"Low"	// Minimal user action to fix OR cannot fix, but doesn't severly affect usability
#define SEVERITY_HIGH @"High"	// Extensive user action to fix OR cannot fix, and affects usability somewhat
#define SEVERITY_CRITICAL @"Critical"	// Crashers; cannot recover from issue
#define SEVERITY_UNKNOWN @"Unknown"	// Not enough information to decide

#define ERROR_CODE_1 1	// No sections defined for library
#define ERROR_CODE_1_STRING @"No sections defined for library browser. Returning 1"
#define ERROR_CODE_1_SEVERITY SEVERITY_NO_ACTION

// … Some code later …
NSLog(@"%@", STRING_REPRESENTATION_OF_ERROR(ERROR_CODE_1));

This code example will output the following:

==== ERROR ====
Error 1: No sections defined for library browser. Returning 1
Severity: No action required
Function: <ContainingMethod>
Line: <SomeLineNumberOfCalling>
File: <ContainingFile>

So how does this work? It’s actually rather simple. You pass in the error code listing, and the preprocessor “assigns” this value to the “variable” x. Then it uses that variable in the standard NSString method call. It first “plugs in” the value of x, then combines the full token (remember that you pass in the entire token, ERROR_CODE_1, so that gets combined and forms ERROR_CODE_1 ## _STRING, which gets joined into ERROR_CODE_1_STRING). It then does the same thing with _SEVERITY token, resulting in ERROR_CODE_1_SEVERITY. Finally, it plugs in the results from the other macro, LOCATION_AS_NSSTRING, which in turn calls three built-in macros (__func__, __LINE__, and __FILE__), which return the calling function, line, and file is C-style strings.

So really, it’s quite simple. There are just a lot of macros involved, and it really becomes quite useful. As an example, you could use this to show a meaningful error to users with an alert, if necessary.

Extension 11: Conditional Compilation


Building off of last week’s Extension, there are a few more preprocessor directives that allow you to control which portions of a program are compiled.

The #ifdef, #endif, #else, and #ifndef Statements

Sometimes (and hopefully not often), a program relies on system- or platform-specific features that must be written differently across different platforms. For example, not all iOS devices can run iOS 4.x, so any program that uses 4.x features must check to see if the system can support that feature. Or an application for OS X can check for the architecture of the system, PowerPC or Intel, or the version of the OS.

The The #ifdef, #endif, and #else directives work just like the The #ifdef, #endif, #elseif…else construct; the #endif directive is used to end an #ifdef block, just like a curly brace ( } ) ends an if block.

Note that a definition such as

#define POWER_PC

is sufficient to pass the #ifdef test; no value actually has to be specified for the constant.

So how does it work?

#ifdef MAC_OS_X
#	define DATADIR ~/Documents
#else
	NSLog(@"Not running in OS X");
#endif

This can also be used for debugging:

#ifdef DEBUG
	NSLog(@"Data array is %@", self.dataArray);
#endif

This makes the code (your debugging intentions) clearer. When the program is working properly, you can undefine DEBUG and the NSLogs will not even be compiled in.

The #ifndef statement is the exact opposite of #ifdef—the code that follows gets executed only if the value is not defined.

The #if and #elif Directives

These directives are similar to if()… and else if()…. For example, the following appears in NSString.h:

#if MAC_OS_X_VERSION_MIN_REQUIRED < MAC_OS_X_VERSION_10_5
#define NSMaximumStringLength     (INT_MAX - 1)
#endif

The example is rather self explanatory.

The operator defined (something) can also be used in #if statements:

#if defined(DEBUG)
// Is equivalent to
#ifdef DEBUG

A common use of this is #if defined (DEBUG) && DEBUG, which will be true if DEBUG has been defined to a non-zero value.

The #undef Operator

The #undef operator undefines a name:

#undef DEBUG

Subsequent #ifdef DEBUG and #if defined(DEBUG) statements evaluate to false.


Determining iPads

Conditional compilation can be useful in determining if you’re running on an iPad or not. Apple provides a UI_USER_INTERFACE_IDIOM() macro that can perform the check for you:

if (UI_USER_INTERFACE_IDIOM() == UIUserInterfaceIdiomPad)
    //iPad code
else
    //iPhone code

If you have extensive code changes or want to load a different set of resources for example, the iPad code won’t ever get compiled for the iPhone and vice versa, which could simplify the resulting binary (remember, only apps under 20MBs can be downloaded off a 3G connection. You want to make your app as small as reasonably possible).

Extension 8: Methods (in Detail)


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

Message Syntax

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

[receiver message];

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

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

Arguments

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

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

[myFraction setNumerator:2];

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

- (void)setNumerator:(int)value;

In general, methods with arguments are declared like this:

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

Method Declaration Syntax

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

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

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

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

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

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

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

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

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

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

Messages to nil

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

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

NSString *s;

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

NSString *s = nil;

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

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

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

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

A few rules apply here:

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

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

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

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

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

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

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.

Shameless Self-Promotion


To get the word out, I’d generally rely on search engines. And I can’t afford to pay Google to get listed (and most of those ads aren’t exactly reputable anyway). So, here’s some shameless self promotion. I promise I won’t really do this much more. Feel free to not read this. You may want to entertain yourself elsewhere (I’d recommend Uncyclopedia).


Cups of Cocoa Cups of Cocoa Cups of Cocoa Cups of Cocoa Cups of Cocoa Cups of Cocoa Cups of Cocoa Cups of Cocoa

iPhone App Development iPhone App Development iPhone App Development iPhone App Development

iPhone Application Development iPhone Application Development iPhone Application Development

Programming for iOS Programming for iOS Programming for iOS Programming for iOS Programming for iOS

iPhone App iPhone App iPhone App iPhone App iPhone App iPhone App iPhone App iPhone App iPhone App

iPhone Apps iPhone Apps iPhone Apps iPhone Apps iPhone Apps iPhone Apps iPhone Apps iPhone Apps

Creating iPhone Apps Creating iPhone Apps Creating iPhone Apps Creating iPhone Apps Creating iPhone Apps

iPhone Programming iPhone Programming iPhone Programming iPhone Programming iPhone Programming iPhone Programming

Developer Developer Developer Developer Developer Developer Developer Developer Developer Developer Developer

iOS iOS iOS iOS iOS iOS iOS iOS iOS iOS iOS iOS iOS iOS iOS iOS iOS iOS iOS iOS iOS iOS iOS iOS

App Store App Store App Store App Store App Store App Store App Store App Store App Store App Store App Store

Programming Programming Programming Programming Programming Programming Programming

iPhone iPhone iPhone iPhone iPhone iPhone iPhone iPhone iPhone iPhone iPhone iPhone iPhone

iPod Touch iPod Touch iPod Touch iPod Touch iPod Touch iPod Touch iPod Touch iPod Touch

iPad iPad iPad iPad iPad iPad iPad iPad iPad iPad iPad iPad iPad iPad iPad iPad iPad iPad iPad iPad

Xcode Xcode Xcode Xcode Xcode Xcode Xcode Xcode Xcode Xcode Xcode Xcode Xcode Xcode Xcode

Objective-C Objective-C Objective-C Objective-C Objective-C Objective-C Objective-C Objective-C

Apple Apple Apple Apple Apple Apple Apple Apple Apple Apple Apple Apple Apple Apple Apple Apple

Cocoa Cocoa Cocoa Cocoa Cocoa Cocoa Cocoa Cocoa Cocoa Cocoa Cocoa Cocoa Cocoa Cocoa Cocoa

iPhone SDK iPhone SDK iPhone SDK iPhone SDK iPhone SDK iPhone SDK iPhone SDK iPhone SDK

Software Software Software Software Software Software Software Software Software Software Software

Object-Oriented Programming Object-Oriented Programming Object-Oriented Programming Object-Oriented Programming


😀

Escape Sequences


NSLog, and its corresponding C function printf(), use escape sequences to print certain characters that are “invisible.” For iOS, include:

Sequence Meaning
\b Backspace
\f Form Feed
\n Newline
\t Horizontal Tab
\v Vertical Tab
\\ Backslash
\’ Single Quote
\” Double Quote
\? Question Mark

There are others, such as \a for Alert, but they are rarely used, and don’t make an effect.

Reserved Keywords


Objective-C reserves certain words, so that you can’t, or shouldn’t, use them in your own code. At best, you’ll get a compiler warning or error; at worst, you’ll get a nasty bug that you won’t discover until millions of people are already using your app. Here is a list of reserved keywords, to help you avoid that pitfall.

Words You Can’t Use Words You Shouldn’t Use
auto BOOL
break Class
case bycopy
char byref
const id
continue IMP
default in
do inout
double nil
else NO
enum NULL
extern oneway
float out
for Protocol
goto SEL
if self
inline super
int YES
long @interface
register @end
restrict @implementation
return @protocol
short @class
signed @public
sizeof @protected
static @private
struct @property
switch @try
typedef @throw
union @catch()
unsigned @finally
void @synthesize
volatile @dynamic
while @selector
_Bool atomic
_Complex nonatomic
_Imaginery retain

How Programming Languages Work—A Brief Overview


Warning: Extremely technical information follows. Feel free to skip this section for now, but make sure you do read it sometime. It’s important enough to know, but not vital for you to begin programming.

A computer only understands a string of commands in a very basic format—machine language. At the lowest level, a computer can only process 0s and 1s—off, or on. In effect, computer is just a huge collection of billions of switches, rapidly turning on and off. A typical command that the computer understands may look like this:

00101011 11110111 10000100 01010110 00000110 10001001

Of course, having to program like this would not be very easy, and it is easy to loose track of what you’re doing. To fix this, chip makers created what is called assembly language—a language that chips understand, and will get translated to machine language, independently of the programmer. A typical command in assembly might look like this:

l_objc_msgSend_fixup_alloc:
.quad	_objc_msgSend_fixup
.ascii "long int"
.byte	0x4
.byte	0x8
.byte	0x7
.byte	0x2
.byte	0x1
.byte	0x6
.ascii "char"

Better than machine language, but still not too useful. In addition, the programmer is still interacting directly with the hardware, which increases the chances of mistakes.

Both machine language and assembly are specific to the hardware. Different hardware involves different machine and assembly code.

Higher-level languages were invented to solve of these problems. The “height” of these languages varies, but they all involve English-like syntax, and are hardware-independent. Objective-C is one such language.

The majority of these languages are passed through a compiler—a program that converts the code into assembly. Compilers are usually provided with a system; Xcode, Apple’s native development program, includes gcc, one of the most prominent compilers, as well as LLVM, an Apple-developed compiler that offers many benefits over gcc, all of which are beyond the scope of this book.

Higher-level languages allow programmers to focus on their task, and not on writing obscure code.

For anyone who’s interested in learning more, an even more technical and low-level approach will be coming. When? I don’t know.

  • 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

    July 2020
    S M T W T F S
     1234
    567891011
    12131415161718
    19202122232425
    262728293031  
  • Time Machine

  • You count!

    • 622,733 views
  • Worldwide Stats

    free counters
%d bloggers like this: