Protocols and Delegates – a really simple example

Thanks to John Muchow for this post explaining how to use protocols and delegates so that one object can notify other objects when an event happens in iOS.

There’s a great comment on this page by a “Grant” which encapsulates what we’re trying to do in a nutshell:

“The best way to describe what it does is a “call back”. i.e. Object A creates Object B and tasks it to do something. Object A would like to know when Object B is done, but rather than it keep asking Object B at set intervals: “Are you done yet? Are you done yet? Are you done yet? etc…” (polling), it instead just says to Object B, “This is a reference to me, use this reference to let me know when you’re done”.

Advertisements

Things wot I learnt from Stanford – iPad & iPhone App Development – Fall 2011 – Lecture 3

If you implement the getter and setter for a @property yourself, there is no need to call @synthesize at all.

Its possible that some (usually read-only) @properties might be “calculated” rather than stored to a member variable.

C structures (quite common in iOS) are (almost) never allocated on the heap and so are not referenced by pointers. They are passed around by value. E.g. CGPoint:

typedef struct {
    float x;
    float y;
} CGPoint;

Like class names, it is standard practice to capitalize structure names (and enums) such as CGPoint.

Pointer Referencing

strong reference
– means “keep in the heap until I don’t point to it anymore”.

You won’t point to it anymore if:
You set your pointer to nil.
Or
Your pointer is removed from the heap because no one strongly points to it!

weak reference
– means “keep this as long as someone else points to it strongly“.
If your pointer is thrown out of the heap in iOS 5 or later, it is automatically set to nil so its safe and easy to check for this.

Unlike garbage collection (as used by other frameworks such as .NET) Automatic Reference Counting (ARC) IMMEDIATELY frees objects as soon as they become unreferenced.

Local variables are strong by default and get cleaned up by ARC as soon as they go out of scope.

-(void) dealloc: is called on your object when it is freed – but by this time its really too late to do anything useful. You’d rarely intercept it. dealloc has become less important since iOS 5.

Since the introduction of ARC with iOS 5, release, retain and autorelease are no longer used.

Class methods are normally used for creation and utility functions (e.g. alloc) or to get a reference to a shared instance. They are called with the name of the class or by obtaining a reference to the class by calling an instance with the “class” method.

[[ship class] myClassMethod];

Instance methods are normally called with a pointer to an instance.

Classes cannot have properties as properties are instance variables.

All objects must have “init” or some form of init called immediately after alloc. We ALWAYS nest these calls in one statement e.g. [[MyObj alloc] init];

All classes inherit init from NSObject. If a class cannot be fully initialized with a call to init the guidelines suggest that (strictly) you should code init to raise an exception.

Designated Initializers…

All your classes should declare a designated initializer for sub-classes to call (using [super init…]). Sub-classes MUST call this initializer from their designated initializer (using [super…]). E.g. a sub-class of UIView should ALWAYS call [super initWithFrame]. You should declare your designated initializer in your documentation, there’s no way to indicate this in code. Consult Apple’s documentation to find out the designated initializer for classes you wish to sub-class in the Objective-C framework.
Only call the super-class’s designated initializer from your designated initializer – no-where else. If you have other initializer’s in your class, make sure they call your designated initializer to guarantee the super class’s designated initializer is called regardless of which initializer is called to initialize your class (dig? – dug).

init methods should return an id (not a static type). The caller though should use a static type for the returned pointer.

An example of an initialization method

DYNAMIC BINDING – VERY IMPORTANT!!!

Objective C is different to most statically typed languages in that the code to send a message to an object method is generated at runtime NOT at compile time. This is very important to realize as it is intrinsic to the way an iOS app operates. Static typing in your source code is purely an aid to the compiler to help you find bugs at write time. Dynamic binding at runtime allows messages to be sent to any object at runtime. As long as the object supports that message this is fine – if not, your app will crash. you can use the “introspection” method respondsToSelector to test whether a receiving object supports a particular message.

(See “The Objective-C Runtime Programming Guide” for the technical details of messaging)

You can obtain a dynamic reference to a method (referred to as a SELECTOR) at runtime using various commands (use the SEL type to store a reference to a Selector)

Objective-C classes support Introspection using various methods e.g. isKindOfClass, isMemberOfClass, respondsToSelector etc. The “class” method mentioned earlier is handy here.

if ([obj isKindOfClass:[NSString class]]) {
    NSString *s = [(NSString *)obj stringByAppendingString:@"xyz"];
}

