在 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 项目后出现内存警告。初学者

iOS:ARC,不释放内存

iOS 应用程序以 350MB 大小发出低内存警告

收到内存警告?

在 UICollectionView iOS 7 中收到内存警告

使用仪器收到内存警告