在 ARC 中收到内存警告
Posted
技术标签:
【中文标题】在 ARC 中收到内存警告【英文标题】:Received Memory Warning in ARC 【发布时间】:2014-01-08 08:34:09 【问题描述】:我正在调用 syncWithCalendar,在成功添加事件后,我收到内存不足警告,并且应用程序因“收到内存不足”警告而终止。日历中生成和保存的事件超过 50 个。我尝试使用工具,但无法找到发生内存泄漏的代码,也无法通过工具中显示的实时字节找到导致泄漏。谁能帮我解决这个问题。
- (void)syncWithCalendar
@autoreleasepool
[self deleteEventsIfExist];
NSMutableDictionary *dictionary = [util readPListData];
NSMutableArray *courses = [util getCourses];
__block NSMutableArray *lessons;
__block NSMutableDictionary *lesson;
NSString *studentID = [util getProgramDetails].studentId;
NSString *programName = [util getProgramDetails].programName;
double offset[] = 0, 0, -300, -900, -1800, -3600, -7200, -86400, -172800;
__block NSString *startDateString = @"", *endDateString = @"";
NSTimeInterval relativeOffsetValue = 0;
int index = [[dictionary objectForKey:@"event-alert-option"] intValue];
relativeOffsetValue = offset[index];
NSDateFormatter *formatter;
formatter = [[NSDateFormatter alloc] init];
[formatter setDateFormat:@"MM/dd/yyyy HH:mm:ss"];
[formatter setDateFormat:@"MM/dd/yyyy"];
NSString *currentDateString = [NSString stringWithFormat:@"%@ 09:00:00", [formatter stringFromDate:[NSDate date]]];
[formatter setDateFormat:@"MM/dd/yyyy HH:mm:ss"];
NSDate *currentDate = [formatter dateFromString:currentDateString];
EKEventStore *eventStore = [[EKEventStore alloc] init];
if([eventStore respondsToSelector:@selector(requestAccessToEntityType:completion:)])
// ios 6 and later
[eventStore requestAccessToEntityType:EKEntityTypeEvent completion:^(BOOL granted, NSError *error)
if (granted)
//---- codes here when user allow your app to access theirs' calendar.
dispatch_async(dispatch_get_main_queue(), ^
// Event creation code here.
for (int i=0; i<[courses count]; i++)
@autoreleasepool
lessons = [[courses objectAtIndex:i] objectForKey:@"lessons"];
for (int j=0; j<[lessons count]; j++)
@autoreleasepool
lesson = [lessons objectAtIndex:j];
NSString *title = nil;
title = [NSString stringWithFormat:@"%@ %@-Complete %@ lesson",studentID,programName,[lesson objectForKey:@"lesson-name"]];
if ([[lesson objectForKey:@"actual-exam-date"] isEqualToString:@"00/00/0000"])
startDateString = [NSString stringWithFormat:@"%@ %@", [lesson objectForKey:@"plan-exam-date"], @"09:00:00"];
endDateString = [NSString stringWithFormat:@"%@ %@", [lesson objectForKey:@"plan-exam-date"], @"18:00:00"];
else
if ([[lesson objectForKey:@"retake-actual-date"] isEqualToString:@"00/00/0000"])
startDateString = [NSString stringWithFormat:@"%@ %@", [lesson objectForKey:@"retake-plan-date"], @"09:00:00"];
endDateString = [NSString stringWithFormat:@"%@ %@", [lesson objectForKey:@"retake-plan-date"], @"18:00:00"];
if (!([startDateString isEqualToString:@""] && [endDateString isEqualToString:@""]))
EKEvent *event = [EKEvent eventWithEventStore:eventStore];
event.title=title;
event.startDate = [formatter dateFromString:startDateString];
event.endDate = [formatter dateFromString:endDateString];
event.allDay = NO;
if (index != 0)
event.alarms = [NSArray arrayWithObjects:[EKAlarm alarmWithRelativeOffset:relativeOffsetValue], nil];
[event setCalendar:[eventStore defaultCalendarForNewEvents]];
// Compare current date to event start date, if start date has been passed then preventing to sync with calendar
NSComparisonResult result = [event.startDate compare:currentDate];
if (result != NSOrderedAscending)
NSError *err = nil;
[eventStore saveEvent:event span:EKSpanThisEvent commit:YES error:&err];
if (err)
NSLog(@"event not saved .. error = %@",err);
else
NSLog(@"event added successfully");
// autoreleasepool
// lessons for loop
// autoreleasepool
// courses for loop
[self hideModal];
);
else
//----- codes here when user NOT allow your app to access the calendar.
// [self performSelectorOnMainThread:@selector(hideModal) withObject:nil waitUntilDone:NO];
];
else
// sync calendar for <iOS6
// autoreleasepool
- (void)deleteEventsIfExist
@autoreleasepool
NSMutableArray *courses = [util getCourses];
__block NSMutableArray *lessons;
__block NSMutableDictionary *lesson;
NSString *studentID = [util getProgramDetails].studentId;
NSString *programName = [util getProgramDetails].programName;
EKEventStore* store = [[EKEventStore alloc] init];
dispatch_async(dispatch_get_main_queue(), ^
// Event creation code here.
NSDate* endDate = [NSDate dateWithTimeIntervalSinceNow:[[NSDate distantFuture] timeIntervalSinceReferenceDate]];
NSPredicate *fetchCalendarEvents = [store predicateForEventsWithStartDate:[NSDate date] endDate:endDate calendars:store.calendars];
NSArray *allEvents = [store eventsMatchingPredicate:fetchCalendarEvents];
for (int i=0; i<[courses count]; i++)
@autoreleasepool
lessons = [[courses objectAtIndex:i] objectForKey:@"lessons"];
for (int j=0; j<[lessons count]; j++)
@autoreleasepool
lesson = [lessons objectAtIndex:j];
NSString *oldEventSubtitle = [NSString stringWithFormat:@"%@ %@-Complete %@ lesson",studentID,programName,[lesson objectForKey:@"lesson-name"]];
for (EKEvent *e in allEvents)
if ( [oldEventSubtitle isEqualToString:e.title])
NSError* error = nil;
[store removeEvent:e span:EKSpanThisEvent commit:YES error:&error];
NSLog(@"deleting events");
// autoreleasepool
// lessons
// autoreleasepool
// courses
);
// autoreleasepool
【问题讨论】:
问题可能出在您的util
电话中。您正在阅读整个 plist,然后将其他数据放入变量中(可能是多余的)。不要在这里转储你的代码。想想问题可能出在哪里,然后发布选择性代码。
仪器至少应该向您展示分配大量内存的“热点”。你能发布 Instruments 视图吗?
@Mundi 但我正在阅读 plist 一次。你说的冗余是什么意思。除了我使用这两个函数时,应用程序在所有其他情况下都运行良好,所以我认为问题可能只出在这两个函数上。
@CouchDeveloper 我尝试发帖,但它说我需要至少 10 个声望才能发帖。我有 9 名声望,但在发布此问题后,我猜我的声望降至 3。现在我猜我无法发布图片了。
@CouchDeveloper drive.google.com/file/d/0ByegeKbbsr95SGQ3dG93azc1VVE/…
【参考方案1】:
这是一个粗略的猜测,但似乎异步调用可能会导致麻烦。
为了测试这一点,只需使用dispatch_sync
而不是dispatch_async
并检查内存消耗。如果这会带来改进,那么解决方案就在眼前,这涉及重构您当前的异步“并行”方法并将其转变为适当的异步“串行”方法或完整的同步方法。
这可能还需要“序列化”此异步方法的所有调用:
[eventStore requestAccessToEntityType:EKEntityTypeEvent
completion:^(BOOL granted, NSError *error)
...
]
【讨论】:
【参考方案2】:这就是我调用syncWithCalendar
函数的方式
if([eventStore respondsToSelector:@selector(requestAccessToEntityType:completion:)])
// iOS 6 and later
[eventStore requestAccessToEntityType:EKEntityTypeEvent completion:^(BOOL granted,
NSError *error)
if (granted)
dispatch_async(dispatch_get_main_queue(), ^
[self syncWithCalendar];
);
else
// calendar access not granted
];
在syncWithCalendar
函数中,除了代码行之外,一切都保持不变
正在创建崩溃/内存问题。下面是我错误的代码行
早点使用
// wrong
[self.eventstore saveEvent:event span:EKSpanThisEvent commit:YES error:&err];
保存事件的正确方法:(注意:在我的例子中我不需要事件标识符)
// correct
[self.eventstore saveEvent:event span:EKSpanThisEvent commit:NO error:&err];
然后在保存所有事件后使用[self.eventstore commit:NULL]
。在我的情况下,这停止了崩溃。希望这篇文章会
帮助其他人获得解决方案。谢谢!!!!
【讨论】:
【参考方案3】:当你收到内存警告时,你需要清除缓存,使用这个方法会帮助你。
-(void)applicationDidReceiveMemoryWarning:(UIApplication *)application
[[NSURLCache sharedURLCache] removeAllCachedResponses];
【讨论】:
以上是关于在 ARC 中收到内存警告的主要内容,如果未能解决你的问题,请参考以下文章
在 ARC 中启动新的 Cocos2d-iPhone 项目后出现内存警告。初学者