views:

151

answers:

2

I hope someone understands what happens to my NSMutableArray.

I read records a, b, c, d from a database, load the fields into an object an add the object to an array. To do this I read the records into an instance of that object (tmpEvent) and add the Object to the target array (NSMutableArray myArray).

the code looks like:

for (condition) {
     tmpEvent.field1 = [NSString stringWithUTF8String:(char*)sqlite3_column_text(stmt, 0)];
     tmpEvent.field2 = [NSString stringWithUTF8String:(char*)sqlite3_column_text(stmt, 1)];
     tmpEvent.field3 = [NSString stringWithUTF8String:(char*)sqlite3_column_text(stmt, 2)];

     NSLog(@"myArray: adding %@", tmpEvent.field1);
     [myArray addObject:tmpEvent];
}

The NSLog shows

 myArray: adding a
 myArray: adding b
 myArray: adding c
 myArray: adding d

Subsequent I enumerate the array (this can be in the same or a different method):

for (myObject *records in myArray) {
    NSLog(@"iEvents  value %@", records.field1);
}

The NSLog now shows:

 myArray value d
 myArray value d
 myArray value d
 myArray value d

a mystery .... ??? any thoughts?

A: 

How are the field1, field2 and field3 properties defined ? Are they defined with (retain) attribute ?

Laurent Etiemble
yes, they are all defined as (nonatomic, retain).
iFloh
+3  A: 

You need to allocate a new "iEvent" for each event. tmpEvent.field1 is pointing to the same place in memory for each subsequent add, therefore you are modifying the object that is already stored in the array. NSArray does not make a new copy of the object, just stores its pointer/address.

One fix:

[myArray addObject:[tmpEvent copy]];

This assumes the members of the iEvent class conform to NSCopy.

Another is to allocate a new tmpEvent for each event that you want to store.

Question: But does this mean that when I free my array agein I need to enumerate through and "manually" release the objects again?

Answer: You should send the object a release after adding it to the array as the array sends it a retain. When you dispose of the array, all objects are sent a release so you don't have to. See below...

Give the alloc option a try as conforming to NSCopying requires you to alloc an object anyway:

for (condition) { tmpEvent = [[TmpEvent alloc] init]; // Or however it is initialized

 tmpEvent.field1 = [NSString stringWithUTF8String:(char*)sqlite3_column_text(stmt, 0)];
 tmpEvent.field2 = ...

 [myArray addObject:tmpEvent];

// NSArray retains objects that you add. [tmpEvent release]; }

---- To Conform to NSCopy --- 1. Class must inherit from NSCopying @interface TmpEvent : NSObject ...

  1. Implement - (id) copyWithZone:(NSZone *) zone

    • (id)copyWithZone:(NSZone *)zone { TmpEvent *copyOfMyself = [[TmpEvent alloc] init]; copyOfMyself.field1 = [self.field1 copy]; .... etc.

    return copyOfMyself; }

Kenny
understand, especially like the "copy" alternative. But does this mean that when I free my array agein I need to enumerate through and "manually" release the objects again?
iFloh
hmmm, just tried the copy method. get an uncaught exception with unrecognized selector. How do i make my object conform to the NSCopy protocol? I did not find it in the apple docu ...
iFloh
iFloh
Sorry for the crappy formatting, just getting used to this site.
Kenny
IT's not NSCopy, it's NSCopying.
JeremyP
And it's documented here:http://developer.apple.com/mac/library/documentation/Cocoa/Reference/Foundation/Protocols/NSCopying_Protocol/Reference/Reference.html#//apple_ref/occ/intf/NSCopying
JeremyP
Calling `copy` without later calling `release` will cause a leak.
rpetrich