In this tutorial, you will discover the main tools used for the developement of iOS native applications. Starting off with Objective-C, the programming language, and Xcode, the IDE. html
At the end of this tutorial, you will be able to : java
User | epfl.sdp2011@gmail.com |
Password | <ask the staff> |
It is normal if you don't understand everything after reading this example ! Things are explained one after the other in the next sections. node
#import <Foundation/Foundation.h> ios
#import <UIKit/UIKit.h> objective-c
#import "Person.h" xcode
@interface Car : NSObject { app
/* Instance variables */ less
//won't be accessible outside class, applies to all following variables ide
@private ui
long serialNumber;
NSString* modelName;
UIColor* paintColor;
Person* owner;
}
/* Properties */
//non thread-safe getters will be generated
@property (nonatomic, readonly) long serialNumber;
@property (nonatomic, readonly) NSString* modelName;
//non thread-safe getter and setter and will be generated. Setter will copy received color
@property (nonatomic, copy) UIColor* paintColor;
//non thread-safe getter and setter and will be generated. Setter will make a strong reference on received owner
@property (nonatomic, strong) Person* owner;
/* Public methods */
//Instance (-) custom constructor method
- (id)initWithSerialNumber:(long)serial modelName:(NSString*)model color:(UIColor*)color;
//Class (+) custom "convenient" constructor
+ (Car*)carWithSerialNumber:(long)serial modelName:(NSString*)model color:(UIColor*)color;
//Instance (-) method
- (void)printHelloWorld;
Car.m - class implementation
#import "Car.h"
@implementation Car
@synthesize serialNumber, paintColor, modelName, owner;
/* Instance (-) custom constructor method */
- (id)initWithSerialNumber:(long)serial modelName:(NSString*)model color:(UIColor*)color {
self = [super init]; //call to default super constructor
if (self) { //check that that construction did not return a nil object.
Equivalent to if (self != nil) { ... }
serialNumber = serial;
modelName = model;
self.paintColor = color; //Because of the "self." we are using here the setter that will copy the value
//we could also have done : paintColor = [color copy];
}
return self;
}
/* Class (+) custom "convenient" constructor */
+ (Car*)carWithSerialNumber:(long)serial modelName:(NSString*)model color:(UIColor*)color {
return [[self alloc] initWithSerialNumber:serial modelName:model color:color];
}
/* Instance method */
- (void)printHelloWorld {
NSLog(@"Hello, world !"); //NSlog(@"<string format>", arguments) is the equivalent to System.out.println(...)
}
/* Overriding description (equivalent to toString in JAVA) */
- (NSString*)description {
return [NSString stringWithFormat:@"<Car> serial number : %ld, model : %@, color : %@, owner : %@", serialNumber, modelName, paintColor, owner];
}
Use of Car class - example
UIColor* red = [UIColor redColor];
Car* car0 = [[Car alloc] initWithSerialNumber:12344 modelName:@"Clio" color:red];
/* OR SIMPLER : use convenient constructur we have defined : */
Car* car = [Car carWithSerialNumber:12345 modelName:@"Clio" color:red];
[car printHelloWorld]; //prints "Hello, world !"
NSLog(@"%@", car); //prints "<Car> serial number : 12345, model : Clio, color : UIDeviceRGBColorSpace 1 0 0 1, owner : (null)"
/* change car attributes */
UIColor* blue = [UIColor blueColor];
car.paintColor = blue; //Important : this line uses the setter ! This is completely equivalent to [car setPaintColor:blue]. Both are correct.
car.serialNumber = 234234; //ERROR : serial number is readonly
car->serialNumber = 234234; //ERROR : serial number is @private
What we can learn from this example :
Before diving into more details, here is a short overview of the 2 most useful collection types.
NSArray : represents an immutable array, on which objects pointers can be retrieved using an index integer.
NSMutableArray : mutable version of NSArray. NSMutableArray subclasses NSArray.
NSDictionary : represent an immutable key-value/object pointer dictionary (hash table).
NSMutableDictionary : mutable version of NSDictionary. NSMutableDictionary subclasses NSDictionary.
- (void)insertObject:(id)anObject atIndex:(NSUInteger)index
[array insertObject:myCar atIndex:3]
insertObject and atIndex are called labels.
Labels added to method type (instance (-) or class (+)) define the method. Two methods with the same labels cannot be declared together, even if their type differ.
The previous method minimal, exact and sufficient definition is actually : - insertObject:atIndex:
For example, these three methods are the same for the compiler and fail to compile when declared together :
- (void)insertObject:(id)anObject atIndex:(NSUInteger)index
- (void)insertObject:(Car*)anObject atIndex:(NSUInteger)index
- (void)insertObject:(Car*)aCar atIndex:(NSUInteger)index
The selector type is very important in Objective-C. It is a kind of pointer on a method. You'll see them very often in the documentation.
Imagine for example that you want to call the printHelloWorld method on a car instance with a delay and every 1 second. We use the class NSTimer class for that :
[NSTimer scheduledTimerWithTimeInterval:1.0 target:car selector:@selector(printHelloWorld)userInfo:nil repeats:YES];
We have created a selector by using @selector(<method_signature>).
Memory management
Until last year memory management is Objective-C was manual, using a system called reference counting.
But this management is now automatic since Xcode 4.2 that introduces automatic reference counting (ARC).
Basically, you can see ARC as a mechanism provided by the compiler that will automatically add in the code calls to methodrelease (free memory) on an object A whenever no more pointer are pointing to it.
Nonetheless, it does not prevent retain cycles.
A retain cycle is when to objects A and B have a reference to each other. Meaning A->B and B->A.
Imagine the following case:
Now, if A is freed from memory, it is no longer pointing to B. But as C is pointing to B and vice-versa, they will be both kept in memory.
problem typically appears when implementing a tree : chilldren nodes should not have a strong reference to their parents, so that releasing the root node releases the whole tree.
To prevent this problem, pointers can be strong (default) or weak.
Node* __weak parent;
In the previous Car class example, we have seen that @property is the clean way to generate getters and setters for instance variable.
Now that we have seen how the memory is managed, we can understand how they are defined :
@property(attributes) type name;
The attributes are divided into four groups and can take the following values :
1) Access type
name = @"A name";
/* The instance variable is accessed directly, without using the setter => ignores attributes mentioned above */
self.name = @"A name";
[self setName:@"A name"];
/* These two lines are completely equivalent. The setter is called. */
Property without instance variable : it is totally possible to have a property without a corresponding instance variable. In that case, it is necessary to access the variable using the self. notation.
Xcode is the main tool you will use to develop your app. It is the Eclipse for iOS development.
We will use Xcode 4 and iOS 5 SDK, which are installed on INF2 machines.
If you have a Mac on Mac OS X 10.7.4 or later ( -> About this Mac) you can download Xcode 4.5 with iOS 6 SDK for free from the Mac App Store.
@property (nonatomic) IBOutlet UILabel* label; // IBOutlet attribute makes this property visible from the interface builder tool
@property (nonatomic) IBOutlet UIButton* tapMeButton;
- (IBAction)buttonAction { //return type IBAction makes this method visible as action from the interface builder tool
self.label.text = [NSString stringWithFormat:@"Hello World ! Time : %ld", time(NULL)];
}