iPhone - didSelectRowAtIndexPath - selecting row 26 or greater mystery
My UITableView has up to 50 rows in each section, populated by a plist dictionary which contains arrays (sections) which contains arrays (row objects) which contains two strings (row title / filename and file extension).
Select row 1 - 25 (item 0-24) and everything behaves normally. But select a row greater than 26 (item 25) and the app crashes. I'm a novice at all this and I tried researching for the answer but I'm at a loss for how to research this. Can tables only have 25 rows per section?
Any ideas? Thank you!
Jon
- (void)tableView:(UITableView *)tableView
didSelectRowAtIndexPath:(NSIndexPath *)indexPath {
NSUInteger section = [indexPath section];
NSUInteger row = [indexPath row];
NSMutableString *key = [categories objectAtIndex:section];
NSMutableArray *sound = [categoriesSounds objectForKey:key];
NSMutableString *soundName = [[sound objectAtIndex: row] objectAtIndex: 0];
NSMutableString *soundOfType = [[sound objectAtIndex: row] objectAtIndex: 1];
if (leftSwitch.on == YES) {
showLeft.text = soundName;
left = [[NSBundle mainBundle] pathForResource:(@"%@", soundName) ofType:(@"%@", soundOfType)];
AudioServicesCreateSystemSoundID((CFURLRef)[NSURL
fileURLWithPath:left], &soundNegZ);
AudioServicesPlaySystemSound (soundNegZ);
if (indexPath != leftOldIndexPath) {
UITableViewCell *newCell = [tableView cellForRowAtIndexPath:indexPath];
newCell.accessoryType = UITableViewCellAccessoryCheckmark;
UITableViewCell *oldCell = [tableView cellForRowAtIndexPath:leftOldIndexPath];
oldCell.accessoryType = UITableViewCellAccessoryNone;
leftOldIndexPath = indexPath;
}
[tableView deselectRowAtIndexPath:indexPath animated:YES];
}
if (downSwitch.on == YES) {
showDown.text = soundName;
down = [[NSBundle mainBundle] pathForResource:(@"%@", soundName) ofType:(@"%@", soundOfType)];
AudioServicesCreateSystemSoundID((CFURLRef)[NSURL
fileURLWithPath:down], &soundNegX);
AudioServicesPlaySystemSound (soundNegX);
if (indexPath != downOldIndexPath) {
UITableViewCell *newCell = [tableView cellForRowAtIndexPath:indexPath];
newCell.accessoryType = UITableViewCellAccessoryCheckmark;
UITableViewCell *oldCell = [tableView cellForRowAtIndexPath:downOldIndexPath];
oldCell.accessoryType = UITableViewCellAccessoryNone;
downOldIndexPath = indexPath;
}
[tableView deselectRowAtIndexPath:indexPath animated:YES];
}
if (rightSwitch.on == YES) {
showRight.text = soundName;
right = [[NSBundle mainBundle] pathForResource:(@"%@", soundName) ofType:(@"%@", soundOfType)];
AudioServicesCreateSystemSoundID((CFURLRef)[NSURL
fileURLWithPath:right], &soundPosX);
AudioServicesPlaySystemSound (soundPosX);
if (indexPath != rightOldIndexPath) {
UITableViewCell *newCell = [tableView cellForRowAtIndexPath:indexPath];
newCell.accessoryType = UITableViewCellAccessoryCheckmark;
UITableViewCell *oldCell = [tableView cellForRowAtIndexPath:rightOldIndexPath];
oldCell.accessoryType = UITableViewCellAccessoryNone;
rightOldIndexPath = indexPath;
}
[tableView deselectRowAtIndexPath:indexPath animated:YES];
}
}
For "EXC_BAD_ACCESS" in the console, this is the debugger output (error trace is marked with asterisks):
0x91a6cec0 <+0000> mov 0x8(%esp),%ecx
0x91a6cec4 <+0004> mov 0x4(%esp),%eax
0x91a6cec8 <+0008> cmp $0xfffeb010,%ecx
0x91a6cece <+0014> je 0x91a6cf35 <objc_msgSend+117>
0x91a6ced0 <+0016> test %eax,%eax
0x91a6ced2 <+0018> je 0x91a6cf1a <objc_msgSend+90>
0x91a6ced4 <+0020> mov (%eax),%edx
0x91a6ced6 <+0022> push %edi
**0x91a6ced7 <+0023> mov 0x20(%edx),%edi**
0x91a6ceda <+0026> push %esi
0x91a6cedb <+0027> mov (%edi),%esi
0x91a6cedd <+0029> mov %ecx,%edx
0x91a6cedf <+0031> shr $0x2,%edx
0x91a6cee2 <+0034> and %esi,%edx
0x91a6cee4 <+0036> mov 0x8(%edi,%edx,4),%eax
0x91a6cee8 <+0040> test %eax,%eax
0x91a6ceea <+0042> je 0x91a6cef5 <objc_msgSend+53>
0x91a6ceec <+0044> cmp (%eax),%ecx
0x91a6ceee <+0046> je 0x91a6cf00 <objc_msgSend+64>
0x91a6cef0 <+0048> add $0x1,%edx
0x91a6cef3 <+0051> jmp 0x91a6cee2 <objc_msgSend+34>
0x91a6cef5 <+0053> pop %esi
0x91a6cef6 <+0054> pop %edi
0x91a6cef7 <+0055> mov 0x4(%esp),%eax
0x91a6cefb <+0059> mov (%eax),%eax
0x91a6cefd <+0061> jmp 0x91a6cf09 <objc_msgSend+73>
0x91a6ceff <+0063> nop
0x91a6cf00 <+0064> mov 0x8(%eax),%eax
0x91a6cf03 <+0067> pop %esi
0x91a6cf04 <+0068> pop %edi
0x91a6cf05 <+0069> xor %edx,%edx
0x91a6cf07 <+0071> jmp *%eax
0x91a6cf09 <+0073> sub $0x4,%esp
0x91a6cf0c <+0076> push %ecx
0x91a6cf0d <+0077> push %eax
0x91a6cf0e <+0078> call 0x91a6d33f <_class_lookupMethodAndLoadCache>
0x91a6cf13 <+0083> add $0xc,%esp
0x91a6cf16 <+0086> xor %edx,%edx
0x91a6cf18 <+0088> jmp *%eax
0x91a6cf1a <+0090> call 0x91a6cf1f <objc_msgSend+95>
0x91a6cf1f <+0095> pop %edx
0x91a6cf20 <+0096> mov 0xe79d961(%edx),%eax
0x91a6cf26 <+0102> test %eax,%eax
0x91a6cf28 <+0104> je 0x91a6cf30 <objc_msgSend+112>
0x91a6cf2a <+0106> mov %eax,0x4(%esp)
0x91a6cf2e <+0110> jmp 0x91a6ced4 <objc_msgSend+20>
0x91a6cf30 <+0112> mov $0x0,%edx
0x91a6cf35 <+0117> ret
0x91a6cf36 <+0118> nopw %cs:0x0(%eax,%eax,1)
My cellForRowAtIndexPath method:
开发者_C百科- (UITableViewCell *)tableView:(UITableView *)tableView
cellForRowAtIndexPath:(NSIndexPath *)indexPath {
NSUInteger section = [indexPath section];
NSUInteger row = [indexPath row];
NSString *key = [categories objectAtIndex:section];
NSArray *nameSection = [categoriesSounds objectForKey:key];
static NSString *SectionsTableIdentifier = @"SectionsTableIdentifier";
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:
SectionsTableIdentifier];
if (cell == nil) {
cell = [[[UITableViewCell alloc]
initWithStyle:UITableViewCellStyleDefault
reuseIdentifier:SectionsTableIdentifier] autorelease];
}
cell.textLabel.text = [[nameSection objectAtIndex:row] objectAtIndex: 0];
if (leftSwitch.on == YES){
NSUInteger leftRow = [leftOldIndexPath row];
NSUInteger leftSection = [leftOldIndexPath section];
cell.accessoryType = (row == leftRow && section == leftSection && leftOldIndexPath !=nil) ?
UITableViewCellAccessoryCheckmark : UITableViewCellAccessoryNone;
}
if (downSwitch.on == YES){
NSUInteger downRow = [downOldIndexPath row];
NSUInteger downSection = [downOldIndexPath section];
cell.accessoryType = (row == downRow && section == downSection && downOldIndexPath !=nil) ?
UITableViewCellAccessoryCheckmark : UITableViewCellAccessoryNone;
}
if (rightSwitch.on == YES){
NSUInteger rightRow = [rightOldIndexPath row];
NSUInteger rightSection = [rightOldIndexPath section];
cell.accessoryType = (row == rightRow && section == rightSection && rightOldIndexPath !=nil) ?
UITableViewCellAccessoryCheckmark : UITableViewCellAccessoryNone;
}
return cell;
}
From analysing your code, I can see a few places where you can be failing.
First of all:
NSMutableString *soundName = [[sound objectAtIndex: row] objectAtIndex: 0];
NSMutableString *soundOfType = [[sound objectAtIndex: row] objectAtIndex: 1];
Do you have more than 25 elements in the sound array?
Second of all:
if (indexPath != downOldIndexPath) {
What is downOldIndexPath? Are you sure that if you pass it to this method
UITableViewCell *oldCell = [tableView cellForRowAtIndexPath:downOldIndexPath];
it is a correct index path?
When your app crashes do you see any error messages logged in the debugger console? (If it's not viewable whilst you debug your application select the Run menu -> Console menu option in XCode). You should see some log entries in here that will help to point you in the right direction.
I have 101 rows in my current project. As far as i know there isn't a limit.
As for the crash, can you give us more info? Like a stack trace? Console output?
If you were accessing more elements than the array held, you would get an out of bounds exception - not a EX_BAD_ACCESS crash.
That means you are trying to access memory you have released. There are two possibilities:
1) The item you are pulling out of the array, has been released. This is pretty unlikely as you would have had to do extra releases to get rid of something held in an array.
2) (most likely) You have released one of the arrays you are trying to get data from - or more accurately, you get the array from somewhere and then forget to retain it (since most things given to you by other methods are auto-released).
I'm having this same problem, I see you've solved it by the following:
Thanks Eddie, NSZombieEnabled pointed me in the right direction and I was able to fix the problem. Turned out my index path retain count needed to be increased by 1 in cellForRowAtIndexPath. – Jonathan Cohen Jan 25 at 18:00
Sorry I'm a bit new to iPhone programming, what does this mean exactly?
I've tried a few things and they all seem to work I'm just wondering which is the correct way!?
I have tried this:
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
static NSString *CheckMarkCellIdentifier = @"CheckMarkCellIdentifier";
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier: CheckMarkCellIdentifier];
if (cell == nil) {
cell = [[[UITableViewCell alloc]
initWithStyle:UITableViewCellStyleSubtitle
reuseIdentifier:CheckMarkCellIdentifier] autorelease];
}
// need to retain to stop this crashing with rows > ~26
[indexPath retain];
NSUInteger row = [indexPath row];
NSUInteger oldRow = [lastIndexPath row];
// Print the text
cell.textLabel.text = [itemTextList objectAtIndex:row];
// Print the detail text
cell.detailTextLabel.text = [itemDetailTextList objectAtIndex:row];
cell.accessoryType = (row == oldRow && lastIndexPath != nil) ? UITableViewCellAccessoryCheckmark : UITableViewCellAccessoryNone;
return cell;
}
Also (separately) I've tried both of these:
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath {
NSUInteger newRow = [indexPath row];
NSUInteger oldRow = (lastIndexPath != nil) ? [lastIndexPath row] : -1;
if (newRow != oldRow) {
UITableViewCell *newCell = [tableView cellForRowAtIndexPath: indexPath];
newCell.accessoryType = UITableViewCellAccessoryCheckmark;
UITableViewCell *oldCell = [tableView cellForRowAtIndexPath: lastIndexPath];
oldCell.accessoryType = UITableViewCellAccessoryNone;
// retain indexPath to use as last index path
lastIndexPath = [indexPath retain];
}
[tableView deselectRowAtIndexPath:indexPath animated:YES];
}
and
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath {
NSUInteger newRow = [indexPath row];
NSUInteger oldRow = (lastIndexPath != nil) ? [lastIndexPath row] : -1;
if (newRow != oldRow) {
UITableViewCell *newCell = [tableView cellForRowAtIndexPath: indexPath];
newCell.accessoryType = UITableViewCellAccessoryCheckmark;
UITableViewCell *oldCell = [tableView cellForRowAtIndexPath: lastIndexPath];
oldCell.accessoryType = UITableViewCellAccessoryNone;
// get a copy to use as last index path
lastIndexPath = [indexPath copy];
}
[tableView deselectRowAtIndexPath:indexPath animated:YES];
}
Now I'm wavering towards the last [indexPath copy], is this the correct way to do it from a memory management point of view? Or does it not really matter whether I retain or copy it?
Thanks,
Matt.
精彩评论