Building Cocoa Applications: A Step-by-Step Guide by Michael Mahoney, Simson Garfinkel The unconfirmed error reports are from readers. They have not yet been approved or disproved by the author or editor and represent solely the opinion of the reader. Here's a key to the markup: [page-number]: serious technical mistake {page-number}: minor technical mistake : important language/formatting problem (page-number): language change or minor formatting problem ?page-number?: reader question or request for clarification This page was updated July 9, 2008. UNCONFIRMED errors and comments from readers: (1) 1; The Smalltalk programming language is not spelled 'SmallTalk' but 'Smalltalk' This error is made in several places. {20} 6. Modeless dialogs; Modeless dialogs are treated undifferently than "other windows" listed in #7. They do NOT remain on top of document windows. As an example try TextEdit and place a document window on top of the find dialog. (Mac OS X, v. 10.1.5) (22) First sentence; "A dark gray disclosure triangle (>) at the right side of a menu cell ..." When those triangular marks appear on menus, they are not called disclosure triangles. A disclosure triangle is the same shape, but has a completely different function and different behavior. When clicked, a disclosure triangle rotates to point down (v) and reveals (discloses) more information. Clicking again rotates it back to point right (>) and reveals less information. Those symbols on menus do not share the same meaning or the same behavior, and should not be called by the same name. (32) Last paragraph under "Menu Structure"; "To see the contents of a menu, single-click on the menu name in the title bar." Menus are in the menu bar, not the title bar. The title bar is the thing at the top of a window, the menu bar is the thing at the top of the main display. {35} Step 12; "Only one application can be dropped into the Dock at a time." This is not true as of Mac OS X 10.1.4. You can drop multiple items onto the dock simultaneously. [38] Figure 3-5; There seem to be many problems with this diagram. (i) ATTENDEES table shouldn't have a USER_TYPE_ID field (ii) ATTENDEES table should link to USERS table, not USER_TYPES (iii) OFFICES table should link to USERS table, not ACCOUNT_TYPES (iv) ROLES table should link to USER_ROLES {41} Step 42; "In fact, you can switch the scrolling direction while pressing the mouse button by simply sliding from one scroll arrow to the other." As of Mac OS X 10.1.4, this only works in Cocoa apps. (43) 3rd paragraph; The last sentence in this paragraph begins with "Note that the keyboard equivalents...". This sentence should refer to figure 1-32. [59] Ex. 4; This is a difficult exercise to accomplish. I have always wondered if Apple's implementation of services is buggy, or if particular apps don't handle services. For example, I can do Services:Grab:Selection in TextEdit, but not in Internet Explorer. It would seem that only Apple Apps can be a client to services. Can you show me a non-Apple App that can be a service client? Obviously some services are context sensitive and require user action such as highlighting text before using them. **[59] Ex. 4;** Oh, I get it. Services only work with Cocoa apps. (82) step 15; The text says to click "Step over ..." (and the icon shown is for stepping over) but then the text says: "This action steps out..." There's a different icon to click for stepping out. (82) Step 14. and 17.; "Font" window should be "Colors" window, of course. [83] 3rd paragraph; While it is interesting to see gdb running in Emacs, Emacs itself is not introduced until page 108 and is very difficult for a novice to use. The introduction on page 108 should be moved to chapter 2 and a few Emacs commands mentioned; specifically, how to get help and how to quit the program. (89) Last paragraph; the bland window should probably be blank {90} First paragraph; If a window is behind another window or closed, you can make it visible by double-clicking its icon in the Nib file window. Not entirely true. When you create a new IB palette project in PB and double-click Testinspector.nib, the Inspector window is completely hidden behind the IB Info panel and will stay that way however much you double-click the Inspector window icon. {93} Figure 3-7; Though the figure tries to label all of the items in the Views palette, it omits labels for the AppleScript palette and the second NSBox (depicted as a vertical line between the rounded NSButton and the NSTextViews). (109) last paragraph; The programming language "Smalltalk" is spelled "Smalltalk" and not "SmallTalk". There are several occurences of "SmallTalk" in the book. [123] 1st code sample, methods -setStart: and setEnd:; The code as given can lead to serious memory problems, and provoke an application crash. This problem can happen if the object passed as parameter to the methods is the same as the instance variable object. The more correct approaching pattern (there are other solutions) is: - (void) setStart:(id) anObject { [start autorelease]; start = [anObject retain]; } (117) 3rd paragraph; "This object is then sent an init method..." should read "This object is then sent an init message...". [118] 1st large paragraph; The text says that a "class interface" is "a fancy name for an included file that is brought to the compiler's attention with an #import directive," but the example which follows shows an import directive and a separate class interface. This is confusing because the import directive in the example is not explained. I think what is meant is that the example file, which contains a class interface, will be brought to the compiler's attention with an import directive in some other file. Then explain the import directive in the example. [123] dealloc sample code ; I am not sure if this is a technical mistake or not.. But in the over ridden -(void)dealloc routine, shouldn't the last action call be to [super dealloc] and NOT [super release] It makes no sense to override the dealloc routine and then call the parent's release routine to finish doing the dealloc work.. so.. I think the proper code should be -(void)dealloc { [start release] ; [end release] ; [super dealloc] ; } [123] First code sample block; The implementation of the two set methods shown does not work in some special cases and will cause an application to show unpredictable results depending on its use. Precisely, the set methods do not work, if the argument passed in has the same value as the instance variable (e.g. start) and the retain count of the object referenced is one. i.e. (start==anobject && [start retainCount]==1) yields YES. What happens it that the start object is destroyed using -dealloc and retain is called on that freed memory region. (125) 1st paragraph; The second sentence says "This variable is of type Class, which is a typedef for an ANSI C structure..." This should read "This variable is of type Class, which is a typedef for a pointer to an ANSI C structure...". {125} Code snippet at bottom.; Code snippet begins "NSMutableString *str3 = [[NSString alloc] init];" It should read "NSMutableString *str3 = [[NSMutableString alloc] init];" Otherwise, a run-time error occurs. [125] bottom code listing; The appendString: calls are missing the @ signs before the double-quotes. It should read: [str3 appendString:@"This is how you build "]; [str3 appendString:@"a Cocoa String"]; Without the @ signs this will generate a compiler error [127] Box, second bullet; This one is related to the one on page 123: i.e. (start==anobject && [start retainCount]==1) yields YES. What happens it that the start object is destroyed using -dealloc and retain is called on that freed memory region. The sentence in the box ought to be some mantra to always remember. However, it is inacurate, as "accessor method" means both set- and get- methods. In order to be correct, it should say "set-methods" or "setters". (128) example 4-5; The line "NSAutoreleasePool ..." should be in bold. (128) Example 4-5; The line that begins NSString *str2 = ... should be indented. Also, NSLog(@"The value of str1 is '%@'", str2); should be NSLog(@"The value of str2 is '%@'", str2); That is, change str1 to str2. (131) 2nd paragraph ("After the autorelease pool is created...."); The paragraph says "... the program allocates an NSApplication object by sending the alloc message ..." but the example shows the program creating the NSApplication instance by sending the sharedApplication message. There is no [NSApplication alloc] in the example. [131] 5th (last) paragraph; The text reads: "The event loop terminates when the NSApp object is sent an NSApp or stop: message; this usually happens when the user chooses the Quit menu command. The NSApp message causes NSApp to call exit(), terminating the program. The stop: message causes [NSApp run] to exit." The first message should not be NSApp but rather terminate:. Thus the text should read: "The event loop terminates when the NSApp object is sent a terminate: or stop: message; this usually happens when the user chooses the Quit menu command. The terminate: message causes NSApp to call exit(), terminating the program. The stop: message causes [NSApp run] to exit." {131} inside the main() function; There is an inconsistency between the main() function listing of the Tiny.m program on page 131 and the original one on page 107. The one on page 131 is missing the line: [NSApp release]; (131) 2nd paragraph; In the second paragraph, there is the text "This object [refering to NSApp] is create with the sharedInstance method, ...." In the code sample, the "sharedApplication" message/method is used. Code seems to run fine, and no 'sharedInstance' in NSApplication.h.... {132} First code section. 2nd paragraph on the page; Small snippet of Tiny.m code is reproduced for examination. When the Tiny.m code is first introduced on pages 105-107, there is an inconsistency. From the code for the setup function (listed at the bottom of page 106): NSWindow *myWindow; // typed pointer to NSWindow object NSView *myView; // typed pointer to NSView object NSRect graphicsRect; // contains an origin, width, height From the code listed on page 132: NSWindow *myWindow = nil; NSView *myView = nil; NSRect graphicsRect; Specifically, comments are not included on page 132, and NSWindow and NSView are not initialized to nil in the complete code listing on pages 105-107. {133} second code eg [myWindow code]; The third line of the code has styleMask: NSTitledWindowMask (etc.) here and in the initial listing of the program on p.107. Further down p. 133, in the explanation of the arguments, this appears simply as style: NSTitledWindowMask.... (135) 3rd paragraph; It says that NSView is an abstract superclass and thus instances of NSView are *rarely* used. If it's an abstract class, instances are *never* used. {136} makeKeyAndOrderFront note (middle of page); Not a mistake, but one answer to how to make the window be brought to the front is to insert the following in main(): [NSApp unhideWithoutActivation]; //bring window to front This should be inserted just before [NSApp run]; The result will be a front (but unactivated) window, which is Ok for this example. (136) 3rd line from bottom; Should be int n = 12; not n=31 (see page 106) {156} Step 14; when creating Actions. Step 14 refers to 'clearall:' and the 'A' should be capitalized. The picture has it correctly and page 157 also refers to it correctly but if someone where to follow the steps they would create an error. {156} when creating Actions. Step 14 refers to enterdigit and the 'd' should be capitalized, this will cause problems in the resulting code. The picture has it correctly and page 157 also refers to it correctly but if someone where to follow the steps they would create an error. [156] step 14; incorrect capitilisation of methods, should read: 14. Add the clearAll:, enterDigit:, not 14. Add the clearall:, enterdigit:, [160] and following pages; One of the important things programmers do is name things. The instance variables in Calculator are poorly named. 'X' and 'Y' are uppercase, against social norms. They would be better named 'operand' and 'total'. 'enterFlag' and 'yFlag' are named a bit vague. Once you name things for what they do, you see how the algorithm is really working. For example, the assignment Y = X; in enterDigit is redundant: it's always going to have happened already in enterOp. After renaming and tracing, I just had to rewrite the algorithm: - (IBAction)clearAll:(id)sender { operand = total = 0.0; operator = NONE; //(add this to enum and lose yFlag) operandEntryToBeginFlag = YES; //enterFlag renamed [self displayOperand]; } - (IBAction)enterDigit:(id)sender { if (operandEntryToBeginFlag) { operand = 0.0; operandEntryToBeginFlag = NO; //operand entry now begun } operand = (operand >= 0 ? (operand * radix) + [[sender selectedCell] tag] : (operand * radix) - [[sender selectedCell] tag]); [self displayOperand]; } - (IBAction)enterOperator:(id)sender { switch (operator) { case NONE: total = operand; break; case ADD: operand = total += operand; break; case SUBTRACT: operand = total -= operand; break; case MULTIPLY: operand = total *= operand; break; case DIVIDE: operand = total /= operand; break; case EQUALS: total = operand; break; default: //should never reach here NSLog(@"Error: reached switch default in enterOperator"); [readout setStringValue: [NSString stringWithFormat: @"improper operator"]]; return; } operator = [[sender selectedCell] tag]; //get the next operation operandEntryToBeginFlag = YES; //ready to start entering the next operand [self displayOperand]; //display the total (same as the operand for the moment) } {161} enterDigit method; In the enterDigit method, the calculation of the new X value does not consider the sign of X. Hence, if you make X negative, and then add some more digits, the value increases (closer to zero). I changed the function to this - (IBAction)enterDigit:(id)sender { double Z = [[sender selectedCell] tag]; if (enterFlag) { Y = X; X = 0.0; enterFlag = NO; } X = (X*10.0) + (X >= 0 ? Z : -Z); [self displayX]; } I am having a lot of fun putting the calculator together. Cocoa is pretty exciting technology. In Chapter 6, I am curious what happens to the 'default' about box. I added the new one, but there were no steps to delete the existing one to recover its storage. Is the default part of the System? (168) paragraph following step 16; "...the NSMatrix object will send the enterDigit: action message to an instance of our Controller class." Since there is just one instance of the Controller class, this reads better if it says "... will send the enterDigit:" action message to the instance of our Controller class." [177] 3 line; return self; not consistent with same code on pp 161 where function exits after [self display] (179) Step 5; In this step we are told to fill in the appropriate tag values for each of the recently added function buttons to the calculator interface. Each of the tag values is supposed to correspond to the function enum values created on page 177 (PLUS = 1001, SUBTRACT = 1002, ...). The author forgets to mention what to do with the equal sign button "=" -- it has no enum value. Figure 5-28, which is on the same page as this step, hints at the solution. (It shows the equal sign as having the tag value '1005'). The text, however, fails to mention it. [181] 2nd paragraph; When you implement doUnaryMinus, you must modify one line of enterDigit: X = (X >= 0 ? (X * radix) + [[sender selectedCell] tag] : (X * radix) - [[sender selectedCell] tag]); Otherwise, the user may change the sign to minus and continue entering the operand and the calculator will malfuntion. {185} middle paragraph; The text says the NSApplication() function "loads the applications's main nib file (MainMenu.nib in Calculator)" but the NSApplication() function shown above that paragraph does not refer to "MainMenu.nib" (it refers to "myMain"). (185) Last paragraph of first section.; The text states that main() recieves the return code from NSApplication(). The code example shows that its return type is 'void,' however. Either the statement incorrect, or it warrants further explanation. {193} middle paragraph; The text says "(Recall that we parsed an outlet from the Controller.h file into IB in the previous chapter; here we'll parse a method in a similar fashion.)" In fact, in the previous chapter we did not parse an outlet -- we parsed a method (page 181). (195) 3rd Paragraph. 4th sentence.; The first letter (the lowercase m) of the method name makeKeyAndOrderFront: is not emboldened according to the formatting conventions used through the book. [203] Step 35 in making the Calculator App; Here's what you get for running out and buying Jaguar before these fine authors could revise their book: Bring up the File's Owner information as decribed in the text. Here's where we start to deviate from the text. The way to inform File'sOwner that is of the Controller is by finding it under the Custom Class screen not the Attributes screen of the File's Owner Info dialog. Follow the instructions in the book to make AboutPanel.nib of the Controller class. Just do not go by Figure 6-16. I compiled and it worked. {205} "Adding Icons to Applications section" "For example, the Mail program uses the icon containing an envelope (shown at the edge of the page)..." It's a stamp, not an envelope. {206} references an onlamp article. The URL is incorrect -- the substring "muc" in it should be "mac" -- also, it should probably point at a oreillynet.com front door instead of onlamp.com. http://www.oreillynet.com/pub/a/mac/2001/05/24/aqua_design.html (210) Step 5; When dragging the icn file to the Images group, the little gray arrow does not change colors. Instead, the triangle twists and the group folder opens. {211} Step 14; If the AboutPanel.nib is not visible in IB, double-click AboutPanel.nib in the PB's Groups and Files pane. [213] middle paragraph; Oh, where to begin? This section about images really needs clarification, perhaps a whole page. The text says to drop an image file in "a nib window in IB or in the main window in PB." Are the results of those two actions identical, or are they entirely different? The following sentence seems to indicate that they are different ("images that are stored in one nib cannot easily be used by objects in other nibs"), yet when dragging an image to one nib window the image is accessible in the other nib (under its Images tab). Also, when dragging an image to a nib, a dialog appears asking if the image should be added to the project. So, it appears that the two actions are the same. (I'm using PB 1.1.1 and IB 2.2.) Also, it appears that dropping an image file in PB only leaves a reference (alias?) because deleting the image in the groups window of PB asks about d eleting the reference. What happens then if the original image file is moved or altered? Images appear to be stored in the project folder. Where are the sounds, where'd they come from? How to delete (unused) images or sounds from a nib? Should we? [213] middle paragraph; The text says to drop an image "in the main window in PB." What is the main window? I find that an image can only be dropped in the "Groups and Files" window, and even then the user must drag it in to desired location within the list of files. (PB 1.1.1.) [219] First paragraph under "Modifying the Controller Class"; In the first sentence: "To make the Controller work with this new NSMatrix that contains a pop-up menu, ..." Do you really mean to say 'NSMatrix', or did you mean to say 'NSButton'. We haven't added an NSMatrix to the application since adding the function buttons; but we have just added a pop-up menu, and that is a type of NSButton. {221} Step 23, case: 16; The format string for base 16 should be @"%X" and not @"%x" so that the hex digits will be displayed in upper case. [225] Section on delegation; This section would benefit by a diagram showing how objects delegate messages and which object talks to which other objects, etc. In general, the entire book would be better if there were a few diagrams showing how object and windows relate, how actions and outlets operate, etc. {229} Step 4; In step 4 the Controller class interface reads "Controller:Object". This is wrong, it should be "Controller:NSObject". [230] Paragraph after the figure 7-10; The direction of the connection seems counter-intuitive since the information flow (the initial value of the popup) is from the popup to the Controller. I guess "information flow" is the wrong way to think about this (aren't we dealing with references here?), but I lack the mental image of how this works. Please explain further in the text. (231) 4th paragraph (i.e. step 10 code to type in); The first line of bold is: @interface Controller(NSApplicationNotifications) but should be: @interface Controller(ApplicationNotifications) That is the category name should match in the Category.h and Category.m files. [234] first paragraph; "Refer to the Cocoa Foundation documentation." This is not the first place this phrase appears. A brief description of how to navigate in PB to the documentation based on a highlighted selection in the code would be useful. {235} 3rd code listing; In the line: id cell = [cellList objectAtIndex: i]; "cellList" should be "cells" {236} N/A; The "applicationDidFinishLaunching" delegate method is not updated to disable keypad buttons that are not applicable for the given radix. "Works" using the default base- 10, but if has a different default radix is specified in IB, the keypad is incorrect on startup. Can't just call the setRadix as implemented from the delegate.... {238} Step 5.; The autosizing of the function keys should not match that of the readout, but rather that of the digit button matrix. That is, if they should remain anchored to the bottom, as in Figure 7-15. {240} setRadix: method; Why not declare ysize to be a float? Both NSSize and NSRect have fields that are floats, and ysize is used in calculations with the fields of these structs. Although declaring it to be a double doesn't hurt, it leaves me wondering why. [260] entire chapter on events; I'm repeating myself here, but the discussions of keyboard events and event chains in general (in addition to objects and messages) just cry out for some diagrams. For example, look at pages 224 and 225 in Aaron Hillengass's "Cocoa Programming for Mac OS X". {265} step 7, last sentence; You must Control-click on the CalcWindow subclass, not just anywhere in the nib window. [269] last line; In checkView, I believe there is a more straightforward way (and indeed, a more robust way in this case) to test the class, as follows: if ( [aView isKindOfClass: [NSMatrix class] ] ) { could be simplified to: if ( [aView class] == [NSMatrix class] ) { and on page 270: if ( [aView isKindOfClass: [NSButton class] ] ) { easier: if ( [aView class] == [NSButton class] ) { If my logic is not equivalent or better, then the text could explain why. {270} checkView method, calls to [self checkButton/checkMatrix: aView];; Two warnings when compiling the Calculator app in Chapter 8. To remove the warnings and match the downloadable code, in the 'checkView' method, the aView parameter passed to 'checkMatrix' and 'checkButton' needs to be cast to the type of the method parameters: (NSMatrix *) and (NSButton *), respectively. {271} 1st word on that page; The 1st word in this page is "NSButtons". It should be "NSCells". {272} 2nd paragraph; Describing the performClick: method, the last sentence in this paragraph says "The message is not sent if the on-screen button is disabled." It looks as though the message is indeed sent, but the button doesn't respond to it if it is disabled. {273} first footnote; In addition to the compiler warning discussed in the footnote, there are also warnings in the checkView method as follows: [self checkMatrix: aView]; passing arg 1 of `checkMatrix:' from incompatible pointer type [self checkButton: aView]; passing arg 1 of `checkButton:' from incompatible pointer type these can be fixed by adding (NSMatrix *) and (NSButton *) casts, respectively, as follows: [self checkMatrix: (NSMatrix *)aView]; [self checkButton: (NSButton *)aView]; Or, an (id) cast could be used in both cases. [276] top of page; In addition to the errors in the Calculator code itself (that are fixed in the exercises), there appears to be some errors in the nibs (indicated by the little yellow "!" attached to the instances icons). In my case, the "MainMenu" instance of MainMenu.nib shows "One of the children has a bad/missing connection") and the "File's Owner" instance in both nibs show "unconnected outlet". [276] Exercises; Suggestion. Other good exercises would be to program the Delete key to delete the least-significant digit from the calculator display and the Return key to perform the "=" function. Also, how about adding an RPN mode? [270] and next page; If you want to investigate special characters (delete key, enter, etc.) put this in your keyDown method: NSLog([theEvent characters]); Also, see NSEvent.h. Then implement them in your checkButton method: if (@selector(clearAll:) == [aButton action]) { //clear key (note capital %C for unicode!) [keyPressTable setObject: aButton forKey: [NSString stringWithFormat: @"%C", NSClearLineFunctionKey]]; //delete key [keyPressTable setObject: aButton forKey: [NSString stringWithFormat: @"\177"]]; //escape key [keyPressTable setObject: aButton forKey: [NSString stringWithFormat: @"\033"]]; } else if ([title isEqualToString: @"="]) { //enter key for equals [keyPressTable setObject: aButton forKey: [NSString stringWithFormat: @"\003"]]; //return key for equals [keyPressTable setObject: aButton forKey: [NSString stringWithFormat: @"\r"]]; //equals for equals [keyPressTable setObject: aButton forKey: title]; } else if ([title isEqualToString: @"w"]) { //option forward slash for division (change your nib to w) [keyPressTable setObject: aButton forKey: title]; //forward slash for division [keyPressTable setObject: aButton forKey: [NSString stringWithFormat: @"/"]]; } else if (1 == [title length] && [aButton isEnabled]) { [keyPressTable setObject: aButton forKey: [title uppercaseString]]; [keyPressTable setObject: aButton forKey: [title lowercaseString]]; } (288) 4th entry in Table 9-2; "ATS" stands for "Apple Type Services" and not "Adobe Type Server". The ATSServer is a separate process that registers plugin scalers (like the TrueType and Type 1 scalers), and maintains a cache of rendered glyphs which it provides to clients. This prevents the same glyphs from having to be hinted and rendered separately for every process in the system. {290} Exercise 5; The exercise suggests examining the "vmstat" command in the Terminal. I haven't checked every possible version of OS X, but in 10.1.4 the command is spelled "vm_stat", and does not take the parameters described in the exercise. {302} 1st paragraph (after example); Text recommends using Control-C (interrupt) to abort the program. It's both more elegant and more correct to use Control-D to generate an end-of-file, which terminates the input and the program cleanly. {305-307}, in the section "Changing the Names of MathPaper Project Files" I found that the steps for renaming files can be simplified. Also, an important step was left out that subsequently causes a bug. * All the steps described requiring one to go to the Finder and renaming folders can more easily be done within Project Building using the menu item Project -> Rename. Using this method also changes the names of the files and directories in the file system and obviates the need to delete and add files in Project Builder. * A bug is introduced if a change in the Info.plist file of the Products folder is not made. The string "MathDocument" replaces "MyDocument" and should read as: NSDocumentClass MathDocument Omitting this step results in the application not being able to create a new document. {307} Changing the Names of MathPaper Project Files, after point 26; It's necessary to change the NSDocumentClass in the Application Settings from "MyDocument" to MathDocument". Without this the built program reports it can't create a window when it's run. User needs to select the "Target" tab, and select "MathPaper" in the left pane. From there, a "Application Settings" tab appears in the right pane. Select that, click on the "Expert" button, and open the CFBundleDocumentTypes array. Open entry 0 and change the value of "NSDocumentClass" from "MyDocument" to "MathDocument". While this is a minor error, the complexity of finding where to correct it is beyond the grasp of anyone who hasn't done it previously! {311} Figure 10-8; The screenshot is missing the "Show All" menu item below the "Hide Others" menu item. {311} Step 38; Step 38 on page 311 says to remove the space below the Preferences menu. The image shown in Figure 10.8 includes this space, going against the last sentence in step 38: "Your MathPaper application menu should now look exactly like the one in Figure 10-8." (314) Between step 45 and 50 the order is wrong (49 right after 46?) {314} Step 49; The 'Identifier' field is found under 'Basic Information'. As Step 49 is between a number of steps in the 'Document Types' setting groups, it is a little confusing. (Note: This is with PB 2.0 April 2002 beta.) [327] steps to buid executable location; My PB is 2.0.1 the command: [NSBundle mainBundle] pathForAuxiliaryExecutable: @"Evaluator" look at Contents folder (that is build/MathPaper.app/Contents in our example} >From PB I cannot move to Contents folder of product. No bar line shown up It may be PB2.0.1 change the GUI layout, I I cannot find "New Build Phase -> New Copy Files Build Phase command" {328} Step 28 and next paragraph; Step 28 says to "Drag the recently added Evaluator executable into the Resources group," and the following paragraph after Step 29 says that Figure 11-8 should list Evaluator as both a resource and a product. It is only listed as a Produt in the Figure. [330] Step 9: Type Command-1 (not Command-5) and change... (see also unofficial errata dated 5/22/2002: (330) Step 3 and 4 (and elsewhere); Searching for the NSWindowController class is not a good suggestion, it is much easier and faster to select it in the Pop-Down menu. Subclassing from the Classes menu is less efficient than simply use the context menu directly over the NSWindowController listing. This also goes for creating new outlets and actions, as well as reading declaration files and creating new files, of course. (333) The last paragraph of the page refers to the first and third lines of - (void)windowDidLoad method; the paragraph should refer to the first and fourth lines. [339] text; Another way to get lastLine with two lines of code: NSArray *paragraphs = [[theText string] componentsSeparatedByString: @"\n"]; NSString *lastLine = [paragraphs objectAtIndex: [paragraphs count] - 2]; (341) Step 8.; A space is missing between NSMakeRange(len,0) and the second parameter - withString:str {341} Second paragraph; The second paragraph, which attempts to explain the steps in the gotData: code, has a small error. The sentence that starts "The third statement..." indicates an autorelease is done on the variable "str". In fact, that is NOT done at all, and instead the "str" variable is forced released at the end of the method instead. That sentence should be removed, and a new sentence inserted at the end of the paragraph with something like "Finally, we release the storage for the str variable to not cause a memory leak" . [342] point 10.; While this may not really qualify as Errata, I found the following problem: After succcessful compilation the program failed to show any document windows, reporting 2006-11-05 18:40:16.290 MathPaper[10718] *** -[NSCFDictionary setObject:forKey:]: attempt to insert nil value in the Run Log window. The same happened with the code downloaded from the web site so I will assume some incompatibility with newer versions of XCode (I am running 2.4 right now). As I just started Cocoa programming using this book I feel unable yet to figure out what might have gone wrong and would appreciate any hints on how to proceed ... [342]point 10; Using old-fashioned debugging with printf I found the problem was the line [evaluator setLaunchPath:path]; in windowDidLoad in PaperController.m see code on page 333 Looking further, the problem is that the line path=[[NSBundle mainBundle] pathForAuxiliaryExecutable=@"Evaluator"]; in windowDidLoad fails to work and returns null. That explains the error message triggered by setLaunchPath a few lines later. A workaround is to hardcode the path as an NSString. I am not yet clear why pathForAuxiliaryExecutable fails to work. {344} Step 22; The bold text indicates that the code goes in the AUTORELEASE method, yet it plainly goes in the dealloc method. Also, the code sample shown for the dealloc method is NOT the correct one. It is missing the statement: [ [ NSNotificationCenter defaultCenter ] removeObserver: self ] ; which should go before the [evaluator termiante]. The proper code for dealloc should look like: // Clean up resouces when we are done with a MathDocument - (void)dealloc { [ [ NSNotificationCenter defaultCenter ] removeObserver: self ] ; [evaluator terminate] ; [evaluator release] ; [super dealloc] ; } {356} RTF.h @implementation code; In the RTF.h file, the dealloc method is declared but this is not needed since we are sub-classing from NSObject which has it defined already, similar to our override of init(). [357] in the -data method;; In order to avoid memory leakage, data2 shoud be autoreleased. Thus, the last line should be: return [data2 autorelease]; {357} in the -dealloc method; The last line of the -dealloc method returns [super dealloc], however the method definition states that it should not return any value. Thus, the word "return" should be deleted. {357} The header definition is incorrect in - (id)init { NSString *header = @"{\\rtf1\\mac{\\fontbl\\f0\\fswiss Helvetica;}\\f0\\fs24"; [super init]; data = [NSMutableData dataWithData:[header dataUsingEncoding:NSASCIIStringEncoding] ]; [data retain]; return self; } replace header definition with NSString *header = @"{\\rtf1\\mac{\\fontbl\\f0\\fswiss}\\f0\\fs24 "; {358} Implementation of RTF class - (void)setSize:(float)aSize { [self appendRTF:[NSString stringWithFormat:@"\\fs%d",(int)aSize*2]]; } should read: - (void)setSize:(float)aSize { [self appendRTF:[NSString stringWithFormat:@"\\fs%d ",(int)aSize*2]]; } The first (string) argument requires a trailing space, after "%d". This ensures proper parsing of the RTF data for some floating point numbers. For example, the result of entering "sin pi" is "1.22465e-16", and without the space, RTF like "\fs201 \cf0 .22465e-16" is emitted. I have no idea why this is so, it's easier simply to try it. {358} middle of page; I can't think of any good reason why the parameter to setSize: should be a float. We immediately cast it to an integer anyway. Maybe it's because it makes exercise 2 at the end of the chapter more interesting? At the very least, this needs an explanation. {360} Step 8. appendRTFData: declaration; The method appendRTFData: is declared with a NSData * parameter called "str", yet in the implementation the actual parameter is "data" . Thus, the code should read: - (void)appendRTFData: (NSData *) data ; {361} step 10; The method replaced by performing step 10 on page 361 is inconsistent with the original code on page 340. The original method contained a [str release] mesage at its conclusion, not the [str autorelease] shown on page 361. However, the [str autorelease] version of the code seems to be intended by the authors, as that same message was mentioned on page 341 in the sentence beginning, "The third statement sends...", even though it did not agree with the method listed on page 340. (366) Third paragraph; Saving the current location and size of the MathPaper window, as in the example, is a perfectly valid demonstration, of course. However, it would also be helpful to mention that this is handled automatically by NSWindow when you implement the methods setFrameUsingName: and setFrameAutosaveName:. {368} Step 3; The second line of the code example should read - (id)init instead of - (init) (368) Step 3.; The use of TRUE in [rtf setBold:TRUE]; is logically correct, but inconsistent with the use of YES and NO elsewhere; for example on p. 361: [rtf setBold:NO]; [368] step 4; All three #import statements should not have a semicolon at the end, and the file names should be surrounded with double quotes. #import "PaperController.h" #import "RTF.h" #import "MathDocument.h" {369} Step 4; The code added to windowDidLoad is intended (1) to display the data from the saved document and (2) to position the window in its previous location on the screen. The second part does not work. The window frame is set, but this appears to have no effect -- Cocoa simply cascades the window below and to the right of existing windows. I believe an additional line is needed to disable the default behavior: if ([doc hasFrame]) { [self setShouldCascadeWindows:NO]; // add this line [[self window] setFrame:[doc frame] display:YES]; } (374) Thir paragraph from the end; The line NSData *theData = |NSArchiver archiveDataWithRootObject:anObject] is misspelled. It must read NSData *theData = |NSArchiver archivedDataWithRootObject:anObject] {383} Step 2.; The initWithCoder: method returns self and should be typed with (id) like this: - (id)initWithCoder:(NSCoder *)coder [387] 4th full paragraph; With the current Project Builder, the default File->Print command sends printDocument:, not print: to the first responder object. The NSTextView doesn't respond to print: so nothing happens when you select Print. You can either change the action of the Print item to print: or override printDocument: in MathDocument.m: - (IBAction) printDocument:(id)sender { [[self windowControllers] makeObjectsPerformSelector:@selector(print)]; } Then you need to add a method to the PaperController object: - (void) print { [theText print:self]; } (389) Excersie 1; There should be a space after the bold "cat" and command.. It currently reads catcommand .. not quite right.. {396} Steps 13 and 15 (on page 397); These steps references the nib file "MathPaper.nib" which does not exist. It should read "MainMenu.nib" I believe. [396] Step 14; I am using the new April 2002 Dev tool set, and in this version to change the class for File's Owner, it is located under the general attributes section of the info panel, i.e. Command-1 and not Command-5. This may change again in the production release.. {413} inset note; The sentence should read, "M_PI is the ANSI C-defined constant for pi, which is the ratio of a circumference of a circle to [twice] its radius." Pi is the ratio of a circumference of a circle to its diameter. {414} First paragraph just above Step 6.; The text indicates that the [ self display ] method is called to cause display to be refreshed. However, the code clearly indicates that [ self setNeedsDisplay: YES ] is being called instead. {415} Code section at bottom of page; Similar to the previous error, the CODE for tick: now DOES call [ self display ] . However, as pointed out later in the book (page 426 and 427 to be precise) [ self display ] should hardly ever be called, and certainly this is not a case when it should. The code should remain at the more proper: [ self setNeedsDisplay: YES ] logic that was there a page previous.. (422) Second paragraph; The explanation does not take into account the considerable time that Project Builder spends on indexing the project and its imported frameworks during the first build. (428) Step 1 -- Middle of page; "... select them one by one and type Control-X." I believe Control-X should be Command-X [429] step #13; Despite having the user try to "add the takePercentage: action method to the BarView class in the BarView Class inspector", and mentioning that one "cannot select the rectangular BarView instance in the window to add outlets or actions", BarView is not listed under the "instances" tab. One can either connect to the "Window" instance (which will bring up the choice to choose the right action), or, contrary to the warning, control-drag directly to the BarView instance in the window and then connect the right action. At least for a beginner, this was a confusing subtlety maybe worth a tiny screenshot. {435} step 19; In step 19, the instructions say to drag PolygonView.h from PB to IB. In parentheses it says that you can also choose Classes -> Create Files for PolygonView. This is incorrect. In this case you would want to choose Classes -> Read PolygonView.h since we have updated this file in PB and want the changes to be read into IB. [455] Steps 5 & 6 -- Top of page; Steps 5 & 6 describe a multiple selection of files "grammar.y" followed by "rules.l" from the finder and then dragging them into PB's Resources group. When I followed these instructions explicitly I later got an error when I tried to compile, in step 8. The error something to the effect that a a file was missing. The fix is to drag the two files from the finder separately. Refer to page 325, indented paragraph, first sentence. "It is important that you add grammar.y before you load rules.l ..." It might also be helpful to mention that the instructions for building GraphPaper using the Evaluator are somewhat different than building MathPaper, which also uses the Evaluator. perhaps the difference is because MathPaper is document-based. {455} Step 11; Step 11 instructs you to add the compiled Evaluator file to the GraphPaper target by using Project->Add Files and selecting Evaluator in the build directory. Project Builder wouldn't let me select Evaluator in this way (Evaluator was greyed out). I found it easier to add Evaluator as a dependent project of GraphPaper by using the drag and drop method in the targets pane as described at the top of page 326. {460} Step 28.; It looks as if the @public ivar char *formula isn't needed. The program works without it, and it clashes with the internal variable 'formula' in the sendData method, generating two compiler warnings. {462} Bottom, under step 30; The line: - initWithFrame:(NSRect)frame should probably be: - (id)initWithFrame:(NSRect)frame [463] Middle of page; an error in the code, left out a third set of brackets: evaluator = [[NSTask alloc] init] retain; [evaluator setLaunchPath:path]; should be: evaluator = [[[NSTask alloc] init] retain]; [evaluator setLaunchPath:path]; [469] code; sendData works when called normally without an argument (i.e., [self sendData];). However, it cannot be detached as a thread, using Dec. 2002 Developer Tools, unless it is declared as - (void)sendData:(id)arg. arg does not have to be used in sendData, but the program does not work as printed--an exception is immediately raised that there is no autorelease pool. Adding an argument to the declaration of sendData immediately fixes the problem. {474} Step 44.; I believe that the line [super init]; is unnecessary, as the NSObject init does nothing, it only returns self. Removing it does not break the program. [479] Step 10 -- Lower third; Step 10 omits an ";" - (void)dealloc { [dict release] [text release]; [super dealloc]; } should be: - (void)dealloc { [dict release]; [text release]; [super dealloc]; } (483) Add the following exercise: 6. GraphPaper recognizes 'pi' as the ratio of a circle's circumference to its diameter. Mathematicians prefer to use the Greek symbol '<' (option-p on the keyboard). Improve GraphPaper to recognize '<'. The easy way is to modify -sendData to parse the string 'formula', substituting 'pi' for '<'. The cleaner and more extensible way is to modify Evaluator to recognize '<'. -sendData will still need a slight change. {488} 2nd paragraph; "If a user clicks the border of an NSColorWell, the Colors panel is displayed." You can click anywhere in an NSColorWell and get the same behavior. {493} Step 22; "Instantiate the Controller class" should be "Instantiate the PrefController class" {493} Step 24, 4th sentence; "... and three Message Text icons from the Cocoa-Views palette and drop them all in the new panel." "Message Text" should be "System Font Text". [523] thru p526; I have been building this project somewhat differently than the book describes. I have been saving the generated curve directly into a Bezier curve. Then rather than scale the view down to the bounds of the graph I scale the Bezier curve up to the coordinates of the view and draw with a 1 pixel pen. Done this way the lines do not require scaling to draw them. A 1 pixel line is stil one pixel no matter how the image is scaled and is symmetric. Further, as the image is in screen coordinates rather than Bezier curve coordinates the print to PDF problems described on p 523 do not occur. The image prints properly to a pdf without the need of a temporary window p525, section 6. [533] setFormat code; In the message implementation for setFormat in Controller, the first line "savePanel = [NSSavePanel savePanel];" should be eliminated, and in fact has been in the downloadable code. If you use the code as printed in the book, it changes the format on a newly created panel, not the one attached to the window, and thus you can only ever create PDF files. {544} Step 4; Step 4 imports the ZoomScrollView.h header file into Controller.m, but this code is superfluous. The clear method called in the next step is declared in the GraphView.h header file which has already been imported into Controller.m. {559} between Step 35 and 36; The authors forget to mention that you have to replace the old GraphPaper application previously placed in the /Applications folder with the newly built GraphPaper application in order to get the Graph Formula service to work. {567} next-to-last line; Here the return value from tagAtPoint is compared to -1 meaning "no match found" but on page 566 in step 14 the code uses 0 for that purpose. {567} last line; There is no setObjectsToColor:forTag: method. This should be setObjectToColor:forTag. (584) Step 11; awakeFromNib is erroneously described as a class method. It is not a class method (+), just a method. [590] Step 13; "Change some colors and click the Revert button; notice how the original colors return to the Preferences panel." I have followed the instructions in the chapter, however my Revert button doesn't revert the color wells to their original colors. I downloaded and built the corresponding example program from your ftp site, but it does not appear to work either. {602} paragraph cocoa-dev ; URL is incorrect should read http://lists.apple.com/mailman/listinfo/cocoa-dev