Lecture #2: Coding in Objective

advertisement
CSCI 251:
iPhone Application
Development
Spring Term 2012
Lecture #2: Coding in
Objective-C
(Chapter 4)
Thursday, May 10, 12
Declaring and Defining
Classes
In Python and Java, all code for a class goes
into the same file:
class Circle(Shape):
class Circle extends Shape {
double cx, cy; // center
double r;
// radius
def __init__(self, cx, cy, r):
self.cx = cx # center X
self.cy = cy # center Y
self.r = r
# radius
public void move(double dx, double dy) {
def move(self, dx, dy):
self.cx += dx
self.cy += dy
Thursday, May 10, 12
}
}
cx += dx;
cy += dy;
Declaring and Defining
Classes
In Objective-C, we put the declarations into
an interface file, with extension .h
// ImAwesome.h
#import <UIKit/UIKit.h>
@interface ImAwesomeViewController : UIViewController {
! UILabel!*label;
! UIButton *button;
}
@property (nonatomic, retain) IBOutlet UILabel *label;
-(IBAction)sayHello:(id) sender;
@end
Thursday, May 10, 12
The implementation code goes into a file
with the extention .m
//
ImAwesome.m
#import "ImAwesomeViewController.h"
@implementation ImAwesomeViewController
@synthesize label;
-(IBAction) sayHello:(id) sender {
!
! label.text = @"M**********r I’m awesome"; // No you’re not dude, don’t lie
}
- (void)didReceiveMemoryWarning {
! // Releases the view if it doesn't have a superview.
[super didReceiveMemoryWarning];
!
! // Release any cached data, images, etc that aren't in use.
}
- (void)viewDidUnload {
! // Release any retained subviews of the main view.
! // e.g. self.myOutlet = nil;
}
- (void)dealloc {
[label release];
[button release];
[super dealloc];
}
@end
Thursday, May 10, 12
Good News & Bad
News
• The Bad News: this is all very confusing
• C/C++ programmers: confusing
• Java programmers: very confusing
• Python programmers: WTF?!
Thursday, May 10, 12
Good News & Bad
News
• The Good News
• Xcode writes most of the code for
you, “automagically”
• We will break it down piece-by-piece,
through a “close reading” of the
ImAwesome app.
Thursday, May 10, 12
import
The import statement (like Java's import)
tells the compiler to include a file in yours:
//
ImAwesomeViewController.m
#import "ImAwesomeViewController.h"
// ImAwesomeViewController.h
#import <UIKit/UIKit.h>
Thursday, May 10, 12
import
The import statement (like Java's import)
tells the compiler to include a file in yours:
//
ImAwesomeViewController.m
#import "ImAwesomeViewController.h"
// ImAwesomeViewController.h
#import <UIKit/UIKit.h>
Thursday, May 10, 12
quotes are for your code in
your project
import
The import statement (like Java's import)
tells the compiler to include a file in yours:
//
ImAwesomeViewController.m
#import "ImAwesomeViewController.h"
// ImAwesomeViewController.h
#import <UIKit/UIKit.h>
angle brackets are for Apple's
toolkit code
Thursday, May 10, 12
Pointers
•
•
In Python and Java, a variable can be used to
store either a primitive type or an object:
n = 5
int n = 5;
pi = 3.14159
double pi = 3.14159;
c = Circle(100,75, 10)
Circle c = new Circle(100,75, 10);
How does this actually work?
Thursday, May 10, 12
Pointers
Primitive types are stored directly in
memory:
Address
0
1
2
3
4
Thursday, May 10, 12
11010001011010000011010101110101
00000000000000000000000000000101
00000000000000000000001111110100
00000000000000000000000000000000
11110000101000110111010011010101
n = 5
Pointers
Objects are stored as pointers to other
memory locations:
Address
0
1
2
3
4
11010001011010000011010101110101
00000000000000000000000000000101
00000000000000000000001111110100
00000000000000000000000000000000
11110000101000110111010011010101
1012
1013
00100100100011100110100010010101
1014
00000000000001111111110101010101
01
..
.
..
.
Thursday, May 10, 12
..
.
11111111110000000000000111111010
..
.
n = 5
c = Circle(100,75, 10)
Pointers
Objective-C requires us to declare pointers
explicitly, using the * notation:
! UILabel!*label;
! UIButton *button;
Thursday, May 10, 12
Properties
•
When memory is tight and performance
crucial, we can help the OS avoid problems
by declaring how an object is going to be
used; i.e., its properties:
@property (nonatomic, retain) IBOutlet UILabel *label;
•
Let's break these down....
Thursday, May 10, 12
(non)atomic
@property (nonatomic, retain) IBOutlet UILabel *label;
•
Atomic: from Greek α-τοµ-, “indivisible” (ironic,
huh?)
•
atomic declaration (the default) makes variable
access “robust in multithreaded environments”;
i.e., one operation on a variable must be allowed
to complete before another can start.
•
But this slows things down; so we declare variables
nonatomic
Thursday, May 10, 12
retain
@property (nonatomic, retain) IBOutlet UILabel *label;
•
retain tells the compiler how memory should be
managed for this object
•
We will return to this topic shortly ....
Thursday, May 10, 12
IBOutlet
@property (nonatomic, retain) IBOutlet UILabel *label;
IBOutlet tells the compiler that this object will
be an Interface Builder outlet; i.e., something that
we will be able to see and connect to in IB, once
we’ve compiled the code.
Thursday, May 10, 12
Method Declaration
-(IBAction)sayHello:(id) sender;
Thursday, May 10, 12
-(IBAction)sayHello:(id) sender;
Thursday, May 10, 12
•
The - (minus sign) means that this
method is an instance method: it has access
to the class’s instance variables.
•
Use of a + (plus sign) would indicate a
class method: no access to the class’s
instance variables (like static methods in
Java).
-(IBAction)sayHello:(id) sender;
Return type
Thursday, May 10, 12
-(IBAction)sayHello:(id) sender;
Method name
Thursday, May 10, 12
-(IBAction)sayHello:(id) sender;
•
•
Thursday, May 10, 12
Parameter type
Here, it is polymorphic
(unspecified). The idea is that
different types of objects can send
a sayHello message to the
ViewController.
-(IBAction)sayHello:(id) sender;
Parameter name
Thursday, May 10, 12
// ImAwesomeViewController.h
#import <UIKit/UIKit.h>
@interface ImAwesomeViewController : UIViewController {
! UILabel!*label;
! UIButton *button;
}
@property (nonatomic, retain) IBOutlet UILabel *label;
-(IBAction)sayHello:(id) sender;
@end
Thursday, May 10, 12
Implementation Details
//
ImAwesomeViewController.m
#import "ImAwesomeViewController.h"
@implementation ImAwesomeViewController
@synthesize label;
-(IBAction) sayHello:(id) sender {
!
! label.text = @"ImAwesome";
}
- (void)didReceiveMemoryWarning {
! // Releases the view if it doesn't have a superview.
[super didReceiveMemoryWarning];
!
! // Release any cached data, images, etc that aren't in use.
}
- (void)viewDidUnload {
! // Release any retained subviews of the main view.
! // e.g. self.myOutlet = nil;
}
- (void)dealloc {
[label release];
[button release];
[super dealloc];
}
@end
Thursday, May 10, 12
//
ImAwesomeViewController.m
#import "ImAwesomeViewController.h"
@implementation ImAwesomeViewController
@synthesize label;
-(IBAction) sayHello:(id) sender {
!
! label.text = @"ImAwesome";
}
- (void)didReceiveMemoryWarning {
! // Releases the view if it doesn't have a superview.
[super didReceiveMemoryWarning];
!
! // Release any cached data, images, etc that aren't in use.
}
- (void)viewDidUnload {
! // Release any retained subviews of the main view.
! // e.g. self.myOutlet = nil;
}
- (void)dealloc {
[label release];
[button release];
[super dealloc];
}
@end
Thursday, May 10, 12
@synthesize
•
The @synthesize declaration in the
implementation is the partner of the
@property declaration in the interface
•
@synthesize tells the compiler to
generate accessor methods automagically,
according to what you specified in the
@property declaration
•
Thursday, May 10, 12
This seems to go on behind the scenes, so
we can just ignore it for now
//
ImAwesomeViewController.m
#import "ImAwesomeViewController.h"
@implementation ImAwesomeViewController
@synthesize label;
-(IBAction) sayHello:(id) sender {
!
! label.text = @"ImAwesome";
}
- (void)didReceiveMemoryWarning {
! // Releases the view if it doesn't have a superview.
[super didReceiveMemoryWarning];
!
! // Release any cached data, images, etc that aren't in use.
}
- (void)viewDidUnload {
! // Release any retained subviews of the main view.
! // e.g. self.myOutlet = nil;
}
- (void)dealloc {
[label release];
[button release];
[super dealloc];
}
@end
Thursday, May 10, 12
!
! label.text = @"ImAwesome";
•
•
This looks pretty much like Java or Python
Equivalent to
! [label setText:@"ImAwesome"];
Thursday, May 10, 12
//
ImAwesomeViewController.m
#import "ImAwesomeViewController.h"
@implementation ImAwesomeViewController
@synthesize label;
-(IBAction) sayHello:(id) sender {
!
! label.text = @"ImAwesome";
}
- (void)didReceiveMemoryWarning {
! // Releases the view if it doesn't have a superview.
[super didReceiveMemoryWarning];
!
! // Release any cached data, images, etc that aren't in use.
}
- (void)viewDidUnload {
! // Release any retained subviews of the main view.
! // e.g. self.myOutlet = nil;
}
- (void)dealloc {
[label release];
[button release];
[super dealloc];
}
@end
Thursday, May 10, 12
Memory Management
(Traditional)
- (void)dealloc {
[label release];
[button release];
[super dealloc];
}
Every class you write must have a dealloc
method, which
1. releases (calls release on) the object’s
instance variables
2. calls the superclass’s dealloc method
Thursday, May 10, 12
The alloc ...
and release Cycle
•
•
Each object is associated with a reference count.
•
When the object is released with release,
When the object is created with alloc, the new
object’s reference count is set to 1.
•
•
Thursday, May 10, 12
the reference count is decremented by 1, and
if the reference count is 0, the object’s dealloc
method is called automatically
The alloc ...
and release Cycle
1
alloc
+1
Thursday, May 10, 12
0
release
-1
The alloc ...
and release Cycle
•
•
Thursday, May 10, 12
It’s actually a little more complicated than that
What if we want to
•
Retain an object beyond the duration of the
class that created it?
•
Copy the contents of one object to another?
#import <Foundation/NSObject.h>
#import <Foundation/NSString.h>
@interface AddressCard: NSObject {
NSString *first;
NSString *last;
NSString *email;
}
// ...
-(void) setFirst: (NSString*) f;
-(void) setLast: (NSString*) l;
@end
Thursday, May 10, 12
http://www.otierney.net/objective-c.html#retain
#import "AddressCard.h"
@implementation AddressCard
-(void) setFirst: (NSString*) f {
[f retain];
[first release];
first = f;
}
http://www.otierney.net/objective-c.html#retain
Thursday, May 10, 12
The alloc, retain,
copy, and release Cycle
•
•
•
•
Each object is associated with a reference count.
When the object is created with alloc (or a new
copy is made with copy) the new object’s
reference count is set to 1.
When retain is called on the object, its reference
count is incremented by 1.
When the object is released with release,
• the reference count is decremented by 1, and
• if the reference count is 0, the object’s dealloc
method is called automatically
Thursday, May 10, 12
The alloc, retain,
copy, and release Cycle
1
alloc
+1
Thursday, May 10, 12
2
1
0
retain
+1
release
-1
release
-1
The Autorelease Pool
Sometimes we want to create an object (e.g. string)
without using alloc:
• Constant objects:
NSString *msg = @"I talk to myself on my Facebook wall";
• Objects created by class methods (c.f. Java
"factory methods"):
NSString *msg =
[NSString stringWithString : @"I'm out of breath"];
Thursday, May 10, 12
The Autorelease Pool
•
Storage for such objects is managed behind-thescenes by the autorelease pool
•
XCode creates this for you in your main:
// Older version
int main(int argc, char *argv[]) {
}
NSAutoreleasePool * pool = [[NSAutoreleasePool alloc] init];
int retVal = UIApplicationMain(argc, argv, nil, nil);
[pool release];
return retVal;
// Current version
int main(int argc, char *argv[])
{
@autoreleasepool {
return UIApplicationMain(argc, argv, nil,
NSStringFromClass([BMAppDelegate class]));
}
}
Thursday, May 10, 12
Other Times to Release Memory
//
ImAwesomeViewController.m
#import "ImAwesomeViewController.h"
@implementation ImAwesomeViewController
@synthesize label;
-(IBAction) sayHello:(id) sender {
!
! label.text = @"ImAwesome";
}
- (void)didReceiveMemoryWarning {
! // Releases the view if it doesn't have a superview.
[super didReceiveMemoryWarning];
!
! // Release any cached data, images, etc that aren't in use.
}
- (void)viewDidUnload {
! // Release any retained subviews of the main view.
! // e.g. self.myOutlet = nil;
}
- (void)dealloc {
[label release];
[button release];
[super dealloc];
}
@end
Thursday, May 10, 12
Automatic Reference Counting
• Makes ObjectiveC more like a garbage-collected
(GC) language (Java, Python)
• Without ARC, XCode adds explicit retain /
release / autorelease statements
automatically, and you are expected to add more
as needed.
• With ARC, these operations take place behind-the
scenes: sufficient for most apps
• Conclusion: Unless you need to manage very large
data objects explicitly, use ARC!
Thursday, May 10, 12
Automatic Reference Counting
Thursday, May 10, 12
Delegates
• In ordinary language, a delegate is someone
you assign to do a certain set of tasks for you.
• In Cocoa, a delegate is a class containing
methods that perform useful tasks for another
class: typically, handling application events
(window launch, etc.)
• Typically, a delegate implements a set of
protocols (like Java's interface mechanism)
Thursday, May 10, 12
Delegates
//
HelloWorldAppDelegate.h
#import <UIKit/UIKit.h>
@class HelloWorldViewController;
@interface HelloWorldAppDelegate : NSObject
<UIApplicationDelegate> {
UIWindow *window;
HelloWorldViewController *viewController;
}
@property (nonatomic, retain) IBOutlet UIWindow *window;
@property (nonatomic, retain) IBOutlet
HelloWorldViewController *viewController;
@end
Thursday, May 10, 12
//
HelloWorldAppDelegate.m
#import "HelloWorldAppDelegate.h"
#import "HelloWorldViewController.h"
@implementation HelloWorldAppDelegate
@synthesize window;
@synthesize viewController;
- (BOOL)application:(UIApplication *)application
didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
// Override point for customization after app launch
[window addSubview:viewController.view];
[window makeKeyAndVisible];
}
return YES;
- (void)dealloc {
[viewController release];
[window release];
[super dealloc];
}
@end
Thursday, May 10, 12
Protocols
Java uses the interface mechanism to enforce
a “contract” for services offered by a class:
// Movable.java
interface Movable {
}
void move(double dx, double dy);
// Circle.java
class Circle extends ClosedCurve implements Movable {
double cx, cy; // center
double r;
// radius
}
Thursday, May 10, 12
public void move(double dx, double dy) {
cx += dx;
cy += dy;
}
Objective-C uses a protocol:
// Movable.h
@protocol Movable
-(void) move:(double)dx : withDY:(double) dy;
// Circle.h
#import "Movable.h"
@interface Circle: ClosedCurve <Movable> {
double cx, dy;
double r;
}
// Circle.m
#import "Circle"
@implementation Circle
-(void) move:(double)dx : withDY:(double) dy {
cx += dx;
cy += dy;
}
Thursday, May 10, 12
Protocols
Like Java, Objective-C
•
Uses this protocol mechanism to avoid relying
on multiple inheritance (not supported)
•
Supports multiple protocols for a class:
// Circle.h
#import "Movable.h"
#import "Resizable.h"
@interface Circle: ClosedCurve <Movable, Resizable> {
double cx, dy;
double r;
}
Thursday, May 10, 12
Foundation Framework
Classes
Most languages provide common (foundational)
objects via libraries (frameworks):
• Strings
• Lists / Arrays
• Dictionaries / Hashtables
Thursday, May 10, 12
Foundation Framework
Classes
Objective-C inherited its foundation classes
from NextStep, so they all begin with NS
• NSString
• NSArray
• NSDictionary
Thursday, May 10, 12
NSString
NSString *s = @"I met all my friends online";
int n = [s length];
char c = [s characterAtIndex:3];
// etc.
Thursday, May 10, 12
NSArray
NSArray *arr =
[[NSArray alloc] initWithObjects:
@"Ken", @"Sara", @"Joshua", nil];
NSMutableArray *mut =
[[NSMutableArray alloc] init];
[mut addObject:@"Simon"];
[mut addObject:@"Joshua"];
Thursday, May 10, 12
NSArray
int n = [mut count];
int i = [arr indexOfObject:@"Simon];
int j = [arr objectAtIndex:2];
[mut sortArrayUsingSelector:
@selector( caseInsensitiveCompare: )];
// etc.
Thursday, May 10, 12
NSEnumerator
NSEnumerator *e = [arr objectEnumerator];
id obj;
while (obj = [e nextObject]) {
// do something with obj
}
Thursday, May 10, 12
NSDictionary
NSDictionary *dictionary =
[[NSDictionary alloc] initWithObjectsAndKeys:
@"one", [NSNumber numberWithInt: 1],
@"two", [NSNumber numberWithInt: 2],
@"three", [NSNumber numberWithInt: 3],
nil];
Thursday, May 10, 12
NSDictionary
NSMutableDictionary *mutable =
[[NSMutableDictionary alloc] init];
// add objects
[mutable setObject: @"Tom" forKey: tom@jones.com"];
[mutable setObject: @"Bob" forKey: @"bob@dole.com" ];
Thursday, May 10, 12
Reflection (Introspection)
• With dynamic typing, the class to which an object
belongs may not be established until run-time
• Hence we may want to query this:
// get class of this object
[myObject class]
• We can also get other info:
// does object respond to method?
[myObject respondsToSelector:@selector(foo)]
// is this class a subclass of another?
[class1 isSubclassOfClass:class2]
Thursday, May 10, 12
What We're Skipping
• Categories
• Posing
• Exceptions
• See
http://www.otierney.net/objective-c.html#retain
interested
Thursday, May 10, 12
if you're
Syntax Notes #1
Single-argument functions are pretty straightforward:
-(void) setEmail: (NSString*) e {
[e retain];
[email release];
email = e;
}
Thursday, May 10, 12
Syntax Notes #1:
Passing Arguments
•
Multi-argument functions use a special syntax:
@implementation AddressCard
-(AddressCard*) initWithFirst: (NSString*) f
! ! ! ! ! ! last: (NSString*) l
! ! ! ! ! ! email: (NSString*) e {
}
•
// ...
So what is the actual name of this method?
Thursday, May 10, 12
•
Multi-argument functions use a special syntax:
@implementation AddressCard
-(AddressCard*) initWithFirst: (NSString*) f
! ! ! ! ! ! last: (NSString*) l
! ! ! ! ! ! email: (NSString*) e {
}
•
// ...
Its name is initWithFirst:last:email
Thursday, May 10, 12
Calling syntax requires (redundant) naming, too:
NSString *first =[[NSString alloc] initWithCString: "Tom"];
NSString *last = [[NSString alloc] initWithCString: "Jones"];
NSString *email =
[[NSString alloc] initWithCString: "tom@jones.com"];
AddressCard *tom = [[AddressCard alloc] initWithFirst: first
! ! ! ! ! ! ! ! ! ! ! ! ! last: last
! ! ! ! ! ! ! ! ! ! ! ! ! email: email];
Thursday, May 10, 12
Syntax Notes #2:
Conditionals / Assignment
• What you're used to:
if ( x == foo() ) // Java
if x == foo():
• This won't work:
if ( x = foo() )
if x = foo():
Thursday, May 10, 12
# Python
• Q: So why does the author do this:
if (self = [super initWithNibName:...]) {
}
Thursday, May 10, 12
• Q: So why does the author do this:
if (self = [super initWithNibName:...]) {
}
• A: This code is shorthand for
self = [super initWithNibName:...];
if (self != nil) {
}
Thursday, May 10, 12
• What you're used to:
if ( x == 3 ) // Java
if x == 3:
Thursday, May 10, 12
# Python
• Q: So why does the author do this:
if ( nil == x )
Thursday, May 10, 12
• Q: So why does the author do this:
if ( nil == x )
• A: Putting a constant on the left side of the
double-equal is a good habit in a language
that supports single-equal in conditionals
Thursday, May 10, 12
Syntax Notes #3:
Less is More
Author does this:
-(UITableViewCell *)tableView:(UITableView *)tv
cellForRowAtIndexPath:(NSIndexPath *)indexPath {
}
Thursday, May 10, 12
UITableViewCell *cell = nil;
if (indexPath.row == 0) {
cell = nameCell;
}
else {
cell = descriptionCell;
}
return cell;
If this is all you want to do, it can be simplified as:
-(UITableViewCell *)tableView:(UITableView *)tv
cellForRowAtIndexPath:(NSIndexPath *)indexPath {
}
Thursday, May 10, 12
return indexPath.row == 0 ? nameCell : descriptionCell;
Author does this:
if (editing == YES) {
// ...
}
Thursday, May 10, 12
I prefer this:
if (editing) {
// ...
}
Thursday, May 10, 12
Syntax Notes # 4:
When Is an Object Not an Object?
From pp 171-172:
Reachability *reach = [[Reachability
reachabilityForInternetConnection] retain];
NetworkStatus status = [reach currentReachabilityStatus];
// ...
- (NSString *)stringFromStatus:(NetworkStatus)status {
NSString *string;
switch(status) {
// ...
Thursday, May 10, 12
/*
File: Reachability.h
...
*/
typedef enum {
NotReachable = 0,
ReachableViaWiFi,
ReachableViaWWAN
} NetworkStatus;
Thursday, May 10, 12
Syntax Notes #5: Type
Casting
@implementation WeatherForecast
// ...
(void)queryService:(NSString *)city withParent:
(UIViewController *)controller! {
!
viewController = (MainViewController *)controller;
Thursday, May 10, 12
@interface WeatherForecast : NSObject {
MainViewController *viewController;
// ...
}
Thursday, May 10, 12
Syntax Notes #6:
Iterating with in
@implementation WeatherForecast
// ...
(NSString *)fetchContent:(NSArray *)nodes {
! NSString *result = @"";
! for (NSDictionary *node in nodes) {
! ! for (id key in node) {
! ! ! if ([key isEqualToString:@"nodeContent"]) {
! ! ! ! result = [node objectForKey:key];
! ! ! }
! ! }
! }
! return result;
}
Thursday, May 10, 12
Syntax Notes #7:
Repeating Code
What's wrong with this picture?
!
// Populate the humidity (an NSString object)
!
xpathQueryString = @"//current_conditions/humidity/@data";
!
nodes = PerformXPathQuery(responseData, xpathQueryString);
!
humidity = [self fetchContent:nodes];
!
NSLog(@"humidity = %@", humidity);
!
!
// Populate the wind (an NSString object)
!
xpathQueryString = @"//current_conditions/wind_condition/@data";
!
nodes = PerformXPathQuery(responseData, xpathQueryString);
!
wind = [self fetchContent:nodes];
!
NSLog(@"wind = %@", wind);
!
!
// Populate the condition (an NSString object)
!
xpathQueryString = @"//current_conditions/condition/@data";
!
nodes = PerformXPathQuery(responseData, xpathQueryString);
!
condition = [self fetchContent:nodes];
!
NSLog(@"condition = %@", condition);
!
Thursday, May 10, 12
Download