UITableView madness...

Discussion in 'iOS Programming' started by fergusjk, Sep 18, 2010.

  1. fergusjk macrumors member

    Joined:
    Aug 23, 2010
    Location:
    Ayrshire Coast, Scotland
    #1
    I have a UITableView which I am trying to set up. The sections should have titles and each section will have a different number of rows.

    If I hardwire the return data in, then it works, however if I try and pull the data from NSMutableArrays in the relevant methods then the app crashes with no explanation in the console.

    To summarise
    Code:
    - (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section{
    
    	//return [[sectionNumberRows objectAtIndex:section] intValue];
    	return 5;
    }
    works (giving me 5 rows per section)

    but
    Code:
     
    - (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section{
    
    	return [[sectionNumberRows objectAtIndex:section] intValue];
    	//return 5;
    }
    doesn't

    the code (placed in viewDidLoad)
    Code:
    for (int i = 0; i < [sectionNumberRows count]; i++) {
    		NSLog(@"sectionNumRows[]=%d", [[sectionNumberRows objectAtIndex:i] intValue]);
    		
    }
    outputs
    sectionNumRows[]=1
    sectionNumRows[]=4
    as i would expect.

    What am I doing wrong in the numberOfRowsInSection method?

    This behaviour is repeated in the titleForHeaderInSection method. i.e. if I hardwire the return string
    return @"testing";
    then the app works (with two section titles of 'testing')
    but if I try accessing the names from an NSMutableArray
    return [sectionTitles objectAtIndex:(int)section];
    then, again, it falls over with no explanation in the console.
     
  2. fergusjk thread starter macrumors member

    Joined:
    Aug 23, 2010
    Location:
    Ayrshire Coast, Scotland
    #2
    The madness continues...

    I tried running it in debug mode and it worked first time - picking up the titles from the array and not crashing. I changed NOTHING and i tried running it again and it crashed with this error in debug mode

    Program received signal: “EXC_BAD_ACCESS”.
    Data Formatters temporarily unavailable, will re-try after a 'continue'. (Not safe to call dlopen at this time.)

    I pressed continue a couple of times but get the same error.

    (gdb) continue
    Program received signal: “EXC_BAD_ACCESS”.
    Data Formatters temporarily unavailable, will re-try after a 'continue'. (Not safe to call dlopen at this time.)
    (gdb) continue
    Program received signal: “EXC_BAD_ACCESS”.
    Data Formatters temporarily unavailable, will re-try after a 'continue'. (Not safe to call dlopen at this time.)
    (gdb)

    I read somewhere that this might be caused by a stack overflow? I'm lost...
     
  3. chown33 macrumors 604

    Joined:
    Aug 9, 2009
    #3
    First rule of debugging: Break it down.

    There is a big difference between returning a constant and accessing an array to retrieve a value. You know what happens in the extremes: a fixed constant works, but the array reference and intValue doesn't. So figure out an intermediate point between those two extremes, and test that.

    For example, get the object at the index, but return a constant instead of messaging it with intValue. Or confirm that the array is valid and has a non-zero length, but return a constant. Start simple, closer to the known-working "return a constant". Gradually add.


    Second rule of debugging: Confirm that what you expect to happen is always what does happen.

    Here, the expectation is that the section value passed to numberOfRowsInSection: is always a valid index for sectionNumberRows.

    So instead of taking it on blind faith that sectionNumberRows always has the necessary number of elements, you could change numberOfRowsInSection: so if it's given a section parameter that's out of range for the current length of sectionNumberRows, it does something safe and informative, like NSLog() this fact, without attempting to access an invalid member of the NSArray.


    Finally, is it running on the simulator or the device? Do both of them exhibit the same behavior?
     
  4. fergusjk thread starter macrumors member

    Joined:
    Aug 23, 2010
    Location:
    Ayrshire Coast, Scotland
    #4
    Hi, thanks for the response.
    I checked several things you mentioned before I posted, in fact I've been at this for around 6/7 hours now.

    However based on what you said about trying all intermediate steps I tried using NSArrays in the methods instead of NSMutableArrays and they seems to work.

    I simply copy the contents of the mutable array to the NSArray as follows

    NSArray *aSectionTitles = [[NSArray alloc] initWithArray:sectionTitles];

    I cant understand why the mutable arrays don't work in the stated methods when trying to access/manipulate the data in the arrays, but the exact same code using NSArrays does?
     
  5. chown33 macrumors 604

    Joined:
    Aug 9, 2009
    #5
    Your NSArray is created using alloc, so you own it.
    How is sectionTitles created, and do you own it?

    In short, is the problem caused by arrays (or mutable arrays) being assigned to instance variables, but you've neglected to take ownership of them by retain?


    And you didn't answer the question: Is it running on the simulator or the device?
     
  6. fergusjk thread starter macrumors member

    Joined:
    Aug 23, 2010
    Location:
    Ayrshire Coast, Scotland
    #6
    Hi,

    The code is running in the simulator (iPhone 4.0 with xcode 3.2.3). I haven't provisioned any device yet as I'm waiting to complete my app before joining the Apple dev program.

    In the header file I have

    NSMutableArray *sectionTitles;
    NSArray *aSectionTitles;

    I've only declared them as instance variables not with any retain statement.

    Then in the implementation file I have
    sectionTitles = [[NSMutableArray alloc] init];
    declared within a method that populates the array thus
    [sectionTitles addObject:aString];

    If I try to use sectionTitles directly within the NSTableView delegate/dataSource methods the app crashes (although it may work 10% of the time - I think it is an out of memory issue).

    However if I perform the copy
    NSArray *aSectionTitles = [[NSArray alloc] initWithArray:sectionTitles];
    after sectionTitles has been populated then aSectionTitles can be used successfully in the delegate/dataSource methods (works 100% of the time).

    I'm confused as to why NSArray works but NSMutableArray causes crashes within the the delegate/dataSource methods?
     
  7. fergusjk thread starter macrumors member

    Joined:
    Aug 23, 2010
    Location:
    Ayrshire Coast, Scotland
    #7
    IMPORTANT UPDATE!!!!

    I added
    @property (nonatomic, retain) NSMutableArray *sectionTitles;

    and it is now working with the mutable array!!!

    hmmmm, none of the examples I saw had a retain statement but it seems that this is necessary to ensure that they remain in the stack? I'm assuming they must have been getting kicked off the stack 90% of the time resulting in the silent crashes.

    This was a valuable (if not painful) lesson learned - I was at this for hours last night. Any more funny behaviour related to memory I'll need to make sure that those variables are retained...

    Thank you so much for your assistance chown33. I was rapidly going crazy :p
     
  8. dejo Moderator

    dejo

    Staff Member

    Joined:
    Sep 2, 2004
    Location:
    The Centennial State
    #8
    I think in your case, the more important aspect to consider is whether the objects added to the array were being retained or not.
     
  9. chown33 macrumors 604

    Joined:
    Aug 9, 2009
    #9
    As I'm sure you know, objects added to an NSMutableArray are retained by that array. So unless those objects are being over-released somewhere else, they won't be released until the array holding them is dealloc'ed.


    The sectionTitles ivar is being assigned an alloc'ed NSMutableArray reference. Therefore, unless something else is release'ing that array, then the creating object should own it.

    Also, the array is being populated using an ivar reference form, not a property reference form, so there shouldn't be any unexpected releases during that.

    Frankly, there just isn't enough posted code to tell anything about the context of how the variables are being used. Isolated lines of code are only meaningful if the cause of the error is in that specific code fragment. But given that you don't know the cause of the problem, it's unlikely that the fragments selected for posting will contain the cause, except by sheer luck. It's also possible that the direct cause is in a posted fragment, but a precipitating cause was not posted, and without that context, the posted code would look perfectly correct.

    Posting complete methods and complete declarations would be an improvement over isolated fragments of code.


    Given the code you've posted so far, I see no reason why declaring a nonatomic retained property would cause it to work, especially because none of the posted code even uses a property reference form: it's all ivar references. I can only guess that the real cause of the problem lies somewhere in the code you haven't posted.

    Retain isn't a magic bullet. You should retain objects only when you don't already have ownership, and the object's lifetime needs to be guaranteed. Throwing retain around like it's a magic bug-fixer will only lead to memory leaks.
    http://developer.apple.com/library/mac/documentation/Cocoa/Conceptual/MemoryMgmt/MemoryMgmt.html

    One of the best things you could do now is run your program under Instruments.app, and check for leaks. Also run it with zombies enabled. You can't do both at once, so it will take two runs.

    I also recommend running the old crashing version under Instruments with zombies enabled. If an object is being dealloc'ed because of a failure to retain, zombies will show it.

    Thereafter, you should run the program under Instruments at least once a week, with leaks and zombies, and fix any problems it finds. Trust me, it's a lot easier to deal with leaks and over-releases in the early stages of development. Also, it will get you familiar with Instruments when your program is simpler, which will be very helpful after the program grows.
     
  10. dejo Moderator

    dejo

    Staff Member

    Joined:
    Sep 2, 2004
    Location:
    The Centennial State
    #10
    Yeah, I guess that is what I was hinting at.
     

Share This Page