Introspection

The same NSArray can hold references to different object types. You cannot store nil in an NSArray though – use NSNull instead. NSNull acts as a placeholder indicating “no value”.

Contrary to what you’d assume, NSMutableString is rarely used. It doesn’t really save much heap thrashing compared to the immutable NSString class which is highly optimized for most operations.

NSStrings are mostly passed around as immutable copies.

Many objects support copy: and mutableCopy: to convert between mutable and immutable types.

NSValue can be used as a generic object wrapper even for C structures such as CGPoint.

CGPoint point = CGPointMake(25.0, 15.0);
NSValue *pointObject = [NSValue valueWithCGPoint:point];

This would let you store a bunch of points as objects in an NSArray.

NSData is just a “bag of bits” (similar to a “Blob” in other languages). Its up to the app to interpret what the bits mean (e.g. a jpeg image). NSData is commonly used to save/restore and transmit data of many kinds throughout iOS.

Tip: NSArray containsObject: can be slow. Think about using an NSOrderedSet instead. NSOrderedSet is an immutable, ordered (indexed) collection of distinct objects.
NSSet is similiar to NSOrderedSet but is unordered – i.e. has no index for referencing.
One of the most important features of NSSet and NSOrderedSet is that they are geared up for highly efficient searching using “contains”. Also, they will only store “distinct” objects. If you attempt to add an identical object (where isEqual == YES) the attempt will simply be ignored. These two features make them ideal for looking up a match for any object.

Enumerating through collections
Using for…in is highly efficient see slides below from the lecture for example syntax.

Enumeration - Slide 1

Enumeration - Slide 1

Enumeration - Slide 2

Enumeration - Slide 2

Property Lists (aka “plist”)

These seem to be just collections of collections where the collection members are “simple” types – i.e. not your own, custom classes.

Property List

Property List (Slide from Lecture 3)

NSUserDefaults makes use of a plist for persisting data such as a user’s preferences.

Things wot I learnt from Stanford – iPad & iPhone App Development – Fall 2011 – Lecture 2

We usually make an outlet @property weak since the view will already have claimed a strong reference to it, and, if the view exists, it can usually be assumed the view object exists e.g. a UILabel outlet.

Most “non-outlet” @properties are declared as strong for the opposite reason – i.e. only we are referencing them. Practically all controllers will have a strong reference to their model.

NSNumber acts as a handy, object wrapper around primitive number types (e.g. double) that we can pass to framework methods that usually require an object and for storage in NSArray, NSMutableArray, NSDictionary etc. e.g. [NSMutableArray addObject:];

Like NSNumber, you can pass NSString messages such as doubleValue, floatValueintegerValue etc. to convert string values to primitive, numeric types.

A view controller can have IBOutlets from more than one Storyboard e.g. you could have one Storyboard for the iPad and one for the iPhone in a universal application.

id does not mean “object of any class”, it means “pointer to an object of any class”. This is why we use (id) rather than (id *) when passing an id to a method etc. The “*” is implicit.

If an object needs allocating and/or initializing upon its first access its a neat idea to allocate and initialize it in its “getter”. This is refereed to as “lazy initialization”.

Remember: @synthesize NEVER allocates anything, it just sets aside a pointer to an object that can be instantiated.

All properties start out with a value of zero (0), or, if an object, nil.

If you send a message to a nil object the object will just do nothing and return zero (or nil) if the message expects a return value. This is allowed, your app will not crash! The only exception is if the method returns a C struct (e.g. CGPoint) in which case the return value will be undefined.

Use an isEqual method to compare objects, not == since == only compares the object addresses.

Things wot I learnt from Stanford – iPad & iPhone App Development – Fall 2011 – Lecture 1

A @property setter’s name is “set” plus the capitalized @property name e.g. the setter for a @property named myValue would be setMyValue. The “getter” is simply: myValue.

It is considered best practice to always name properties starting with a lowercase letter e.g. myProperty not MyProperty.

You can still create a getter and setter even if you use @synthesize in your implementation file. If you do your getter or setter will be called instead of the synthesized one. This is common practice e.g. to implement “lazy initialization” in the getter.

Property and method declarations are considered public if declared in your header (.h) file and private if declared in your implementation (.m) file.

To declare private properties and methods in your implementation file, wrap them in the @interface … @end compiler directives as in your header file but put a parenthesis after the @interface() opener:


