问题
I have this code for loading the UITableView:
- (int)numberOfSectionsInTableView:(UITableView *)tableView {
if (tableView == self.peopleTableView)
return [self.people count];
else
return [[[self.scheduleDays objectAtIndex:self.dayInt] periods] count];
}
- (int)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section {
if (tableView == self.peopleTableView)
return [[self.people objectAtIndex:section] count];
else
return 1;
}
- (NSString *)tableView:(UITableView *)tableView titleForHeaderInSection:(NSInteger)section {
if (tableView == self.peopleTableView)
return [self.headers objectAtIndex:section];
else
return [NSString stringWithFormat:@"%@ - %@", [[[[self.scheduleDays objectAtIndex:self.dayInt] periods] objectAtIndex:section] startTime], [[[[self.scheduleDays objectAtIndex:self.dayInt] periods] objectAtIndex:section] endTime]];
}
- (NSArray *)sectionIndexTitlesForTableView:(UITableView *)tableView {
return self.headers;
}
- (NSInteger)tableView:(UITableView *)tableView sectionForSectionIndexTitle:(NSString *)title atIndex:(NSInteger)index {
return index;
}
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
if (tableView == self.peopleTableView) {
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:@"Cell"];
if (cell == nil)
cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:@"Cell"];
Person *person = [[self.people objectAtIndex:[indexPath section]] objectAtIndex:[indexPath row]];
[[cell textLabel] setText:[NSString stringWithFormat:@"%@ %@", [person firstName], [person lastName]]];
return cell;
} else {
ScheduleTableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:@"Cell"];
if (cell == nil) {
cell = [[ScheduleTableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:@"Cell"];
[cell setSelectionStyle:UITableViewCellSelectionStyleNone];
}
Period *period = [[[[[[self appDelegate] user] scheduleDays] objectAtIndex:self.dayInt] periods] objectAtIndex:[indexPath section]];
[[cell mainLabel] setText:[period desc]];
[[cell subtitleLabel1] setText:[period teacher]];
[[cell subtitleLabel2] setText:[period roomLocation]];
return cell;
}
}
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath {
if (tableView == self.peopleTableView) {
self.currentViewedPerson = [[self.people objectAtIndex:[indexPath section]] objectAtIndex:[indexPath row]];
[self loadPerson:self.currentViewedPerson];
[tableView deselectRowAtIndexPath:indexPath animated:YES];
}
}
- (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath {
if (tableView == self.scheduleTable)
return 70;
else
return 44;
}
I use the method call [_tableView reloadData]
to load the data. It works fine the first time, but the second time it fails with EXC_BAD_ACCESS code=2
. Why?
EDIT:
It appears that the error is coming from a call to
#0 0x01b8c2a3 in TComponentFont::GetMinSideBearing(CGAffineTransform const&, bool) const ()
or a call to
I hope this helps.
EDIT:
Also no help from NSZombies. Running it in Xcode (with NSZombies) I get the same error no output, profiling it with the zombies profile it comes up with no message, the app just crashes.
EDIT:
This error is coming from the section titles, as when I comment out those sections I no longer get the error. What is incorrect about my section titles implementation?
EDIT:
This is how _headers
is declared in DirectoryViewController.h:
@property (strong, nonatomic) NSMutableArray *headers;
How headers is populated (probably not all necessary but...):
- (void)parser:(NSXMLParser *)parser didStartElement:(NSString *)elementName namespaceURI:(NSString *)namespaceURI qualifiedName:(NSString *)qName attributes:(NSDictionary *)attributeDict {
if (parser == self.peopleParser) {
if ([elementName isEqualToString:@"People"]) {
self.people = [NSMutableArray array];
self.headers = [NSMutableArray array];
} else if ([elementName isEqualToString:@"Person"]) {
self.currentPerson = [Person new];
[self.currentPerson setPersonID:[attributeDict objectForKey:@"id"]];
}
} else if (parser == self.scheduleParser) {
if ([elementName isEqualToString:@"Schedule"])
self.scheduleDays = [NSMutableArray array];
else if ([elementName isEqualToString:@"Day"]) {
NSEntityDescription *entity = [NSEntityDescription entityForName:@"ScheduleDay" inManagedObjectContext:[[self appDelegate] managedObjectContext]];
self.currentDay = [[ScheduleDay alloc] initWithEntity:entity insertIntoManagedObjectContext:nil];
} else if ([elementName isEqualToString:@"Course"]) {
NSEntityDescription *entity = [NSEntityDescription entityForName:@"Period" inManagedObjectContext:[[self appDelegate] managedObjectContext]];
self.currentPeriod = [[Period alloc] initWithEntity:entity insertIntoManagedObjectContext:nil];
}
}
}
- (void)parser:(NSXMLParser *)parser foundCharacters:(NSString *)string {
self.currentString = string;
}
- (void)parser:(NSXMLParser *)parser didEndElement:(NSString *)elementName namespaceURI:(NSString *)namespaceURI qualifiedName:(NSString *)qName {
if (parser == self.peopleParser) {
if ([elementName isEqualToString:@"People"])
self.currentLetter = @"";
else if ([elementName isEqualToString:@"Person"]) {
if ([self.currentLetter isEqualToString:[[[self.currentPerson lastName] substringToIndex:1] uppercaseString]])
[[self.people lastObject] addObject:self.currentPerson];
else {
[self.people addObject:[NSMutableArray array]];
[self.headers addObject:[[[self.currentPerson lastName] substringToIndex:1] uppercaseString]];
self.currentLetter = [[[self.currentPerson lastName] substringToIndex:1] uppercaseString];
[[self.people lastObject] addObject:self.currentPerson];
}
} else if ([elementName isEqualToString:@"Last"])
[self.currentPerson setLastName:self.currentString];
else if ([elementName isEqualToString:@"First"])
[self.currentPerson setFirstName:self.currentString];
else if ([elementName isEqualToString:@"EmailAddress"])
[self.currentPerson setEmail:self.currentString];
else if ([elementName isEqualToString:@"PhoneCell"])
[self.currentPerson setCellPhone:self.currentString];
else if ([elementName isEqualToString:@"PhoneHome"])
[self.currentPerson setHomePhone:self.currentString];
else if ([elementName isEqualToString:@"GradYear"])
[self.currentPerson setGradYear:self.currentString];
else if ([elementName isEqualToString:@"StudentGrade"])
[self.currentPerson setGrade:self.currentString];
else if ([elementName isEqualToString:@"Street1"])
[self.currentPerson setStreet1:self.currentString];
else if ([elementName isEqualToString:@"Street2"])
[self.currentPerson setStreet2:self.currentString];
else if ([elementName isEqualToString:@"City"])
[self.currentPerson setCity:self.currentString];
else if ([elementName isEqualToString:@"State"])
[self.currentPerson setState:self.currentString];
else if ([elementName isEqualToString:@"Zip"])
[self.currentPerson setZip:self.currentString];
} else if (parser == self.scheduleParser) {
if ([elementName isEqualToString:@"Course"])
[self.currentPeriod setDay:self.currentDay];
else if ([elementName isEqualToString:@"Day"])
[self.scheduleDays addObject:self.currentDay];
else if ([elementName isEqualToString:@"StartTime"])
[self.currentPeriod setStartTime:self.currentString];
else if ([elementName isEqualToString:@"EndTime"])
[self.currentPeriod setEndTime:self.currentString];
else if ([elementName isEqualToString:@"Description"])
[self.currentPeriod setDesc:self.currentString];
else if ([elementName isEqualToString:@"Location"])
[self.currentPeriod setRoomLocation:self.currentString];
else if ([elementName isEqualToString:@"Teacher"])
[self.currentPeriod setTeacher:self.currentString];
}
self.currentString = @"";
}
- (void)parser:(NSXMLParser *)parser parseErrorOccurred:(NSError *)parseError {
if ([parseError code] == 5) {
self.people = [NSMutableArray array];
self.headers = [NSMutableArray array];
} else {
UIAlertView *alert = [[UIAlertView alloc] initWithTitle:@"Error!" message:[parseError description] delegate:nil cancelButtonTitle:@"Ok" otherButtonTitles:nil];
[alert show];
}
}
EDIT:
Where the reloadData
is called:
- (void)search {
NSString *urlString = [LINK SETUP CODE GOES HERE]
self.peopleParser = [[NSXMLParser alloc] initWithContentsOfURL:[NSURL URLWithString:urlString]];
self.peopleParser.delegate = self;
if ([self.peopleParser parse] && [self.people count] > 0) {
[self.peopleTableView reloadData];
[self.peopleTableView scrollToRowAtIndexPath:[NSIndexPath indexPathForRow:0 inSection:0] atScrollPosition:UITableViewScrollPositionTop animated:NO];
[self.view addSubview:self.peopleView];
} else {
UIAlertView *alert = [[UIAlertView alloc] initWithTitle:@"No Results!" message:@"Your search returned no results. Try broadening your search." delegate:nil cancelButtonTitle:@"Ok" otherButtonTitles:nil];
[alert show];
}
[self.activityIndicator stopAnimating];
self.activityIndicator.hidden = YES;
}
回答1:
To avoid memory issues you should create your objects and refer to your objects using property accessor methods.
For example you declare property for headers
mutable array:
@property (strong, nonatomic) NSMutableArray *headers;
Then you should create your array this way:
self.headers = [NSMutableArray array];
And refer to your array this way:
- (NSString *)tableView:(UITableView *)tableView titleForHeaderInSection:(NSInteger)section {
return [self.headers objectAtIndex:section];
}
- (NSArray *)sectionIndexTitlesForTableView:(UITableView *)tableView {
return self.headers;
}
Using this syntaxis you call setter and getter methods for headers
property (these methods automatically generated by Xcode).
In non-ARC environment it is equivalent for calling these methods for (retain, nonatomic)
property (retain
is analog of strong
):
- (NSMutableArray *)headers {
return _headers;
}
- (void)setHeaders:(NSMutableArray *)headers {
[_headers autorelease];
_headers = [headers retain];
}
If you use self.
notation in ARC environment tracking of an object's reference count will be done automatically.
You can find more information about properties here.
EDIT:
It seems to be a bug in the framework. Check this answer
回答2:
The second it is because due to autorelease ,you should check values using NSLog() and then you can find solution very easliy.Get each value on different stages or in different methods
回答3:
Without seeing more of your code, my guess is that while the strings are equivalent for the array of headers, you are not properly comparing them in sectionForIndexTitle.
Please remember that indexOfObject will only work if the pointers to the strings are exactly the same. Having two strings with the same value isn't necessarily enough.
For safety you might try:
for (int i = 0; i < [_headers count]; ++i)
{
if ([_headers[i] isEqualToString title])
{
return i;
}
}
// error handling belongs here
But again, that's a guess given that we don't have clear insight into how _headers is constructed or what else is going on in your code.
回答4:
I think this crash is due to the auto releasing of TableView object, '_people' array or '_headers' array. The first thing you have to do is the logging of its values to check whether it an already released object. If so you can use
[_people retain];
or
[_headers retain];
or
[tableviewObject retain];
for avoiding the auto release.
来源:https://stackoverflow.com/questions/19042323/uitableview-reloaddata-exc-bad-acess-code-2