Amazon.com Widgets 18-1 Simple way
Welcome, Guest. Please login or register.
Did you miss your activation email?
September 01, 2014, 11:44:21 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 18
| | | |-+ 18-1 Simple way
Pages: 1 [2] Go Down
Print
Author Topic: 18-1 Simple way (Read 7613 times)
skochan
Administrator
Hero Member
*****
Posts: 3114







Reply #15 on: February 25, 2010, 07:13:29 AM

Ok, we're not done here yet.   You guys will be experts on this stuff by the time you're done here!   Grin

Because you changed all the property attributes to assign in both your classes, there are some implications:

1. The AddressBook copyWithZone: and mutableCopyWithZone: methods now leak the array created by the initWithName: method, as they're not getting released.  If you use the retain property for the book instance variable, the old value will be released before the new one is assigned and retained.
2. For all your AdressCard instance variables,  if a mutable string is passed as the value for any of those instance variables (e.g., for a setEmail: method call), then the string won't get copied and won't be owned by the AddressCard class.  In fact, because you use assign, it can even be released and thereby invalidate that field in the address card.   You should use the copy attribute for these properties
3. For the AddressCard class, does it make sense to support a mutableCopy?  I don't think so (tell me how that would be used).  And in the case of copying an AddressCard, you'd want to use retain (and not assign) for the attribute for all those instance variables in case the card that's being copied gets subsequently released.

So remember the following:

1. assign does a straight assignment, overwriting the old value of the instance variable.
2. retain releases the old value, and then assigns and retains the new one.
3. copy releases the old value and then does a copy of the new one.  For collections and string objects, this is an immutable copy.

Keep plugging along....


Cheers,

Steve Kochan
Last Edit: February 25, 2010, 09:51:20 AM by skochan Logged
tadej5553
Full Member
***
Posts: 145


Email




Reply #16 on: February 25, 2010, 07:54:34 AM

Yes, I agree with everything Steve said Cool.

I also noticed this:

In mutableCopyWithZone: in AddressBook you could do with only on AddressCard

