Array losing its contents after a load (XCode/Objective C)

Discussion in 'Mac Programming' started by AJClayton, Mar 4, 2008.

  1. macrumors 6502

    AJClayton

    Joined:
    Jan 9, 2007
    Location:
    Dorset, England
    #1
    Hi

    I'm new to Cocoa programming so apologise in advance that I'm likely to be missing something obvious. However, after several hours trying to fathom out a bug I'm completely stuck and would really appreciate some help!

    I'm using XCode/Interface Builder 3 and Objective C 2.0 with garbage collection enabled. I have a Document-based application. In MyDocument.nib I have an NSTableView. This has its dataSource set to MyDocument in Interface Builder. I've implemented

    - (NSInteger)numberOfRowsInTableView:(NSTableView *)aTableView
    and
    - (id)tableView:(NSTableView *)aTableView objectValueForTableColumn:(NSTableColumn *)aTableColumn row:(NSInteger)rowIndex

    In my application, I don't want users to be able to edit the table view directly. I've not used an NSArrayController. The user clicks a button, a sheet slides down from where various options are set including the text to be displayed in the table view. This all works fine and the table view happily displays the text I want, including amendments carried out via my sheet. So far, so good.

    The next thing for me to do, I thought to myself, was to implement saving. I have Aaron Hillegass' Cocoa book so implemented

    - (BOOL)loadDataRepresentation:(NSData *)data ofType:(NSString *)aType
    and
    - (NSData *)dataRepresentationOfType:(NSString *)aType

    rather than the alternatives offered by XCode 3 as I can't see how to use these (I've got Aaron's next edition on pre-order). However, I don't think that this is at the route of my problem.

    Clicking Save in my application successfully creates a saved file and if I open this in TextEdit I can see the values I've entered (amongst some binary data, of course). When I try to Open a saved file the load appears to work; a fact I've tested as follows:

    - (BOOL)loadDataRepresentation:(NSData *)data ofType:(NSString *)aType {
    NSLog(@"Loading data of type %@", aType);
    newArray = [NSKeyedUnarchiver unarchiveObjectWithData:data];

    if (newArray==nil) {
    return NO;
    } else {
    NSLog(@"Loaded data");
    [self setListOfGreetings:newArray];

    // Debugging - let's see if my array has actually got anything in it
    NSEnumerator *e = [listOfGreetings objectEnumerator];
    greetingLister *import; // greetingLister is my model class
    while (import = [e nextObject]) {
    NSLog(@"%@", [import greeting]); // greeting is an NSString in my model class
    }

    NSLog(@"Size of loaded array %d", [newArray count]);
    NSLog(@"Size of listOfGreetings %d", [listOfGreetings count]);

    // greetingList is the tableview but sending reload/display messages doesn't work :-(
    [greetingList reloadData];
    [greetingList setNeedsDisplay:YES];

    return YES;
    }
    }


    For info, my numberOfRowsInTableView method shown in the output is:

    - (NSInteger)numberOfRowsInTableView:(NSTableView *)aTableView {
    NSLog(@"tableView count %d", [listOfGreetings count]);
    return [listOfGreetings count];
    }


    The output when I run my program and open a data file is:

    [Session started at 2008-03-04 15:49:14 +0000.]
    2008-03-04 15:49:14.865 TestApp[2457:10b] tableView count 0
    2008-03-04 15:49:14.881 TestApp[2457:10b] tableView count 0
    TestApp(2457,0xb0103000) malloc: free_garbage: garbage ptr = 0x105d760, has non-zero refcount = 1
    TestApp(2457,0xb0103000) malloc: free_garbage: garbage ptr = 0x105cbd0, has non-zero refcount = 1
    TestApp(2457,0xb0103000) malloc: free_garbage: garbage ptr = 0x106e4a0, has non-zero refcount = 1
    TestApp(2457,0xb0103000) malloc: free_garbage: garbage ptr = 0x106edc0, has non-zero refcount = 1
    2008-03-04 15:49:19.469 TestApp[2457:10b] Loading data of type DocumentType
    2008-03-04 15:49:19.470 TestApp[2457:10b] Loaded data
    2008-03-04 15:49:19.471 TestApp[2457:10b] This is some data from the saved file
    2008-03-04 15:49:19.471 TestApp[2457:10b] Another entry from the saved file
    2008-03-04 15:49:19.472 TestApp[2457:10b] Size of array 2
    2008-03-04 15:49:19.472 TestApp[2457:10b] Size of listOfGreetings 2
    2008-03-04 15:49:19.500 TestApp[2457:10b] tableView count 0
    2008-03-04 15:49:19.510 TestApp[2457:10b] tableView count 0
    TestApp(2457,0xb0103000) malloc: free_garbage: garbage ptr = 0x1068330, has non-zero refcount = 1


    However (and here's the rub) the table view doesn't update automatically. What appears to be happening is that the array (listOfGreetings) contains objects in the loadDataRepresentation method but as soon as this method ends, the array has no length at all. It's totally baffled me!?

    I've tried turning off garbage collection and sending a retain message to newArray in loadDataRepresentation with no luck, but that was at the point that my clutching at straws reached frantic levels.

    For info, listOfGreetings is an NSMutableArray declared as follows:

    In MyDocument.h:
    @property(copy) NSMutableArray *listOfGreetings;

    In MyDocument.m:
    @synthesize listOfGreetings;

    This array contains objects that are my model class. The model class is set up using Objective C 2.0's properties. I don't think it's relevant as I'm writing and reading the data successfully, but I can post this code if need be.

    I realise that I've kept great chunks of my code out of this post, but it's still pretty long as it is! If any more info is required I'll be happy to post it.

    Thanks, in advance, for any tips!
     
  2. macrumors 68040

    iSee

    Joined:
    Oct 25, 2004
    #2
    Here's an idea:

    Since everytjhing seems fine at the end of loadDataRepresentation, but you don't see the data later, I wonder if there are two different instances of your object: loadDataRepresentation is called on one but the other is used thereafter. You could check for this by logging the pointer value of self in both loadDataRepresentation and numberOfRowsInTableView. If they are different then I'm on to something...
     
  3. macrumors G4

    Eraserhead

    Joined:
    Nov 3, 2005
    Location:
    UK
    #3
    Make sure you are retaining the array if you don't create it with an alloc init block you'll need to send it a retain message or it will be automatically cleared.

    In that case you also need to make sure to release it in the dealloc block.

    EDIT: Apologies if this post is a little technical.
     
  4. Moderator emeritus

    kainjow

    Joined:
    Jun 15, 2000
    #4
    He is using garbage collection though.


    Post your code for the setListOfGreetings: method, or wherever you're creating listOfGreetings. I think the problem may lie in there.
     
  5. thread starter macrumors 6502

    AJClayton

    Joined:
    Jan 9, 2007
    Location:
    Dorset, England
    #5
    Thanks very much for the helpful replies. I'll try to answer the questions asked above.

    First of all, as suggested, I entered a simple line in loadDataRepresentation: and numberOfRowsInTableView to give the pointer to my object (using NSLog(@"%@", self); This gave the following output:

    This is the log entry generated from loadData:
    2008-03-04 22:35:20.577 TestApp[267:10b] <MyDocument: 0x1071990>
    and this is the line generated from numberOfRows...:
    2008-03-04 22:35:20.605 TestApp[267:10b] <MyDocument: 0x10728d0>

    Looks like there's two MyDocument objects. You were spot on iSee, the plot thickens! ;-)

    As pointed out, I'm using garbage collection so I'm not explicitly retaining or releasing objects.

    I don't have a setListOfGreetings method as I'm using a property for this. In my MyDocument.h file I declare it like this:

    @interface MyDocument : NSDocument
    {
    // Various declarations......
    NSMutableArray *listOfGreetings;
    }
    @property(copy) NSMutableArray *listOfGreetings;


    In MyDocument.m I'm using synthesize to generate the methods for me:

    @implementation MyDocument
    @synthesize listOfGreetings;


    At one stage I commented out these lines and did it the old fashioned way but this didn't make any difference.

    One other thing. I'm instantiating the array in the init method of MyDocument, like this:

    -(id)init
    {
    self = [super init];
    if (self) {
    listOfGreetings = [[NSMutableArray] alloc] init];
    }
    return self;
    }


    Having read the Cocoa book by Aaron Hillegass, writing my own apps is a great way to really get my hands dirty and learn how Cocoa ticks. Your time spent helping is truly appreciated.
     
  6. macrumors 6502

    Joined:
    Feb 16, 2007
    Location:
    Waterloo, Ontario
    #6
    This may just be a typo, but there is a problem in your creation of the array.

    listOfGreetings = [[NSMutableArray] alloc] init];
    - should be -
    listOfGreetings = [[NSMutableArray alloc] init];

    I have not yet played with garbage collection, but before ObjC 2 you would have had to retain this as well. I'm not sure if this is necessary (the other users in this thread seem to have some experience here).

    Have you tried setting breakpoints in your table datasource methods?

    Luke Gladding


     
  7. macrumors regular

    Joined:
    Jan 8, 2008
    #7
    Just a shot in the dark, but these words of caution in the apple developers docs could be relevant :

    from http://developer.apple.com/documentation/Cocoa/Conceptual/Documents/Tasks/ImplementingDocApp.html

    phjo
     
  8. thread starter macrumors 6502

    AJClayton

    Joined:
    Jan 9, 2007
    Location:
    Dorset, England
    #8
    First of all, an apology to Luke. You're right - it was a typo in my post. I'm working on this app on my laptop and replied to the post from my desktop Mac re-keying the code snippets. Sorry for the confusion!

    Thanks to phjo, your tip is what has resolved the problem for me. I'd made all of my connections in Interface Builder through My Document, but as soon as I changed all of them to File's Owner everything worked a treat!

    Thanks to everyone who posted replies here, I've picked up some useful debugging tips along the way. I can now crack on with the rest of my project! :)

    Cheers

    Andy
     
  9. Moderator emeritus

    kainjow

    Joined:
    Jun 15, 2000
    #9
    That is incorrect actually. When you create an object with init (or any other init... method), it is already retained for you. If you retain again without a 2nd release, you will leak memory.

    AJClayton, glad you got it working.
     
  10. macrumors G4

    Eraserhead

    Joined:
    Nov 3, 2005
    Location:
    UK
    #10
    That is still wrong, you should use the initWithCapacity method when you initialise an NSMutableArray.

    i.e. it should be:
    Code:
    listOfGreetings = [[NSMutableArray alloc] initWithCapacity:1];
    
     
  11. thread starter macrumors 6502

    AJClayton

    Joined:
    Jan 9, 2007
    Location:
    Dorset, England
    #11
    That's interesting. I've just read up on initWithCapacity as it wasn't something I'd heard of. Out of interest, how much difference does this *really* make? Is the default action of a plain "init" to reserve much more memory initially?
     
  12. Moderator emeritus

    kainjow

    Joined:
    Jun 15, 2000
    #12
    I've never heard this. I've used init all the time and never had issues. Do you have something to point to for reference?
     

Share This Page