Amazon.com Widgets Attempted answer to exercise 15-3
Welcome, Guest. Please login or register.
Did you miss your activation email?
May 21, 2013, 11:59:14 PM
Home Help Search chat Login Register   
News: Read this please.The Great Kangaroo Escape Looking for reviews of the 4th ed on Amazon!   Twitter:  @skochan
                     

+  Official Forum for Programming in Objective-C (the iPhone Programming Language) - Stephen Kochan
|-+  Old Stuff
| |-+  Answers to Exercises
| | |-+  Chapter 15
| | | |-+  Attempted answer to exercise 15-3
Pages: [1]   Go Down
Print
Author Topic: Attempted answer to exercise 15-3  (Read 995 times)
mdeh
Full Member
***
Posts: 166






« on: February 07, 2009, 01:45:27 PM »

AddressBook.h
Code: (Objective-C)
#import "AddressCard.h"
#import <Foundation/NSArray.h>



@interface AddressBook : NSObject {

NSString * bookName;
NSMutableArray * book;

}

-(id) initWithName: (NSString *) theName;
-(void) addCard: (AddressCard *) theCard;
-(int) entries;
-(void) list;
-(void) listMatches: (NSMutableArray *) aList;
-(void) dealloc;

/* -(AddressCard *) lookUp: (NSString *) theName; */
-(NSMutableArray *) lookUp: (NSString *) theName;
-(void) removeCard: (AddressCard *) aCard;
-(void) sort;

@end

AddressBook.m
Code: (Objective-C)
#import "AddressBook.h"



@implementation AddressBook

-(id) initWithName: (NSString *) theName
{
self = [super init];

if ( self)
{
bookName =[ [ NSString alloc] initWithString: theName];
book = [ [ NSMutableArray alloc] init];

}
return self;
}

-(void) addCard: (AddressCard *) theCard
{
[book addObject: theCard];
}

-(int) entries
{
return book.count;
}

-(NSMutableArray *) lookUp: (NSString *) theName;
{
NSMutableArray *theList = [[NSMutableArray alloc] init];
for ( AddressCard *theCard in book)
if ( [theCard.name  caseInsensitiveCompare: theName ] == NSOrderedSame )
[theList addObject: theCard];

if (theList.count != 0)
return theList;
else
return nil;

}



-(void) removeCard: (AddressCard *) aCard
{
[ book removeObjectIdenticalTo: aCard];
}

-(void) sort
{
[book sortUsingSelector: @selector(compareNames:)];
}


-(void) list
{


NSLog(@"******* AddressBook: %@   ********", bookName);

for ( AddressCard * theCard in book)
{
NSLog(@" Name:%-20s   %-32s", [theCard.name UTF8String], [theCard.email UTF8String] );
}


}

-(void) listMatches: (NSMutableArray *) aList
{
for ( AddressCard *theCard in aList)

NSLog(@" Name:%-20s   %-32s", [theCard.name UTF8String], [theCard.email UTF8String] );
}

-(void) dealloc
{
[bookName release];
[book release];
[super dealloc];
}


@end

AddressCard same as ex 15.2

main.m
Code: (Objective-C)
#import "AddressBook.h"