here's how I think you should do it:
Code: (Objective-C)
-(id) copyWithZone: (NSZone *) zone {  
    AddressBook *newBook = [[AddressBook allocWithZone: zone] initWithName: @""]; 
    NSMutableArray *newBookArray = [book copy]; 
    [newbook.book release];

    newBook.book = newBookArray; 
    [newBookArray release]; 
     
    return newBook; 

 
-(id) mutableCopyWithZone: (NSZone *) zone { 
    AddressBook *newBook = [[AddressBook allocWithZone: zone] initWithName: @""]; 
     
    for (AddressCard *card in book) { 
        AddressCard *copy = [card mutableCopy]; 
         
        [newBook.book addObject: copy]; 
        [copy release]; 
    } 
     
    return newBook; 

P.S.: You said that you copy-pasted all of my code and it didin't work. I doubt that.
I ran the code and it gave me no errors. At all. So you should get the same result if you copy-paste my code.
Logged
kadegray
Newbie
*
Posts: 12


Email




Reply #17 on: February 25, 2010, 04:11:02 PM

@Steve: Thank you, all leak free now, its well an truly locked into my head the assign, retain, and copy attributes now..

@tadej5553: I've just copy and pasted your code into a new project for the third time, and still getting the same warnings, maybe its a version thing and I or you need to update or something..

Should I re-post my new leak free code? for the sake of this thread and for your approval.
Logged
skochan
Administrator
Hero Member
*****
Posts: 3114







Reply #18 on: February 25, 2010, 05:41:39 PM

Should I re-post my new leak free code? for the sake of this thread and for your approval.

Yes, please!

Cheers,

Steve Kochan
Logged
kadegray
Newbie
*
Posts: 12


Email




Reply #19 on: February 25, 2010, 08:20:46 PM

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

@interface AddressBook: NSObject <NSCopying> {
NSString *bookName;
NSMutableArray *book;
}

@property (retain, nonatomic) NSString *bookName;
@property (retain, nonatomic) NSMutableArray *book;

- (id) initWithName: (NSString *) name;

- (void) addCard: (AddressCard *) theCard;
- (void) removeCard: (AddressCard *) theCard;

- (NSMutableArray *) lookup: (NSString *) theName searchOnlyFullName: (BOOL) nameOrAll;
- (BOOL) removeName: (NSString *) theName;
- (int) entries;
- (void) list;
- (void) sort;

- (void) dealloc;

@end

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

@implementation AddressBook

@synthesize book, bookName;

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

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

return self;
}

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

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

- (NSMutableArray *) lookup: (NSString *) lookupString searchOnlyFullName: (BOOL) nameOrAll {
NSMutableArray *resultArray = [[NSMutableArray alloc] init];

for (AddressCard *nextCard in book){
NSString *space = [NSString stringWithString:@" "];
if( nameOrAll == YES ){
if( ([[nextCard.firstName stringByAppendingString: [space stringByAppendingString: nextCard.lastName]] rangeOfString: lookupString options: NSCaseInsensitiveSearch].location) != NSNotFound ){
[resultArray addObject: nextCard];
}
} else {
if( ([[nextCard getIndexOfAll] rangeOfString: lookupString options: NSCaseInsensitiveSearch].location) != NSNotFound ){
[resultArray addObject: nextCard];
}
}
[space release];
}

if( [resultArray count] == 0 ){
resultArray = nil;
}

return resultArray;
}

- (BOOL) removeName: (NSString *) theName {
NSMutableArray *resultArray = [[NSMutableArray alloc] init];
resultArray = [self lookup:theName searchOnlyFullName:YES];

if( [resultArray count] == 1 ){
[self removeCard:[resultArray objectAtIndex:0]];
[resultArray release];
return YES;
} else {
[resultArray release];
return NO;
}
}

- (int) entries {
return [book count];
}

- (void) list {
NSLog(@"========= Contents of: %@ =========", bookName);

for(AddressCard *theCard in book){
NSLog(@"Name: %s %s    Email: %s", [theCard.firstName UTF8String], [theCard.lastName UTF8String], [theCard.email UTF8String]);
}

NSLog(@"=====================================================");
NSLog(@"\n");
}

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

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

-(id) copyWithZone: (NSZone *) zone {
    AddressBook *newBook = [[AddressBook allocWithZone: zone] initWithName: @""];

for (AddressCard *card in book) {
AddressCard *copy = [card copy];

[newBook.book addObject: copy];
[copy release];
}

    return newBook;
}

@end

AddressCard.h:
Code: (Objective-C)
#import <Foundation/NSObject.h>
#import <Foundation/NSString.h>

@interface AddressCard : NSObject <NSCopying> {
NSString *firstName;
NSString *lastName;
NSString *email;
NSString *street;
NSString *state;
NSString *city;
NSString *zip;
NSString *country;

NSString *indexOfAll;
}

@property (copy, nonatomic) NSString *firstName, *lastName, *email, *street, *state, *city, *zip, *country;

- (void) setFirstName: (NSString *) theFirstName
  andLastName: (NSString *) theLastName
andEmail: (NSString *) theEmail
andStreet: (NSString *) theStreet
andState: (NSString *) theState
  andCity: (NSString *) theCity
   andZip: (NSString *) theZip
   andCountry: (NSString *) theCountry;

- (void) print;

- (NSString *) getIndexOfAll;

- (void) dealloc;

- (NSComparisonResult) compareNames: (id) element;

@end

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

@implementation AddressCard

@synthesize firstName, lastName, email, street, state, city, zip, country;
- (void) setFirstName: (NSString *) theFirstName
  andLastName: (NSString *) theLastName
andEmail: (NSString *) theEmail
andStreet: (NSString *) theStreet
andState: (NSString *) theState
  andCity: (NSString *) theCity
   andZip: (NSString *) theZip
   andCountry: (NSString *) theCountry {
self.firstName = theFirstName;
self.lastName = theLastName;
self.email = theEmail;
self.street = theStreet;
self.state = theState;
self.city = theCity;
self.zip = theZip;
self.country = theCountry;
}

- (void) print {
NSLog(@"===================================");
NSLog(@"|                                 |");
NSLog(@"|  First Name: %-19s|", [firstName UTF8String]);
NSLog(@"|  Last Name: %-20s|", [lastName UTF8String]);
NSLog(@"|  Email: %-24s|", [email UTF8String]);
NSLog(@"|  Street: %-23s|", [street UTF8String]);
NSLog(@"|  State: %-24s|", [state UTF8String]);
NSLog(@"|  City: %-25s|", [city UTF8String]);
NSLog(@"|  Zip: %-26s|", [zip UTF8String]);
NSLog(@"|  Country: %-22s|", [country UTF8String]);
NSLog(@"|                                 |");
NSLog(@"===================================");
}

- (NSString *) getIndexOfAll {
indexOfAll = firstName;
indexOfAll = [[indexOfAll stringByAppendingString: @" "] stringByAppendingString:lastName];
indexOfAll = [[indexOfAll stringByAppendingString: @" "] stringByAppendingString:lastName];
indexOfAll = [[indexOfAll stringByAppendingString: @" "] stringByAppendingString:email];
indexOfAll = [[indexOfAll stringByAppendingString: @" "] stringByAppendingString:street];
indexOfAll = [[indexOfAll stringByAppendingString: @" "] stringByAppendingString:state];
indexOfAll = [[indexOfAll stringByAppendingString: @" "] stringByAppendingString:city];
indexOfAll = [[indexOfAll stringByAppendingString: @" "] stringByAppendingString:zip];
indexOfAll = [[indexOfAll stringByAppendingString: @" "] stringByAppendingString:country];

return indexOfAll;
}

- (void) dealloc {
[firstName release];
[lastName release];
[email release];
[street release];
[state release];
[city release];
[zip release];
[country release];
[super dealloc];
}

- (NSComparisonResult) compareNames: (id) element {
return [[firstName stringByAppendingString: lastName] compare: [[element firstName] stringByAppendingString: [element lastName]]];
}

-(id) copyWithZone: (NSZone *) zone {

    AddressCard *newCard = [[AddressCard allocWithZone: zone] init];

[newCard setFirstName: firstName
  andLastName: lastName
andEmail: email
andStreet: street
andState: state
  andCity: city
   andZip: zip
   andCountry: country ];

    return newCard;
}

@end

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

int main (int argc, const char * argv[]) {
    NSAutoreleasePool * pool = [[NSAutoreleasePool alloc] init];

AddressCard *card1 = [[[AddressCard alloc] init] autorelease];
AddressCard *card2 = [[[AddressCard alloc] init] autorelease];
AddressCard *card3 = [[[AddressCard alloc] init] autorelease];

AddressBook *myBook = [[AddressBook alloc] autorelease];
NSMutableArray *lookupCards = [[NSMutableArray alloc] init];

[card1 setFirstName: @"Tony"
andLastName: @"Iannino"
   andEmail: @"tony.iannino@techfitness.com"
  andStreet: @"92 Jippy Drive"
   andState: @"ER"
andCity: @"Yippy"
andZip: @"5775"
andCountry: @"Yahw" ];

[card2 setFirstName: @"Julia"
andLastName: @"Kochan"
   andEmail: @"jewls337@axlc.com"
  andStreet: @"42 Setter Street"
   andState: @"YU"
andCity: @"Bingo"
andZip: @"1548"
andCountry: @"Ansettser" ];

[card3 setFirstName: @"AAAAA"
andLastName: @"AAAAAAAA"
   andEmail: @"jewls337@axlc.com"
  andStreet: @"42 Setter Street"
   andState: @"YU"
andCity: @"Bingo"
andZip: @"1548"
andCountry: @"Ansettser" ];

myBook = [myBook initWithName: @"Linda's Address Book"];

[myBook addCard: card1];
[myBook addCard: card2];
[myBook addCard: card3];

NSLog(@"Lookup: kochan");
lookupCards = [myBook lookup:@"jewls337@axlc.com" searchOnlyFullName:NO];

if(lookupCards != nil){
for(AddressCard *thisCard in lookupCards){
[thisCard print];
}

} else {
NSLog(@"Not Found!");
}

[lookupCards release];

[myBook list];

[myBook sort];
NSLog(@"Sorted:");

[myBook list];

if([myBook removeName:@"Julia"] == YES){
NSLog(@"Removed Julia:");
} else {
NSLog(@"Dident remove Julia:");
}

[myBook list];

AddressBook *myBookCopy = [[AddressBook alloc] init];

myBookCopy = [myBook copy];
myBookCopy.bookName = @"myBookCopy";

[myBookCopy addCard: card2];
NSLog(@"mutableCopy:");

[myBookCopy list];

[myBookCopy removeName:@"AAAAA AAAAAAAA"];

NSLog(@"removed AAAAA AAAAAAAA from myBookCopy:");
[myBookCopy list];

[pool drain];
    return 0;
}
Logged
tadej5553
Full Member
***
Posts: 145


Email




Reply #20 on: February 25, 2010, 10:50:48 PM

OK, so I just copy-pasted your code and I got no errors at all. Strange Huh
Logged
kadegray
Newbie
*
Posts: 12


Email




Reply #21 on: February 25, 2010, 11:47:00 PM

The code that I posted is my code and not an edited copy of your code. And I haven't done the setBook: method, which is were the warning occurs in your code.
Logged
tadej5553
Full Member
***
Posts: 145


Email




Reply #22 on: February 26, 2010, 07:58:05 AM

Well all I was trying to say is that your code runs without errors with me (I understood you had errors when running it)
Logged
kadegray
Newbie
*
Posts: 12


Email




Reply #23 on: February 26, 2010, 07:23:14 PM

In your code the setBook: method gets that warning. When I say 'my code' I mean the code that I originaly typed and didn't end up implementing your suggested setBook: method, therefore my code doesn't get the warning.  So yes my code runs without error's or warnings and your code run's, for me, with that warning for the setBook: method.

Thanks helping me through what turned out to be a rather lengthy exercise, yet worth it.
Last Edit: March 01, 2010, 04:41:57 PM by kadegray Logged
essio
Newbie
*
Posts: 30


Email




Reply #24 on: June 16, 2010, 02:29:46 PM

In your main yo have:

AddressBook *myBookCopy = [[AddressBook alloc] init]; 
myBookCopy = [myBook copy]; 

wouldnt it be enough:
 AddressBook *myBookCopy = [myBook copy];

Cuz in your copy method u are already doing an allocation... Doing it like you, isnt like two-times allocating?

Im still a little bit confused withe the @property options, copy, retain and assign... assign will make a straight assignment (no add in the reference count).. but retain and copy add up the count, so, what is the difference...

Another thing that i dont get is that u refer to a setter method in this example: newBook.book = [self.book mutableCopy];
self.book = X    is an example of setter, while   self.book   would be a call to the getter...

Logged
tadej5553
Full Member
***
Posts: 145


Email




Reply #25 on: June 17, 2010, 06:47:55 AM

In your main yo have:

AddressBook *myBookCopy = [[AddressBook alloc] init]; 
myBookCopy = [myBook copy]; 

wouldnt it be enough:
 AddressBook *myBookCopy = [myBook copy];

Cuz in your copy method u are already doing an allocation... Doing it like you, isnt like two-times allocating?

Im still a little bit confused withe the @property options, copy, retain and assign... assign will make a straight assignment (no add in the reference count).. but retain and copy add up the count, so, what is the difference...

Another thing that i dont get is that u refer to a setter method in this example: newBook.book = [self.book mutableCopy];
self.book = X    is an example of setter, while   self.book   would be a call to the getter...



You are right about the copy of myBook. It would be enough with only copy sentence.

The main difference between copy and retain (if you a re asking about that) is that copy makes a copy of the object (uses a copy method) while retain simply retains the object and then assigns it.

A '.' can be used in two ways: when you are not assigning the thing (xy.zx) it is only a getter method, but when you are assigning it (using the '=' sign) it is a setter method. This is not suitable on id objects, as the compiler doesn't know if they have that instance variable, so it gives you an error. When using id use methods instead
Logged
essio
Newbie
*
Posts: 30


Email




Reply #26 on: June 17, 2010, 12:15:12 PM

Okay , i thought that the only way to be a setter was when the = sign was at the right ( self.book = myBook )...  now i know that i'm also using the getter when is like this:  newBook.book = [self.book mutableCopy]  Because we are using the =.

I still don't get it quite right.. retain will add the reference count and would make another pointer to the same location. Doesn't copy do exactly that? looking at the examples programs in that chapter.. when i made a copy it add up its reference count but they pointed to the same location (when making a copy of an object to a class that could hold the same type of object... like copying a mutable string to a mutable string object).. The only difference i saw (if i didn't miss anything) is that if i did a copy of two different things then they would reference different locations in memory... for example i did a copy of a Immutable string to a mutable string... of course when I change the mutable string the other remain intact (cuz its immutable), an is kinda obvious they both had different locations in memory. 
Logged
tadej5553
Full Member
***
Posts: 145


Email




Reply #27 on: June 20, 2010, 11:44:11 PM

Okay , i thought that the only way to be a setter was when the = sign was at the right ( self.book = myBook )...  now i know that i'm also using the getter when is like this:  newBook.book = [self.book mutableCopy]  Because we are using the =.

I still don't get it quite right.. retain will add the reference count and would make another pointer to the same location. Doesn't copy do exactly that? looking at the examples programs in that chapter.. when i made a copy it add up its reference count but they pointed to the same location (when making a copy of an object to a class that could hold the same type of object... like copying a mutable string to a mutable string object).. The only difference i saw (if i didn't miss anything) is that if i did a copy of two different things then they would reference different locations in memory... for example i did a copy of a Immutable string to a mutable string... of course when I change the mutable string the other remain intact (cuz its immutable), an is kinda obvious they both had different locations in memory. 

This is hard to explain, so I'll give you a simple example.

Code: (Objective-C)
@interface Foo: NSObject
{
    NSString *s1;
    NSString *s2;
}
@property(retain, nonatomic) NSString *s1;
@property(copy, nonatomic) NSString *s2;

@end

@implementation Foo

-(void) setS1: (NSString *) ns
{
    [s1 release];
    s1 = [s1 retain];
}

-(void) setS2: (NSString *) ns
{
    [s2 release];
    s2 = [ns copy]; // this is equal to s2 = [[NSString alloc] initWithString: s2]
}

@end

I hope this clarifies things a bit.
Logged
iapplethis
Newbie
*
Posts: 13






Reply #28 on: May 21, 2011, 03:14:32 PM

OK, so I practically spent 7-8h straight with this exercise and this topic, going through everything I did not get. Basically when I wrote my code and later looked at what the next post said, it was like I new it already, so I am pretty happy with that, but I couldn't understand what is the difference when using mutableCopyWithZone: & mutableCopy: and only copyWithZone: & copy: cuz either way you have to implement the methods and they are called by copy/mutableCopy to do the whole job, don't they? When I look at the mutableCopyWithZone: & copyWithZone: in the AddressBook in the last post on 1st page and Steve's first post here, I think that the mutableCopyWithZone: does a DEEP copy and the other not, so what is the difference, at all? Thank you.
Logged
mkoos
Newbie
*
Posts: 9






Reply #29 on: August 05, 2011, 10:42:14 PM

1 - I agree with iapplethis, it's a little foggy.  It seems like it's more convention and good practice to use NSMutableCopying to produce an object that is mutable and NSCopying where the result will be immutable (because couldn't you theoretically achieve the either result using either protocol?).

2 - Just a thought, but in this addressBook scenario, would it make more sense to design the program so that each copy didn't own it's own address cards?  Then, you could edit a card and see the edit across all address books.  Otherwise, how would one replace the names within a copy?

Btw, this is so far the most difficult chapter of the book.  The large thread is greatly appreciated!!

Thanks everyone!
Last Edit: August 05, 2011, 11:09:34 PM by mkoos Logged
Pages: 1 [2] 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.