@interface CalculatorViewController()

// Private declarations...

@property (nonatomic, strong) CalculatorBrain *brain;

@end

To help the compiler and yourself, always specify the name of the hidden member for your @properties e.g. @synthesize myValue = _myValue; The compiler will then warn you if you omit the “self” keyword when referencing a member property.

Running your iOS App in Fullscreen

To hide the status bar programmatically…

- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *) launchOptions {
    [[UIApplication sharedApplication] setStatusBarHidden:YES withAnimation:UIStatusBarAnimationSlide];

    return YES;
}

Or, (better) hide it using configuration…

Using the programmatic approach causes a slight annoyance since the status bar is momentarily visible but slides off the screen quickly. A neater approach is to click on the target and add the Boolean, Info Key “Status bar is initially hidden” and set it to YES. This way the status bar never appears.

MVC Communication Rules

From the excellent Stanford University iOS course, fall 2011 (free on iTunesU). This diagram from the presentation summarizes the rules and methods of communication between the Model, View and Controller components of an iOS app.

MVC Communications

MVC Communication Channels and Methods

  • Model = What your application is (but not how it is displayed)
  • Controller = How your Model is presented to the user (UI logic)
  • View = Your Controller‘s minions.
  • Contoller‘s can always talk directly to their Model.
  • Controller‘s can also talk directly to their View.
  • The Model and View should never speak to each other.
  • Communication between the View and Controller is “blind” and structured.
  • The Controller can drop a target on itself, then hand out an action to the View.
  • The View sends the action when things happen in the UI.
  • Sometimes the View needs to synchronize with the Controller.
  • The Controller sets itself as the View‘s delegate.
  • The delegate is set via a protocol (i.e. it’s “blind” to class).
  • Views do not own the data they display.
  • So, if needed, they have a protocol to acquire it.
  • Controllers are almost always that data source (not Model!).
  • Controllers interpret/format Model information for the View.
  • The Model is (should be) UI independent and should NEVER talk directly to the Controller.
  • If the Model has information (e.g. an update) it uses a “radio station” – like broadcast mechanism that Controllers (or another Model) can “tune in” to for stuff they’re interested in.
  • A View might “tune in” to a “station”, but probably not to a Model‘s.

Stick to these rules when building your application and you will have a much better chance of understanding the monster you’ve created(!).

MVC based application

MVC based application

Using and Abusing UIViewControllers

Jonah Williams has some life changing advice on the proper and improper use of UIViewControllers in his post here. This post will switch on the light bulb for anyone coming to iOS development from another platform such as .NET.

This is his (and Apple’s) advice In a nutshell:

  • One (and only one) view controller should be responsible for a whole hierarchy (or screenful) of UIViews.
  • Mostly, you should only use one view controller per screen. Essentially the rootViewController of the current UIWindow should be the only UIViewController with a visible view.
  • Each different screen should have a different view controller i.e. one controller should not control more than one screen.
  • You should NOT nest custom UIViewControllers within a view hierarchy.
  • If more than one UIViewController hangs off the application’s UIWindow, only one of these will get the messages for changes in orientation. The other(s) will NOT get these messages.
  • Nested UIViewControllers are not guaranteed, or likely, to receive messages for changes in orientation or lifecycle messages such as viewDidAppear:, viewWillAppear:, viewDidDisappear: and viewWillDisappear: even though they inherit from UIViewController. Only the topmost UIViewController is certain to get these messages.

Some interesting discussions on this point on StackOverFlow:

iPhone viewWillAppear not firing

Am I abusing UIViewController Subclassing? – Nice quote from this:

Each screenfull should have one master VC Subclass, with all the subviews controlled instead by custom controllers (which happen to control views) which are subclasses of simple NSObject.

In this case, UIViewControllers should only be directly to the Window or UINavigationController, UITabBarController etc?

Also, this quote from Apple’s View Controller Programming Guide:

View controllers are directly associated with a single view object but that object is often just the root view of a much larger view hierarchy that is also managed by the view controller. The view controller acts as the central coordinating agent for the view hierarchy, handling exchanges between its views and any relevant controller or data objects. A single view controller typically manages the views associated with a single screen’s worth of content, although in iPad applications this may not always be the case.

With iOS 5 and the introduction of Custom Container View Controllers, much of this will change (see Pat Zearfoss’s comment, below). But it is important to understand the above as a starting point.