int main (int argc, const char * argv[]) {
    NSAutoreleasePool * pool = [[NSAutoreleasePool alloc] init];
AddressBook * book = [ [ AddressBook alloc] initWithName: @"Exercise 15-2"];
AddressCard * card1 = [ [ AddressCard alloc] init];
AddressCard * card2 = [ [ AddressCard alloc] init];
AddressCard * card3 = [ [ AddressCard alloc] init];
AddressCard * card4 = [ [ AddressCard alloc] init];
AddressCard * card5 = [ [ AddressCard alloc] init];
AddressCard * card6 = [ [ AddressCard alloc] init];
AddressCard * card7 = [ [ AddressCard alloc] init];
AddressCard * card8 = [ [ AddressCard alloc] init];
AddressCard * card9 = [ [ AddressCard alloc] init];
AddressCard * card10 = [ [ AddressCard alloc] init];

[card1 setName: @"Harry Potter" andEmail: @"hp@hogwarts.witchery"];
[card2 setName: @"Bat Man" andEmail: @"bats@batcave.com"];
[card3 setName: @"Invis Woman" andEmail: @"nosee@wu.gov"];
[card4 setName: @"Dick Cheney" andEmail: @"exwh1@dontcomeback.com"];
[card5 setName: @"Lord Nelson" andEmail: @"hmsvictory@trafalgar.sea"];

[card6 setName: @"Harry Potter" andEmail: @"hp@hogwarts.witchery"];
[card7 setName: @"Bat Man" andEmail: @"bats@batcave.com"];
[card8 setName: @"Invis Woman" andEmail: @"nosee@wu.gov"];
[card9 setName: @"Dick Cheney" andEmail: @"exwh1@dontcomeback.com"];
[card10 setName: @"Lord Nelson" andEmail: @"hmsvictory@trafalgar.sea"];


[book addCard: card1];
[book addCard: card2];
[book addCard: card3];
[book addCard: card4];
[book addCard: card5];
[book addCard: card6];
[book addCard: card7];
[book addCard: card8];
[book addCard: card9];
[book addCard: card10];

[book list];

NSMutableArray * theList;

NSLog(@"Looking up \"Harry Potter\"");
theList = [book lookUp: @"Harry Potter"];
if ( theList != nil)
[book listMatches: theList ];
else
NSLog(@"No matches found");


NSLog(@"Looking up \"Invis Woman\"");
theList = [book lookUp: @"Invis Woman"];
if ( theList != nil)
[book listMatches: theList ];
else
NSLog(@"No matches found");


NSLog(@"Looking up \"Lord Nelson\"");
theList = [book lookUp: @"Lord Nelson"];
if ( theList != nil)
[book listMatches: theList ];
else
NSLog(@"No matches found");



NSLog(@"Looking up \"George\"");
theList = [book lookUp: @"George"];
if ( theList != nil)
[book listMatches: theList ];
else
NSLog(@"No matches found");




[theList release];
[card1 release];
[card2 release];
[card3 release];
[card4 release];
[card5 release];
[card6 release];
[card7 release];
[card8 release];
[card9 release];
[card10 release];
[book release];
    [pool drain];
    return 0;
}


Logged
airhead3
Newbie
*
Posts: 9






« Reply #1 on: August 21, 2009, 06:23:31 AM »


-(NSMutableArray *) lookUp: (NSString *) theName;
{
   NSMutableArray *theList = [[NSMutableArray alloc] init];
   for ( AddressCard *theCard in book)
      if ( [theCard.name  caseInsensitiveCompare: theName ] == NSOrderedSame )
         [theList addObject: theCard];
   
   if (theList.count != 0)
      return theList;
   else
      return nil;
   
}

Help me out here - since you allocated your NSMutableArray in your lookUp method and you don't release it anywhere, doesn't that mean there's a memory leak here?

You release the array in the main program that is assigned to the array in the method, but does that release the memory for both? Even if it does, if you return no results, then it's never assigned.

What I'm having trouble with is how best to handle this - it seems that having to remember to release the object in the main program is rather inelegant, but I'm not sure what alternatives there are....
Logged
rgronlie
Global Moderator
Full Member
*****
Posts: 212







« Reply #2 on: August 21, 2009, 12:46:55 PM »

Yes this lookUp: method does leak if no match is found and it also leaks when lookUp: is called again without releasing the previous theList in main.

Quote
You release the array in the main program that is assigned to the array in the method, but does that release the memory for both? Even if it does, if you return no results, then it's never assigned.

There is only one object (array) here (created by calling alloc). theList variable in the method and theList variable in main are merely pointers to an object... they aren't objects themselves and in this case they both point to the same object.

Think of an object as your house and a pointer to the object as your street address. You can give out your address to as many people as you want... but you still have only one house.

Quote
What I'm having trouble with is how best to handle this - it seems that having to remember to release the object in the main program is rather inelegant, but I'm not sure what alternatives there are....

Inelegant method #1:
In the lookUp: method have theList released if no match is found.
Code: (Objective-C)
    if (theList.count != 0)
        return theList;
    else
    {
        [theList release];
        return nil;
    }
And then in main make sure you release theList after each lookup. For example...
Code: (Objective-C)
    NSLog(@"Looking up \"Harry Potter\"");
    theList = [book lookUp: @"Harry Potter"];
    if ( theList != nil)
    {
        [book listMatches: theList ];
        [theList release];
    }
    else
        NSLog(@"No matches found");

Inelegant method #2:
Always return theList from the lookUp: method even if no match was found.
Code: (Objective-C)
-(NSMutableArray *) lookUp: (NSString *) theName;
{
    NSMutableArray *theList = [[NSMutableArray alloc] init];
    for ( AddressCard *theCard in book)
        if ( [theCard.name  caseInsensitiveCompare: theName ] == NSOrderedSame )
            [theList addObject: theCard];
  
    return theList;
}

