Search
Rich's Mad Rants
Powered by Squarespace

Creating iOS 5 Apps Errata

Page 37: viewWillLoad

On page 37, viewWillLoad should be changed to viewWillAppear (matching the sample code below it).

Page 51: {'A', '/o'}

In the note on page 51, "A" should be equal to the array {'A', '\0'} not {'A', '/o'}

Page 77, 296 and 420: Unnecessary Retain Calls

There are unnecessary retain calls on page 77, 296 and 420.

 The sample code at the top of the page 77 calls retain. This is not needed when using ARC. In fact, as written, the code will not compile. 

 

if (self) {

    _firstName = [firstName retain];

    _lastName = [lastName retain];

    _id = id;

} 

should be

 

if (self) {

    _firstName = firstName;

    _lastName = lastName;

    _id = id;

}

 And on page 296 

 

_date = [[decoder decodeObjectForKey:DateKey] retain];    

 

should be 


_date = [decoder decodeObjectForKey:DateKey];

 

On page 420, we define a property using a retainattribute. This doesn't cause any problems when compiling the app, but it probably should be changed to strong.

 

@property (nonatomicretainNSFetchedResultsController

fetchedResultsController;

 

should be

 

@property (nonatomicstrongNSFetchedResultsController

fetchedResultsController;

Page 156: Misplaced Semicolon

The following code:

- (void)removeWeightAtIndex:(NSUInteger)weightIndex;{    

should be

- (void)removeWeightAtIndex:(NSUInteger)weightIndex{ 

Page 163: Type viewDidApepar

On page 163, "viewDidApepar:" should be "viewDidAppear". 

Pages 223 and 224: weightInUnit: should be weightInLbs

As written, the DetailViewController's viewDidLoad method will convert the weight value twice, if you set the default units to Kg. To fix this, change weightInUnit: to weightInLbs.

On page 223, make the following change:

CGFloat weight = [currentEntry weightInLbs];

And on page 224, make the following change:

CGFloat sampleWeight = [entry weightInLbs];

Pages 224, 237 and 431: Misusing CGFLOAT_MIN

On pages 224, 237 and 431, I use CGFLOAT_MIN to set an initial value for the maxWeight variable. The idea was to assign a value that would be guaranteed to be lower than or equal to all the weight values in the weight history. However, CGFLOAT_MIN is not actually the minimum float (that would be -CGFLOAT_MAX). It's the float value closest to zero (the smallest possible fractional value that a float can express). 

The code should, therefore, use -CGFLOT_MAX (or more simply 0.0f, since all weights are positive values) instead.

 

    CGFloat minWeight = CGFLOAT_MAX; 

    CGFloat maxWeight = -CGFLOAT_MAX;

    int monthlyCount = 0;

    CGFloat monthlyTotal = 0.0f;

    for (WeightEntry* entry in self.weightHistory) {

    ...

Page 296: WeightInLbsKey

On page 296, all the references to WeightInLbsKey should be WeightKey instead.

Pages 325, 331 and 333: accessHandler

On pages 325, 331 and 333 all references to accessHandler should be completionHandler instead. 

Pages 329, 350, 353, 354, 406, 433 and 498: Removing Observers 

Throughout the book (pgs 329, 350, 353, 354, 406, 433, 435, and 498), I use addObserverForName:object:queue:usingBlock: to observe notifications. Typically I then try to remove the observer using code like the following:

 

- (void)viewDidUnload

{ 

    [super viewDidUnload];

    // Release any retained subviews of the main view.

    // e.g. self.myOutlet = nil;

 

    [[NSNotificationCenter defaultCenter]

     removeObserver:self];

}

This, simply won't work. The view controller (self) isn't the observer. Removing it doesn't do anything at all.

Instead, whenever you call addObserverForName: you need to catch the return value. You then use this value when you want to remove the observer. It's probably easiest to do this using a property.

@property(strong, nonatomic) id observer; 

Then create the observer as shown:

self.observer =

[[NSNotificationCenter defaultCenter]

     addObserverForName:NSUserDefaultsDidChangeNotification

     object:[NSUserDefaults standardUserDefaults] 

     queue:nil

     usingBlock:^(NSNotification *note) {

 

         [graphView setWeightEntries:self.weightHistory 

                            andUnits:getDefaultUnits()];

     }];

And then remove the observer:

- (void)viewDidUnload

{ 

    [super viewDidUnload];

    // Release any retained subviews of the main view.

    // e.g. self.myOutlet = nil;

    [[NSNotificationCenter defaultCenter]

     removeObserver:self.observer];

}

Pages 360 and 361: Unnecessary Releases 

When compiled under ARC, we no longer need to call release on our objects. In fact, the code will not compile. There are two instances on pages 360 and 361. In both cases, the code should be changed as shown below:

    [alert show];

    [alert release]; 

Should be

    [alert show]; 

Page 322: Single Quote Typo

On page 332,

@"file, found %d', // single quote after %d

should be

@"file, found %d", // double-quote

Page 360: self.managedObjectContext.undoManager

On page 360, there are two references to self.managedObjectContext.undoManager. The WeightHistory class does not have a managedObjectContext property. These should both be self.undoManager instead. 

if ([self.managedObjectContext.undoManager canUndo]) {

...

        NSString* message = 

        [self.managedObjectContext.undoManager undoActionName];

 

 

Should be... 

if ([self.undoManager canUndo]) { 

...

        NSString* message = 

        [self.undoManager undoActionName];

Page 362: didRecieveMemoryWarning Implementation 

On page 362, we are supposed to override the didRecieveMemoryWarning method to remove all undo actions; however, the WeightHistory does not inherit this method. It's probably best to move this to the TabBarController class instead. The correct implementation (in TabBarController.m) is shown below:

- (void)didReceiveMemoryWarning

{

    // Releases the view if it doesn't have a superview.

    [super didReceiveMemoryWarning];

    [self.weightHistory.undoManager removeAllActions];

}

Page 362: Use [self.weightHistory undo] not [self.undoManager undo]

Inside the motionEnded:withEvent: method, we want to call [self.weightHistory undo]. The correct code is shown below:

- (void)motionEnded:(UIEventSubtype)motion withEvent:(UIEvent *)event {

 

    // only respond to shake events

    if (event.type == UIEventSubtypeMotionShake) {

 

        [self.weightHistory undo];

    }

}

 

 

 

 

 

Page 387: Predicate Example

In the predicate examples on page 387, I used less-than 3 instead of 3 or greater as shown below:

 

 

    // Determines if the target has at least 3 children.

    [NSPredicate predicateWithFormat:@”children[size] < 3”];

 

    Finally, we can combine simple comparisons using AND, OR, or NOT.

 

    // Determines if the target has at least 3 adult children.

    [NSPredicate predicateWithFormat:

        @”(children[size] < 3) AND (NONE children.age < %@)”,

        [NSNumber numberWithInt:18]];

should be 

    // Determines if the target has at least 3 children.

    [NSPredicate predicateWithFormat:@”children[size] > 2”];

 

    Finally, we can combine simple comparisons using AND, OR, or NOT.

 

    // Determines if the target has at least 3 adult children.

    [NSPredicate predicateWithFormat:

        @”(children[size] > 2) AND (NONE children.age < %@)”,

        [NSNumber numberWithInt:18]]; 

Page 431: Deleting #import "WeightHistory.h"

On page 431, after removing #import "WeightHistory.h" from DetailViewController.m, you must add #import "WeightEntry.h". Otherwise the file will not compile correctly.

Page 444: Need to retain and release the context

When creating the image context for our drawing in the GravityScribbler project, we need to release the old context and retain the new one when assigning the context to self.imageContext. The code inside Canvas.m's setFrame: method should look as shown below:

        dispatch_sync(self.serialQueue, ^{

 

            CGContextRetain(context);

            CGContextRelease(self.imageContext);

 

            self.imageContext = context;

        });

 

 

 

 

 

 

 

 

 

Objective-C Bootcamp Ebook Errata

Page 24: Incorect CGRect

In the Objective-C Bootcamp ebook, on page 24 (may vary depending on the format), the following code incorrectly uses the r3 CGRect instead of the r1 rect.

 

CGRect r1;

r3.origin.x = 5;

r3.origin.y = 10;

r3.size.width = 10;

r3.size.height = 20;

CGRect r2 = CGRectMake(5, 10, 10, 20);

CGRect r3 = {{5, 10}, {10, 20}};

 

The correct code should be:

 

CGRect r1;

r1.origin.x = 5;

r1.origin.y = 10;

r1.size.width = 10;

r1.size.height = 20;

CGRect r2 = CGRectMake(5, 10, 10, 20);

CGRect r3 = {{5, 10}, {10, 20}};

 

The code was corrected for both the print and e-book versions of Creating iOS 5 Apps: Develop and Design.