Objective-C · 2014-08-26 · Computer Science Science ObjC = proper superset of C i.e., all C...
Transcript of Objective-C · 2014-08-26 · Computer Science Science ObjC = proper superset of C i.e., all C...
Objective-CCS 442: Mobile App Development Michael Saelee <[email protected]>
1
Computer ScienceScience
Agenda- ObjC overview
- ObjC object-oriented syn/sem
- Memory management
- Declared properties
- Blocks
2
Computer ScienceScience
§Overview
3
Computer ScienceScience
ObjC = proper superset of C
i.e., all C programs are legal ObjC programs
but nasty bits of C not used much
(e.g., pointer arithmetic, function pointers, etc.)
4
Computer ScienceScience
small language specification; additions for:
- OOP (classes, objects, inheritance) - messaging (à la Smalltalk)
- dynamic typing & reflection
- enumeration
- blocks (lambdas)
- misc. preprocessor/compiler directives
5
Computer ScienceScience
ObjC largely defined by runtime behavior
i.e., very large dynamic component
Contrast with C++ — huge number of compile-time features added to C
i.e., static features
6
Computer ScienceScience
dynamic = more flexibility, but harder to optimize performance & type-check
7
Computer ScienceScience
ObjC runtime features are provided via:
- functions in (C) libobjc library
- compiled ObjC language constructs (conceptually → libobjc calls)
8
Computer ScienceScience
think of ObjC language as syntactic sugar for runtime features supported by libobjc
— practically, very rarely need/want to use libobjc functions directly
- ugly; ignores existing ObjC libraries
9
Computer ScienceScience
Foundation framework = ObjC base lib
- root object class: NSObject - basic types: NSString, NSDate - collections: NS[Mutable]Array, NS[Mutable]Set, NS[Mutable]Dictionary
- concurrency: NSOperation, NSThread
- and many, many more
10
Computer ScienceScience
11
Computer ScienceScienceNSObject
Initializing a Class + initialize + load
Creating, Copying, and Deallocating Objects + new + alloc + allocWithZone: – init – copy – dealloc – finalize
Identifying Classes + class + superclass + isSubclassOfClass:
Testing Class Functionality + instancesRespondToSelector:
Obtaining Information About Methods – methodForSelector: + instanceMethodForSelector: + instanceMethodSignatureForSelector: – methodSignatureForSelector:
Describing Objects + description
Sending Messages – performSelector:withObject:afterDelay: – performSelector:withObject:afterDelay:inModes: – performSelectorOnMainThread:withObject:waitUntilDone: – performSelectorOnMainThread:withObject:waitUntilDone:modes: – performSelector:onThread:withObject:waitUntilDone: – performSelector:onThread:withObject:waitUntilDone:modes: – performSelectorInBackground:withObject: + cancelPreviousPerformRequestsWithTarget: + cancelPreviousPerformRequestsWithTarget:selector:object:
+ = class method - = instance method
12
Computer ScienceScience
§OOP with ObjC
13
Computer ScienceScience
1. objc.h: some essential types
2. Messaging (method call) syn/sem
3. Class definition & usage
4. Categories & Protocols
14
Computer ScienceScience
objc.h: some essential types
15
Computer ScienceSciencetypedef struct objc_class *Class;
typedef struct objc_selector *SEL;
/* objc/objc.h */
struct objc_class { Class isa; #if !__OBJC2__ Class super_class OBJC2_UNAVAILABLE; const char *name OBJC2_UNAVAILABLE; long version OBJC2_UNAVAILABLE; long info OBJC2_UNAVAILABLE; long instance_size OBJC2_UNAVAILABLE; struct objc_ivar_list *ivars OBJC2_UNAVAILABLE; struct objc_method_list **methodLists OBJC2_UNAVAILABLE; struct objc_cache *cache OBJC2_UNAVAILABLE; struct objc_protocol_list *protocols OBJC2_UNAVAILABLE; #endif } OBJC2_UNAVAILABLE;
/* objc/runtime.h */
typedef signed char BOOL;
#define YES (BOOL)1 #define NO (BOOL)0
#define nil __DARWIN_NULL /* id of Nil instance */
typedef id (*IMP)(id, SEL, ...);
typedef struct objc_object { Class isa; } *id;
16
Computer ScienceScience
Class class object pointer
id generic object pointer
nil NULL object pointer value
SEL “selector” — method identifier
IMP method pointer
BOOL Boolean values {YES, NO}
17
Computer ScienceScience
§Messaging
18
Computer ScienceScience
[receiver message]; [receiver messageWithArg:arg]; [receiver messageWithArg:arg1 andAlso:arg2];
- note: not named parameters i.e., order is important!
Syntax:
19
Computer ScienceScience
Widget *w = [Widget alloc]; [w init]; [w setName:@"Foo"]; [w turnSprocket:1234 rotations:2.5 speed:10.0];
20
Computer ScienceScience
Dynamic selector dispatching[receiver message]; [receiver messageWithArg:arg]; [receiver messageWithArg:arg1 andAlso:arg2];
21
Computer ScienceScience
Behind the scenes:id objc_msgSend(id self, SEL _cmd,...)
- 50-100 lines of hand-tuned assembly - executed for every ObjC method call
[receiver messageWithArg:arg];
objc_msgSend(receiver, @selector(messageWithArg:), arg);
ObjC compiler directive
22
Computer ScienceScience
/*********** id objc_msgSend(id self, SEL _cmd,...); ************/ ! ENTRY _objc_msgSend CALL_MCOUNTER !// load receiver and selector movl selector(%esp), %ecx movl self(%esp), %eax !// check whether selector is ignored cmpl $ kIgnore, %ecx je LMsgSendDone // return self from %eax !// check whether receiver is nil testl %eax, %eax je LMsgSendNilSelf !// receiver (in %eax) is non-‐nil: search the cache LMsgSendReceiverOk: movl isa(%eax), %edx // class = self-‐>isa CacheLookup WORD_RETURN, MSG_SEND, LMsgSendCacheMiss xor %edx, %edx // set nonstret for msgForward_internal jmp *%eax !// cache miss: go search the method lists (i.e., table lookup) LMsgSendCacheMiss: MethodTableLookup WORD_RETURN, MSG_SEND xor %edx, %edx // set nonstret for msgForward_internal jmp *%eax // goto *imp !// message sent to nil: redirect to nil receiver, if any LMsgSendNilSelf: call 1f // load new receiver 1: popl %edx movl __objc_nilReceiver-‐1b(%edx),%eax testl %eax, %eax // return nil if no new receiver je LMsgSendReturnZero movl %eax, self(%esp) // send to new receiver jmp LMsgSendReceiverOk // receiver must be in %eax LMsgSendReturnZero: movl $0,%edx LMsgSendDone: ret
23
Computer ScienceScience
salient bits:
- method dispatch is completely dynamic - implementation isn’t known until runtime (and may vary across calls)
- neat hacks possible, e.g., “swizzling”
- sending messages to nil is ok!
24
Computer ScienceScience
dynamic selector
SEL selector = @selector(drillHole:); if ([widget respondsToSelector:selector]) [widget performSelector:selector withObject:aDrillBit];
[widget performSelector:NSSelectorFromString(@"reset")];
Class widgetClass = [widget class]; [widget isKindOfClass:widgetClass]; /* (YES) */
id widget = [[Widget alloc] init];
25
Computer ScienceScience
messaging = dynamic resolution of receiver & selector
— low-level implementation of target-action pattern
26
Computer ScienceScience
27
Computer ScienceScience
controller object (coded logic)zoomIn:
[zoomInButton setTarget:controller]; [zoomInButton setAction:@selector(zoomIn:)];
28
Computer ScienceScience
§Class definition & usage
29
Computer ScienceScience
interface vs. implementation
30
Computer ScienceScience
@interface ClassName : SuperClass !// method declarations (API) !@end
31
Computer ScienceScience
@implementation ClassName { // instance variable declarations } !// method definitions !@end
32
Computer ScienceScience
Widget.h Widget.m
#import <Foundation/Foundation.h> !@interface Widget : NSObject !// method declarations !@end
33
Computer ScienceScience
Widget.h Widget.m
#import "Widget.h" !@implementation Widget { NSString *name; NSMutableArray *sprockets; } !// method definitions !@end
34
Computer ScienceScience
instance variable scope modifiers: @private @package @protected @public
(default)
35
Computer ScienceScience
method prototypes
+ (returnType)classMethod; -‐ (returnType)instanceMethod; -‐ (returnType)instanceMethodWithArg:(argType)arg; -‐ (returnType)instanceMethodWithArg:(argType)arg1 andAlso:(argType)arg2;
36
Computer ScienceScience
@class Sprocket; // forward declaration !@interface Widget : NSObject !-‐ (id)init; // overrides NSObject’s -‐ (NSString *)name; -‐ (void)setName:(NSString *)aName; -‐ (void)addSprocket:(Sprocket *)sprocket; -‐ (int)numSprockets; !@end
37
Computer ScienceScience
#import "Widget.h" #import "Sprocket.h" !@implementation Widget { NSString *name; NSMutableArray *sprockets; } !-‐ (NSString *)name { return name; } !-‐ (void)setName:(NSString *)aName { name = aName; } !-‐ (void)addSprocket:(Sprocket *)sprocket { [sprockets addObject:sprocket]; } !-‐ (int)numSprockets { return (int)[sprockets count]; } @end
38
Computer ScienceScience
† implicit access to instance variables
-‐ (NSString *)name { return name; } !-‐ (void)setName:(NSString *)aName { name = aName; }
39
Computer ScienceScience
-‐ (void)foo:(Widget *)otherWidget { [otherWidget setName:nil];
// or, because we're the same class otherWidget-‐>name = nil; }
† access to my class’s privates
40
Computer ScienceScience
referring to myself: self (≈ Java’s this)
41
Computer ScienceScience
-‐ (id)init { ... [self setName:@"Default"]; sprockets = [[NSMutableArray alloc] init]; return self; }
† initializer returns id type
42
Computer ScienceScience
† self is just a variable!
-‐ (void)setName:(NSString *)aName { name = aName; }
≈
void setName(id self, SEL _cmd, NSString* aName) { self-‐>name = aName; }
43
Computer ScienceScience
referring to my superclass: super
44
Computer ScienceScience
-‐ (id)init { [super init]; [self setName:@"Default"]; sprockets = [[NSMutableArray alloc] init]; return self; }
(first attempt)45
Computer ScienceScience
-‐ (id)init { [super init]; [self setName:@"Default"]; sprockets = [[NSMutableArray alloc] init]; return self; }
- superclass init fails?
- superclass init returns a different object?
- e.g., singleton object, cached version
what if:
46
Computer ScienceScience
conventional template for initialization (recall that self is just a variable)
-‐ (id)init { self = [super init]; if (self) { [self setName:@"Default"]; sprockets = [[NSMutableArray alloc] init]; } return self; }
47
Computer ScienceScience
(a bit more succinctly…)
-‐ (id)init { if (self = [super init]) { [self setName:@"Default"]; sprockets = [[NSMutableArray alloc] init]; } return self; }
48
Computer ScienceScience
delegating initialization
@interface Widget : NSObject -‐ (id)init; -‐ (id)initWithName:(NSString *)aName; ... @end
-‐ (id)init { return [self initWithName:@"Default"]; }
-‐ (id)initWithName:(NSString *)aName { if (self = [super init]) { [self setName:aName]; sprockets = [[NSMutableArray alloc] init]; } return self; }
49
¶ Categories (aka informal protocols)
50
Computer ScienceScience
// file: Widget+Tuning.h#import "Widget.h"
@interface Widget ( Tuning ) -‐ (void)optimize; -‐ (void)calibrate:(NSArray *)settings; @end
// file: Widget+Tuning.m#import "Widget+Tuning.h" !@implementation Widget ( Tuning ) -‐ (void)optimize { ... } -‐ (void)calibrate:(NSArray *)settings { ... } @end
“Tuning” category for Widgets
51
Computer ScienceScience
Anonymous category; aka “Extension”
// file: Widget.m#import "Widget.h"
@interface Widget () -‐ (void)privHelper; @end !// main implementation block@implementation Widget
... !-‐ (void)privHelper { ... } @end
52
Computer ScienceScience
“Observer” pattern for all objects
@interface NSObject ( Observable ) -‐ (void)addObserver:(id)observer; -‐ (void)removeObserver:(id)observer; -‐ (void)notifyObservers:(id)event; @end
53
Computer ScienceScience
why?
- partitioning source code
- creating private APIs
- extending existing classes (without source)
54
Computer ScienceScience
but:
- no new instance variables (unless using associative references)
- no language support for type checking
- i.e., no way of knowing if an object implements a category
55
¶ (Formal) Protocols
56
Computer ScienceScience
#import <Foundation/Foundation.h> #import "Adjustable.h" #import "Drilling.h" !@interface Widget : NSObject <Adjustable, Drilling> ... @end
@protocol Adjustable -‐ (float)size; -‐ (void)setSize:(float)inches; -‐ (void)increaseSizeBy:(float)inches; -‐ (void)decreaseSizeBy:(float)inches; @end
@protocol Drilling @required // (default) -‐ (void)boreHole:(float)diameter; -‐ (void)changeBit:(Bit *)bit; !@optional -‐ (void)makeLevel; @end
57
Computer ScienceScience
Widget *widget = [[Widget alloc] init]; id <Adjustable> adjustableTool = widget; Widget <Drilling> *drillingWidget = widget; !if ([drillingWidget conformsToProtocol:@protocol(Adjustable)]) { NSLog(@"My drilling widget is adjustable!"); }
58
Computer ScienceScience
i) NSFastEnumeration (for NSArray, NSSet, etc.)
59
Computer ScienceScience
NSArray *array = [NSArray arrayWithObjects: @"One", @"Two", @"Three", @"Four", nil]; !for (NSString *element in array) { NSLog(@"element: %@", element); }
NSDictionary *dictionary = [NSDictionary dictionaryWithObjectsAndKeys: @"Superman", @"Clark Kent", @"Batman", @"Bruce Wayne", @"Spiderman", @"Peter Parker", nil]; !for (NSString *key in dictionary) { NSLog(@"Superhero: %@, Alter ego: %@", key, [dictionary valueForKey:key]); }
60
Computer ScienceScience
@protocol NSFastEnumeration !- (NSUInteger)countByEnumeratingWithState:(NSFastEnumerationState *)state objects:(id *)stackbuf count:(NSUInteger)len; !@end
typedef struct { unsigned long state; id *itemsPtr; unsigned long *mutationsPtr; unsigned long extra[5]; } NSFastEnumerationState;
61
Computer ScienceScience
countByEnumeratingWithState:objects:count: !Returns by reference a C array of objects over which the sender should iterate, and as the return value the number of objects in the array. !-‐ (NSUInteger)countByEnumeratingWithState:(NSFastEnumerationState *)state objects:(id *)stackbuf count:(NSUInteger)len !Parameters state
Context information that is used in the enumeration to, in addition to other possibilities, ensure that the collection has not been mutated.
stackbuf A C array of objects over which the sender is to iterate.
len The maximum number of objects to return in stackbuf. !Return Value The number of objects returned in stackbuf. Returns 0 when the iteration is finished. !Discussion The state structure is assumed to be of stack local memory and, from a garbage collection perspective, does not require write-barriers on stores, so you can recast the passed in state structure to one more suitable for your iteration.
62
Computer ScienceScience@implementation Widget
... !- (NSUInteger)countByEnumeratingWithState:(NSFastEnumerationState *)state objects:(id *)stackbuf count:(NSUInteger)len { NSUInteger count = 0; if (state->state == 0) { state->mutationsPtr = &(state->extra[0]); } if (state->state < [sprockets count]) { state->itemsPtr = stackbuf; while (count < len && state->state < [sprockets count]) { stackbuf[count++] = [sprockets objectAtIndex:state->state++]; } } return count; } @end
@implementation Sprocket ... !/* for pretty printing objects */ - (NSString *)description { return [NSString stringWithFormat:@"Sprocket: %@", name]; } @end
63
Computer ScienceScience
Widget *w = [[Widget alloc] init]; [w addSprocket:[Sprocket sprocketWithName:@"Foo"]]; [w addSprocket:[Sprocket sprocketWithName:@"Bar"]]; [w addSprocket:[Sprocket sprocketWithName:@"Baz"]]; !for (id sprock in w) { NSLog(@"%@", sprock); }
ObjCDemo[6816:a0b] Sprocket: Foo ObjCDemo[6816:a0b] Sprocket: Bar ObjCDemo[6816:a0b] Sprocket: Baz
64
Computer ScienceScience
ii) NSKeyValueCoding
65
Computer ScienceScience
@interface NSObject ( NSKeyValueCoding ) + (BOOL)accessInstanceVariablesDirectly; - (id)valueForKey:(NSString *)key; - (void)setValue:(id)value forKey:(NSString *)key; - (BOOL)validateValue:(id *)ioValue forKey:(NSString *)inKey error:(NSError **)outError; - (NSMutableArray *)mutableArrayValueForKey:(NSString *)key; - (NSMutableSet *)mutableSetValueForKey:(NSString *)key; - (id)valueForKeyPath:(NSString *)keyPath; - (void)setValue:(id)value forKeyPath:(NSString *)keyPath; - (BOOL)validateValue:(id *)ioValue forKeyPath:(NSString *)inKeyPath error:(NSError **)outError; - (NSMutableArray *)mutableArrayValueForKeyPath:(NSString *)keyPath; - (NSMutableSet *)mutableSetValueForKeyPath:(NSString *)keyPath; - (id)valueForUndefinedKey:(NSString *)key; - (void)setValue:(id)value forUndefinedKey:(NSString *)key; - (void)setNilValueForKey:(NSString *)key; - (NSDictionary *)dictionaryWithValuesForKeys:(NSArray *)keys; - (void)setValuesForKeysWithDictionary:(NSDictionary *)keyedValues; @end
66
Computer ScienceScience
Widget *widget = [[Widget alloc] initWithName:@"Foo"]; ![widget setName:@"Bar"]; NSLog(@"Name: %@", [widget name]); ![widget setValue:@"Baz" forKey:@"name"]; !NSLog(@"Name: %@", [widget valueForKey:@"name"]); !NSLog(@"Name length: %@", [widget valueForKeyPath:@"name.length"]);
ObjCDemo[598] Name: Bar ObjCDemo[598] Name: Baz ObjCDemo[598] Name length: 3
67
Computer ScienceScience
@implementation Widget - (id)valueForUndefinedKey:(NSString *)key { return [NSString stringWithFormat: @"Key '%@' unknown", key]; } @end
ObjCDemo[874] Key 'foo' unknown
Widget *widget = [[Widget alloc] init]; !NSLog(@"%@", [widget valueForKey:@"foo"]);
68
Computer ScienceScience
array elements not directly KVC accessible
[widget valueForKeyPath:@"sprockets[0].name"]
69
Computer ScienceScience
arrays = data storage ≠ set of properties
but, aggregate properties are accessible
70
Computer ScienceScience
NSArray *widgets = [NSArray arrayWithObjects: [Widget widgetWithName:@"Foo"], [Widget widgetWithName:@"Bar"], [Widget widgetWithName:@"Baz"], nil];
ObjCDemo[2244] Widget names: ( Foo, Bar, Baz ) ObjCDemo[2244] Num sprockets: ( 1, 2, 1 ) ObjCDemo[2244] Agg. count of widgets: 3 ObjCDemo[2244] Agg. sum of sprockets: 4
[[widgets objectAtIndex:0] addSprocket:[Sprocket sprocket]]; [[widgets objectAtIndex:1] addSprocket:[Sprocket sprocket]]; [[widgets objectAtIndex:1] addSprocket:[Sprocket sprocket]]; [[widgets objectAtIndex:2] addSprocket:[Sprocket sprocket]];
NSLog(@"Widget names: %@", [widgets valueForKeyPath:@"name"]); NSLog(@"Num sprockets: %@", [widgets valueForKeyPath:@"numSprockets"]); NSLog(@"Agg. count of widgets: %@", [widgets valueForKeyPath:@"@count"]); NSLog(@"Agg. sum of sprockets: %@", [widgets valueForKeyPath:@"@sum.numSprockets"]);
71
Computer ScienceScience
uses:
- programmatic property access
- scripting support
- serialization
72
Computer ScienceScience
iii) NSKeyValueObserving
73
Computer ScienceScience
@interface NSObject(NSKeyValueObserving) - (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary *)change context:(void *)context; @end
@interface NSObject(NSKeyValueObserverRegistration) - (void)addObserver:(NSObject *)observer forKeyPath:(NSString *)keyPath options:(NSKeyValueObservingOptions)options context:(void *)context; - (void)removeObserver:(NSObject *)observer forKeyPath:(NSString *)keyPath; @end
74
Computer ScienceScience@implementation Mechanic
- (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary *)change context:(void *)context { NSLog(@"'%@' changed in %@", keyPath, object); NSLog(@"From '%@' to '%@'", [change objectForKey:NSKeyValueChangeOldKey], [change objectForKey:NSKeyValueChangeNewKey]); } @end
ObjCDemo[1200] 'name' changed in <Widget: 0x10010c8f0> ObjCDemo[1200] From 'Baz' to 'Bat'
Widget *widget = [[Widget alloc] initWithName:@"Baz"]; Mechanic *mechanic = [[Mechanic alloc] init]; [widget addObserver:mechanic forKeyPath:@"name" options:(NSKeyValueObservingOptionNew | NSKeyValueObservingOptionOld) context:nil]; [widget setName:@"Bar"];
75
Computer ScienceScience
uses:
- general “observer” pattern
- model-controller binding
76
Computer ScienceScience
§Memory management
77
Computer ScienceScience
Remember how garbage collection built on top of C’s malloc/free must be conservative?
(if you do, good for you!)
78
Computer ScienceScience
refresher:
- C has no run-time type info
- when traversing memory graph, everything that looks like a pointer must be treated like one
- if we can reach an object by any means, we can’t trash it
79
Computer ScienceScience
good news: ObjC has self describing types: typedef struct objc_object {
Class isa; } *id;
… allows for garbage collection of ObjC objects
80
Computer ScienceScience
but — do we really want garbage collection?
81
Computer ScienceScience
82
Computer ScienceScience
indeterminate performance / “hiccups”
arguably non-ideal for a mobile platform
83
Computer ScienceScience
alternative: reference counting - track all references to each object
- when # references = 0, deallocate
84
¶ Manual reference counting
85
Computer ScienceScience
manual reference counting:
- 4 essential methods
- very simple rules
86
Computer ScienceScience
methods:
- alloc: allocate object with refs = 1
- retain: increment refs by 1
- release: decrement refs by 1
- dealloc: call automatically when refs = 0
87
Computer ScienceScience
rules:
- any object that calls alloc/retain on another object is an owner of the callee
- owner responsibility: must release owned object when no longer needed
- guarantee: object will (should) not be deallocated so long as it has ≥ 1 owner
88
Computer ScienceScience
when to take ownership?
… when an object needs to be “kept alive” longterm (e.g., outside the current frame)
89
Computer ScienceScience
- (void)setName:(NSString *)aName { name = [aName retain]; }
// possibly leaking “old” name!
90
Computer ScienceScience
- (void)setName:(NSString *)aName { [name release]; name = [aName retain]; }
- (void)dealloc { [name release]; [sprockets removeAllObjects]; // not really needed [sprockets release]; [super dealloc]; }
91
Computer ScienceScience
ObjCDemo[7393] Name refcount=1
(memory leak)
NSString *name = [[NSString alloc] initWithCString:"Foo"]; Widget *widget = [[Widget alloc] init]; ![widget setName:name]; !// use widget ... ![widget release]; // widget retainCount = 0 (dealloc'd) !NSLog(@"Name refcount=%d", [name retainCount]);
92
Computer ScienceScience
Program received signal: “EXC_BAD_ACCESS”.
NSString *name = [[NSString alloc] initWithCString:"Foo"]; Widget *widget = [[Widget alloc] init]; ![widget setName:name]; [name release]; !// use widget ... ![widget release]; // widget retainCount = 0 (dealloc'd) !NSLog(@"Name refcount=%d", [name retainCount]);
93
Computer ScienceScience
NSString *name = [[NSString alloc] initWithCString:"Foo"]; Sprocket *spck = [[Sprocket alloc] init]; Widget *widget = [[Widget alloc] init]; ![widget setName:name]; [name release]; ![widget addSprocket:spck]; [spck release]; !// use widget ... ![widget release];
(annoying!)
94
Computer ScienceScience
alternative: autorelease
— call release some time later
95
Computer ScienceScience
Widget *widget = [[Widget alloc] init]; [widget setName:[[[NSString alloc] initWithCString:"Foo"] autorelease]]; [widget addSprocket:[[[Sprocket alloc] init] autorelease]]; !// use widget ... [widget release]; // allocated NSString & Sprocket are automatically released (no leak!)
96
Computer ScienceScience
better: use convenience methods that return autoreleased objects
naming convention: -‐[ClassName className]
-‐[ClassName classNameWith:]
97
Computer ScienceScience
[NSString stringWithCString:"Foo"];
[[[NSString alloc] initWithCString:"Foo"] autorelease];
=
98
Computer ScienceScience
some time later???
99
Computer ScienceScience
NSAutoReleasePool
- thread-local list of autoreleased objects
- when popping off stack frame associated with autorelease pool, call release on objects
100
Computer ScienceScience
int main (int argc, const char *argv[]) { NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init]; ! Widget *widget = [[Widget alloc] init]; [widget setName:@"Foo"]; [widget addSprocket:[Sprocket sprocket]]; ! // use widget ... ! [widget release]; [pool drain]; // autoreleased objects sent release messages return 0; }
101
Computer ScienceScience
while (stillProcessing) { NSAutoreleasePool *loopPool = [[NSAutoreleasePool alloc] init]; // create and use autoreleased objects [loopPool drain]; }
102
Computer ScienceScience
while (stillProcessing) { // newer (& approx 6x faster) way of doing the same @autoreleasepool { // create and use autoreleased objects } }
103
Computer ScienceScience
?
104
Computer ScienceScience
#import <UIKit/UIKit.h> !int main(int argc, char *argv[]) { @autoreleasepool { return UIApplicationMain(argc, argv, nil, nil); } }
105
Computer ScienceScience
Figure 1-1 Application life cycle
Your code
User taps application icon
main()
UIApplicationMain() applicationDidFinishLaunching:
System asks application to terminate
Application execution terminates
EventLoop
UIKit
Handle event
applicationWillTerminate:
The Main Function
In an iPhone application, the main function is used only minimally. Most of the actual work needed to runthe application is handled by the UIApplicationMain function instead. As a result, when you start a newapplication project in Xcode, every project template provides an implementation of the standard mainfunction like the one in “Handling Critical Application Tasks.” The main routine does only three things: itcreates an autorelease pool, it calls UIApplicationMain, and it releases the autorelease pool. With fewexceptions, you should never change the implementation of this function.
Listing 1-1 The main function of an iPhone application
#import <UIKit/UIKit.h>
int main(int argc, char *argv[]){ NSAutoreleasePool * pool = [[NSAutoreleasePool alloc] init]; int retVal = UIApplicationMain(argc, argv, nil, nil); [pool release]; return retVal;}
18 Core Application Architecture2009-10-19 | © 2009 Apple Inc. All Rights Reserved.
CHAPTER 1
The Core Application
106
Computer ScienceScience
Event Loop
autorelease pool
event arrives
107
Computer ScienceScience
Event Loop
autorelease pool
autorelease
autorelease
autorelease
autorelease
108
Computer ScienceScience
Event Loop
autorelease pool
event handler returns
109
Computer ScienceScience
Event Loop
autorelease pool
110
Computer ScienceScience
Event Loop
wait for event
111
¶ Automatic Reference Counting (ARC)
112
Computer ScienceScience
basic idea:
- rules for using retain/release are rigid
- backed by naming conventions
- previously: static (compile-time) analysis tools could help detect memory leaks
- now: extend technique to do away with manual retain/release — all automated!
113
Computer ScienceScience
but:
- what to do when conventions don’t apply, or when non-default behavior is required?
- provide object-relation hints to ARC
- using ownership qualification modifiers for variables
114
Computer ScienceScience
ownership qualifiers: __strong (default)
__weak
__unsafe_unretained
__autoreleasing
115
Computer ScienceScience
__strong:
- object is automatically retained for lifetime of variable scope
- if variable is assigned a reference to a new object, automatically release previous one
116
Computer ScienceScience
-‐ (void)foo { __strong Widget *w = [[Widget alloc] init]; // use widget // no release! } // method returns -‐-‐ w out of scope, automatically released
@implementation Widget { __strong NSString *name; } !-‐ (void)setName:(NSString *)aName { name = aName; // no release/retain needed } // name still in scope, not released
-‐ (void)dealloc { [super dealloc]; // no releases here! // but use C free() if needed }
117
Computer ScienceScience
__weak:
- do not retain object
- if referenced object is deallocated, automatically set variable to nil
- “zeroing weak reference”
- only available in iOS5+
118
Computer ScienceScience
__weak Widget *w = [[Widget alloc] initWithName:@"Foo"]; NSLog(@"%@", w);
Widgets[28309:707] (null)
119
Computer ScienceScience
__unsafe_unretained:
- just like __weak, but not self-zeroing
- required when building for iOS 4
120
Computer ScienceScience
__autoreleasing:
- use for arguments that are passed by reference and release on return
- don’t often specify explicitly
121
Computer ScienceScience
consider:
- strong pointees are kept alive so long as referencing pointer is alive
- what if: strong strong?
- “retain cycle” — ARC memory leak
122
Computer ScienceScience
@implementation Widget { NSMutableArray *sprockets; } !-‐ (void)addSprocket:(Sprocket *)sprocket { [sprocket setOwner:self]; [sprockets addObject:sprocket]; } @end
@implementation Sprocket { Widget *owner; } !-‐ (void)setOwner:(Widget *)aWidget { owner = aWidget; } @end
-‐ (IBAction)do_alloc1:(id)sender { Widget* w = [[Widget alloc] init]; Sprocket *s = [[Sprocket alloc] init]; [w setName:@"Foo"]; [w addSprocket:s]; } !-‐ (IBAction)do_alloc2:(id)sender { Widget* w = [[Widget alloc] init]; w.name = @"Foo"; }
123
demo124
Computer ScienceScience
@implementation Widget { NSMutableArray *sprockets; } !-‐ (void)addSprocket:(Sprocket *)sprocket { [sprocket setOwner:self]; [sprockets addObject:sprocket]; } @end
@implementation Sprocket { __weak Widget *owner; } !-‐ (void)setOwner:(Widget *)aWidget { owner = aWidget; } @end
Widget Sprocket
strong ref (via array)
weak ref (breaks retain cycle)
125
Computer ScienceScience
use weak references:
- to avoid retain cycles
- to refer to objects whose lifetimes you should not influence
- e.g., parent container, callback target
- generally speaking, all delegate objects
126
Computer ScienceScience
ARC rules:
- cannot call retain, release, or autorelease
- cannot call dealloc (explicitly)
- don’t cast between id and (void *)
- don’t store object pointers in C structs
127
Computer ScienceScience
But we still need @autoreleasepools!
- ARC still uses them behind the scenes
- May need to create additional autoreleasepools in special situations
- e.g., heavy memory usage, multithreading
128
Computer ScienceScience
§Declared properties
129
Computer ScienceScience
ObjC supports dot (.) access for properties— compiler can automatically generate:
- setter and getter methods
- instance variables
130
Computer ScienceScience
compiler directives: @property = property declaration
@synthesize = generate setter/getter
131
Computer ScienceScience
@property options: getter=getterName
setter=setterName
readwrite (default) / readonly
atomic (default) / nonatomic
132
Computer ScienceScience
@property ARC-related options:
strong (__strong)
weak (__weak)
assign (__unsafe_unretained) (default)
133
Computer ScienceScience@class Sprocket;
!@interface Widget : NSObject !@property (strong) NSString *name; @property (strong, readonly) NSMutableArray *sprockets; !-‐ (void)addSprocket:(Sprocket *)sprocket; -‐ (int)numSprockets; @end
@implementation Widget @synthesize name, sprockets; !-‐ (void)addSprocket:(Sprocket *)sprocket { [sprockets addObject:sprocket]; } !-‐ (int)numSprockets { return (int)[sprockets count]; } @end
134
Computer ScienceScience
widget.name = @"Foo"; NSArray *arr = widget.sprockets;
[widget setName:@"Foo"]; NSArray *arr = [widget sprockets];
=
135
Computer ScienceScience
// overriding default implementation to do logging -‐ (void)setName:(NSString *)aName { NSLog(@"Setting name to %@", aName); self.name = aName; }
ObjCDemo[8503:a0b] Setting name to Foo ObjCDemo[8503:a0b] Setting name to Foo ObjCDemo[8503:a0b] Setting name to Foo ObjCDemo[8503:a0b] Setting name to Foo ObjCDemo[8503:a0b] Setting name to Foo ObjCDemo[8503:a0b] Setting name to Foo ObjCDemo[8503:a0b] Setting name to Foo ...
136
Computer ScienceScience
§Blocks
137
Computer ScienceScience
≈ lambdas/closures in functional languages
- anonymous functions
- enable higher order functions
- i.e., functions that return or take other functions as arguments
138
Computer ScienceScience
void (^blk)(void);declaration:
blk(); // logs "Hello from block!"invocation:
block-literal:blk = ^{ NSLog(@"Hello from block!"); };
return type parameter types
139
Computer ScienceScience
^(int n){ return n*n; }(7); // => 49
int (^square)(int) = ^(int num) { return num * num; };
square(7); // => 49
140
Computer ScienceScience
BinaryBlock blk = ^(id obj1, id obj2) { /* do something and return an object */ };
typedef id (^BinaryBlock)(id, id);
blk(arg1, arg2); // returns id type
141
Computer ScienceScience
dispatch_block_t foo () { return ^{ /* do something (with side effect) */ }; }
typedef void (^dispatch_block_t)(void);
dispatch_block_t b = foo(); b(); // does something (with side effect)
142
Computer ScienceScience
blocks …
- are objects
- require memory management
- good news: ARC is block-aware!
- create closures over variables in scope
143
Computer ScienceScience
dispatch_block_t foo () { return ^{ /* do something (with side effect) */ }; }
returned block is stack local, but ARC makes a copy and returns it for us.
dispatch_block_t foo () { return [[^{ /* do something (with side effect) */ } copy] autorelease]; }
pre-ARC code:
144
Computer ScienceScience
typedef int (^IntBlock)(int); !IntBlock sumWith(int i) { return ^(int n){ return i + n; }; }
ObjCDemo[4145:a0b] 7 ObjCDemo[4145:a0b] 15
IntBlock sum_with_two = sumWith(2), sum_with_ten = sumWith(10); NSLog(@"%d", sum_with_two(5)); NSLog(@"%d", sum_with_ten(5));
“captured” variable
145
Computer ScienceScience@implementation Foo {
dispatch_block_t blk; } !!- (void)m1 { NSMutableString *str = [[NSMutableString alloc] initWithString:@"Tarzan"]; blk = ^{ [str insertString:@"Hello " atIndex:0]; NSLog(@"%@", str); }; } !- (void)m2 { blk(); } @end
ObjCDemo[55842:303] Hello Tarzan
Foo *f = [[Foo alloc] init]; [f m1]; [f m2];
146
Computer ScienceScience@implementation Foo {
dispatch_block_t blk; } !!- (void)m1 { NSMutableString *str = [[NSMutableString alloc] initWithString:@"Tarzan"]; blk = ^{ [str insertString:@"Hello " atIndex:0]; // str is retained by block! NSLog(@"%@", str); }; } !- (void)m2 { blk(); } @end
Foo *f = [[Foo alloc] init]; [f m1]; [f m2];
ObjCDemo[55842:303] Hello Tarzan
147
Computer ScienceScience@implementation Foo {
dispatch_block_t blk; NSMutableString *str; } !- (void)m1 { str = [[NSMutableString alloc] initWithString:@"Tarzan"]; blk = ^{ [str insertString:@"Hello " atIndex:0]; NSLog(@"%@", str); }; } !- (void)m2 { str = [[NSMutableString alloc] initWithString:@"Batman"]; blk(); } @end
Foo *f = [[Foo alloc] init]; [f m1]; [f m2];
ObjCDemo[55842:303] Hello Batman
148
Computer ScienceScience@implementation Foo {
dispatch_block_t blk; NSMutableString *str; } !- (void)m1 { str = [[NSMutableString alloc] initWithString:@"Tarzan"]; blk = ^{ [self->str insertString:@"Hello " atIndex:0]; // equivalent! NSLog(@"%@", str); }; } !- (void)m2 { str = [[NSMutableString alloc] initWithString:@"Batman"]; blk(); } @end
Foo *f = [[Foo alloc] init]; [f m1]; [f m2];
ObjCDemo[55842:303] Hello Batman
149
Computer ScienceScience@implementation Foo {
dispatch_block_t blk; NSMutableString *str; } !- (void)m1 { str = [[NSMutableString alloc] initWithString:@"Tarzan"]; blk = ^{ [self->str insertString:@"Hello " atIndex:0]; // equivalent! NSLog(@"%@", str); }; }
retaining self causes retain cycle!
150
Computer ScienceScience@implementation Foo {
dispatch_block_t blk; NSMutableString *str; } !- (void)m1 { str = [[NSMutableString alloc] initWithString:@"Tarzan"]; NSMutableString *lstr = str; // break cycle with local pointer blk = ^{ [lstr insertString:@"Hello " atIndex:0]; NSLog(@"%@", lstr); }; }
// or, if we really need to use self inside the block: !- (void)m1 { str = [[NSMutableString alloc] initWithString:@"Tarzan"]; __weak Foo *weakSelf = self; blk = ^{ /* use weakSelf in here instead of self to avoid cycle */ }; }
151
Computer ScienceScience
captured variables are constants by default
alternative: __block keyword
152
Computer ScienceScience
I've been called 1 times I've been called 2 times I've been called 3 times I've been called 1 times
dispatch_block_t tracking_block() { __block int i = 0; return ^{ NSLog(@"I've been called %d times", ++i); }; }
dispatch_block_t tb1 = tracking_block(), tb2 = tracking_block(); tb1(); tb1(); tb1(); tb2();
153
Computer ScienceScience
practical uses for blocks?
154
¶ higher order functions (for collections)
155
Computer ScienceScience
- (void)enumerateObjectsUsingBlock:(void (^)(id obj, NSUInteger idx, BOOL *stop))block;
NSDictionary *aliasMap = [NSDictionary dictionaryWithObjectsAndKeys: @"Superman", @"Clark Kent", @"Batman", @"Bruce Wayne", @"Flash", @"Barry Allen", nil]; !// traditional for loop enumeration for (NSString *key in aliasMap) { NSString *val = [aliasMap objectForKey:key]; NSLog(@"%@ => %@", key, val); } !// block based enumeration [aliasMap enumerateKeysAndObjectsUsingBlock:^(id key, id val, BOOL *stop){ NSLog(@"%@ => %@", key, val); }];
156
Computer ScienceScience
@interface NSArray (HOF) !- (NSArray *)map: (id (^)(id))block; !@end
@implementation NSArray (HOF) !- (NSArray *)map:(id (^)(id))block { NSMutableArray *newArray = [NSMutableArray array]; for (id obj in self) { id newObj = block(obj); [newArray addObject:newObj ? newObj : [NSNull null]]; } return newArray; } !@end
157
Computer ScienceScience
ObjCDemo[3295:a0b] ( 1, 4, 9, 16 )
NSArray *arr = [NSArray arrayWithObjects: [NSNumber numberWithInt:1], [NSNumber numberWithInt:2], [NSNumber numberWithInt:3], [NSNumber numberWithInt:4], nil]; !NSArray *ret = [arr map:^(id obj){ int i = [obj intValue]; return (id)[NSNumber numberWithInt:i*i]; }]; !NSLog(@"%@", ret);
158
¶ code encapsulation
159
Computer ScienceScience
typedef void (^VoidBlock)(void); !void WithAutoReleasePool(VoidBlock block) { NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init]; block(); [pool drain]; }
while (stillProcessing) { WithAutoReleasePool(^{ /* loop body with autoreleases */ }); }
160
¶ concurrency
161
Computer ScienceScience
ObjCDemo[3921:a0b] Waiting ... ObjCDemo[3921:1303] 0 ObjCDemo[3921:1303] 1 ObjCDemo[3921:1503] 0 ObjCDemo[3921:1303] 2 ObjCDemo[3921:1503] 1 ObjCDemo[3921:1503] 2
NSOperationQueue *queue = [[NSOperationQueue alloc] init]; ![queue addOperationWithBlock:^{ /* simulate a long-running operation */ for (int i=0; i<3; i++) { [NSThread sleepForTimeInterval:0.4]; NSLog(@"%d", i); } }]; ![queue addOperationWithBlock:^{ /* simulate another long-running operation */ for (int i=0; i<3; i++) { [NSThread sleepForTimeInterval:1.0]; NSLog(@"%d", i); } }]; !NSLog(@"Waiting ..."); [queue waitUntilAllOperationsAreFinished]; [queue release];
162
¶ callbacks
163
Computer ScienceScience
[UIView animateWithDuration:2.0 animations:^{ aButton.center = CGPointMake(200, 200); aButton.alpha = 0.0; aButton.transform = CGAffineTransformMakeScale(3.0, 3.0); } completion:^(BOOL finished){ [aButton removeFromSuperview]; }];
164
Computer ScienceScience
- (void)animateButton { [UIView beginAnimations:nil context:NULL]; [UIView setAnimationDuration:2.0]; [UIView setAnimationDelegate:self]; [UIView setAnimationDidStopSelector:@selector(animationDidStop:finished:context:)]; aButton.center = CGPointMake(200, 200); aButton.alpha = 0.0; aButton.transform = CGAffineTransformMakeScale(3.0, 3.0); [UIView commitAnimations]; } !- (void)animationDidStop:(NSString *)animationID finished:(NSNumber *)finished context:(void *)context { [aButton removeFromSuperview]; }
(vs. using delegation)165
Computer ScienceScience
§Foundation Workhorse Classes
166
Computer ScienceScience
Basic data:
- NS[Mutable]String
- NSNumber
- NSData
167
Computer ScienceScience
NS[Mutable]String:
- massive API: string ops, file I/O, type conversions, paths, URLs, etc.
- literal syntax: @"hello world"
168
Computer ScienceScience
NSNumber (immutable):
- all purpose number → object wrapper
- needed when storing values in collection types
- literal syntax: @4, @100L, @3.14
169
Computer ScienceScience
Can’t use NSNumbers directly in arithmetic expressions, must “unbox”NSNumber *narr[] = { @1, @2, @3, @4, @5 }; int i, sum = 0; for (i=0; i<5; i++) { sum += [narr[i] intValue]; /* manual "unboxing" */ }
170
Computer ScienceScience
Also: @(…) expressions
NSString *s = @(argv[0]);
NSNumber *n = @(10.0 + sin(5));
- only work with char * & numbers
171
Computer ScienceScience
NSData:
- “binary data” wrapper
NSData *rawdata = [NSData dataWithContentsOfFile: @"pathToFile"]; !void *rawbytes = rawdata.bytes;
172
Computer ScienceScience
Data Structures:
- NS[Mutable]Array
- NS[Mutable]Dictionary
173
Computer ScienceScience
NS[Mutable]Array: array listNSArray *arr = [NSArray arrayWithObjects:@"fi", nil]; NSMutableArray *marr = [arr mutableCopy]; [marr addObject:@"fo"]; [marr insertObject:@"fee" atIndex:0]; [marr insertObject:@"fum" atIndex:3]; // insert at end ok !/* marr = [ @"fee", @"fi", @"fo", @"fum" ] */
174
Computer ScienceScience
NSArray has a literal syntax, tooNSArray *arr = @[ @"fee", @"fi", @"fo", @"fum", @1, @2, @3 ]; !NSLog(@"%@", arr[0]); /* => fee */ !NSMutableArray *marr = [arr mutableCopy]; !marr[0] = @"phee"; /* can only replace existing */ [marr removeObjectAtIndex:4]; [marr addObject:@4]; marr[8] = @5; /* will fail! */
175
Computer ScienceScience
NS[Mutable]Dictionary: key-val mapNSDictionary *aliasMap = [NSDictionary dictionaryWithObjectsAndKeys: @"Superman", @"Clark Kent", @"Batman", @"Bruce Wayne", @"Flash", @"Barry Allen", nil]; !NSMutableDictionary *mAliasMap = [aliasMap mutableCopy]; [mAliasMap setObject:@"Bruce Banner" forKey:@"Hulk"];
176
Computer ScienceScience
NSDictionary literals: @{ key : val }NSMutableDictionary *aliasMap = [@{ @"Clark Kent" : @"Superman", @"Bruce Wayne" : @"Batman", @"Barry Allen" : @"Flash" } mutableCopy]; !aliasMap[@"Bruce Banner"] = @"Hulk"; !NSLog(@"%@", aliasMap[@"Bruce Wayne"]); // => Batman
177
Computer ScienceScience
Can combine, of course:NSArray *aliases = @[ @{ @"hero" : @"Superman", @"alias" : @"Clark Kent" }, @{ @"hero" : @"Batman", @"alias" : @"Bruce Wayne" }, @{ @"hero" : @"Flash", @"alias" : @"Barry Allen" } ]; !for (NSDictionary *d in aliases) { NSLog(@"%@ -> %@", d[@"alias"], d[@"hero"]); }
178
Computer ScienceScience
Concurrency:
- NSOperationQueue
- NSOperation
- NSBlock/Invocation/Operation
179
Computer ScienceScience
URLs:
- NSURL: local/remote resource name
- NSURLRequest/Response
- NSURLConnection
180
Computer ScienceScience
</ObjC>
181