Then in main change the way you check for a successful lookUp. For example:
Code: (Objective-C)
    NSLog(@"Looking up \"Harry Potter\"");
    theList = [book lookUp: @"Harry Potter"];
    if ([theList count])
        [book listMatches: theList];
    else
        NSLog(@"No matches found");
    [theList release];

Elegant method:
Skip ahead and read chapter 17. Then change the lookUp: method to use autorelease.
Code: (Objective-C)
-(NSMutableArray *) lookUp: (NSString *) theName;
{
    NSMutableArray *theList = [[[NSMutableArray alloc] init] autorelease];
    for ( AddressCard *theCard in book)
    if ( [theCard.name  caseInsensitiveCompare: theName ] == NSOrderedSame )
        [theList addObject: theCard];
  
    if (theList.count != 0)
        return theList;
    else
        return nil;
  
}
Then keep main the same as the original code but don't call [theList release];
« Last Edit: August 21, 2009, 12:49:17 PM by rgronlie » Logged

Sanity: Minds are like parachutes. Just because you've lost yours doesn't mean you can borrow mine.
airhead3
Newbie
*
Posts: 9






« Reply #3 on: August 21, 2009, 01:50:06 PM »

Thanks for the clarification, rgronlie!

Here's my attempt at elegant solution #2 (without having to skip ahead to Chapter 17) Wink
Code: (Objective-C)
-(NSMutableArray *) lookup: (NSString *) searchString
{
NSMutableArray *searchBook = [NSMutableArray arrayWithCapacity:0];
for (AddressCard *nextCard in book) {
.......

My thinking on this: Because I let the NSMutableArray do the allocation, I don't have to worry about releasing it. (Ultimately for the same reason as your solution, I think, in that it adds it to the autorelease pool.)

Wouldn't this achieve the same results as your explicit add to autorelease?
Logged
rgronlie
Global Moderator
Full Member
*****
Posts: 212







« Reply #4 on: August 21, 2009, 02:26:42 PM »

Yup... that would work.

You could also use:

NSMutableArray *searchBook = [NSMutableArray array];

Same thing as creating an array with a capacity of 0.
Logged

Sanity: Minds are like parachutes. Just because you've lost yours doesn't mean you can borrow mine.
webwrx
Newbie
*
Posts: 41






« Reply #5 on: August 22, 2009, 07:35:31 PM »

I tried two approaches to solve exercise 3.  My first approach returns the results in an array, my second approach creates a whole new book containing the results.

First the "array" approach:

Code: (Objective-C)
-(NSMutableArray *) lookupAll: (NSString *) partName
{
NSMutableArray *results = [NSMutableArray arrayWithCapacity: 10];
NSRange substr;
for (AddressCard *nextCard in book) {
substr = [[nextCard name] rangeOfString: partName options: NSCaseInsensitiveSearch];
if (substr.location != NSNotFound)
[results addObject: nextCard];
}
return results;
}

It is called from main.m using this code, which also prints it. I run an enumeration on the array to send the contents to NSLog to verify it worked.

Code: (Objective-C)
        NSMutableArray *searchResults;
NSLog (@"list all cards matching partial search 'j'");
searchResults = [myBook lookupAll: @"j"];
if ([searchResults count] != 0) {
for (AddressCard *theCard in searchResults)
NSLog(@"%-20s       %-32s", [theCard.name UTF8String], [theCard.email UTF8String]);
}
else
NSLog (@"No partial matches found");



Then I figured well this is pretty sloppy, because we already have list and sort methods... so why am I effectively re-writing another list to test the array, therefore it made more sense to me for the whole thing to create a new AddressBook book, put the search results into there - and that makes it easier to just list the results, etc..

Code: (Objective-C)
-(AddressBook *) makeNewResultsBook: (NSString *) partName
{
NSRange substr;
AddressBook *resultsBook = [[AddressBook alloc] initWithName: @"Search Results"];
for (AddressCard *nextCard in book) {
substr = [[nextCard name] rangeOfString: partName options: NSCaseInsensitiveSearch];
if (substr.location != NSNotFound)
[resultsBook addCard: nextCard];
}
return resultsBook;
}

Called from main.m as such -
Code: (Objective-C)
	AddressBook *resultsBook;
NSLog (@"list all cards matching partial search 'ne'");
resultsBook = [myBook makeNewResultsBook: @"ne"];
[resultsBook list];
[resultsBook release];


Ben
Logged
Pages: [1]   Go Up
Print
Jump to:  



Login with username, password and session length

Powered by MySQL Powered by PHP Powered by SMF 1.1.11 | SMF © 2006-2009, Simple Machines LLC Valid XHTML 1.0! Valid CSS!
Entire forum contents (c) 2009 classroomM.com. All rights reserved.