绕开Live SDK内存释放bug和实现延迟自动刷新

之前,上一个版本的Live SDK各种内存泄露的bug弄得我比较烦躁,程序闪退崩溃,还要把后台的其他程序的内存都全部占用了。这太不科学了,但是新版本SDK又出现了 一个新问题,就是调用delegate的方法时不检查对象是否已经被dealloc了。我估计它用的是__unsafe_unretained的指针,所以即使对象释放了,指针也不会变为nil。然后就不管3721直接调用该对象的delegate方法,那么程序肯定会马上出错了。但这种错误在上一版SDK上是不会出现的,因为上一版的内存泄露导致controller不会dealloc,同时也不会显现出调用delegate方法出错的问题。

 

一、内存释放bug

那么解决问题的方法,我只能够模仿上一版本的SDK漏洞,就是通过循环引用,避免controller在调用delegate方法之前就被dealloc。

@property (strong, nonatomic) EarthViewController *noDealloc

在调用SDK的API之前,先将noDealloc = self,那么就会因为循环引用,controller无法被dealloc。然后只要在- (void) liveOperationSucceeded:(LiveOperation *)operation通过userState来将noDealloc = nil。那么controller也能够正常释放,再也不会因为用户操作过快而导致奔溃。因为这些controller都用用来浏览文件的,浏览的时候如果用户在刷新但是又没有耐性等,直接就将contrlloer pop了,那么就会出现上面的问题。

 

二、延迟刷新

为了保证能看到更新后SkyDrive上的文件,总是要用户刷新也太不人性化了,所以我加了个自动刷新的功能。一开始我是直接在- (void) viewWillAppear:(BOOL)animated里实现,不过一出现页面就刷新的话,会出现很多问题。例如,在快速不断后退目录的时候,途中经过的目录也会不断刷新,造成最终应该要刷新的目录刷新龟速。同时,这样做也浪费了大量的流量,耗费内存,影响了正在上传和下载任务的速度。

后来我改进了方法,思路就是延迟几秒刷新,不过Objective-C似乎没有,可以延迟执行block的函数。所以先自己实现一个

 1 @implementation NSObject (PerformBlockAfterDelay)
 2 
 3 
 4 - (void)performBlock:(void(^)(void))block afterDelay:(NSTimeInterval)delay
 5 
 6 {
 7     [self performSelector:@selector(fireBlockAfterDelay:) withObject:block afterDelay:delay];
 8 }
 9 
10 - (void)fireBlockAfterDelay:(void(^)(void))block
11 {
12     
13     block();
14     
15 }
16 @end

 

方法很简单,不过很实用。然后就实现刷新的调用

1 [self performBlock:^{
2         [UIApplication sharedApplication] setNetworkActivityIndicatorVisible:YES];
3         [liveClient getWithPath:[NSString stringWithFormat:@"%@/files?sort_by=name", self.URLPath]
4                        delegate:self
5                       userState:@"URLPath"];
6 }
7         afterDelay:3];

不过这样做,还是有问题,实际上所有的controller还是会在3秒后就执行刷新,无论你是否离开了当前这个目录。也就是说,目录无论如何都会刷新,只不过是延迟了3秒。这样实在太糟糕了,虽然也减轻了小部分刷新的压力,但是还是会做无谓的刷新,例如多次后退到同一页面,该页面就进行多次反复刷新。所以要优化一下刷新的思路,保证同一时间不会调用一次以上的刷新,正在刷新的时候也不能重复调用刷新。

于是,我现在的方法是,先定义一个全局变量EarthViewController *currentController,每一次进入到目录时,就先将这个变量赋值为self。那么,这就可以知道当前的controller是哪一个了,当然可以用其他方法实现,方法不唯一。

 1 self.title = self.fileTitle;
 2 noDeallocURLPath = self;
 3 currentController = self;
 4 
 5 //自动刷新
 6 if (self.cellsArray) {
 7     
 8     [self performBlock:^{
 9         if (self == currentController && _reloading == NO) {
10             [[UIApplication sharedApplication] setNetworkActivityIndicatorVisible:YES];
11             _reloading = YES;
12             [liveClient getWithPath:[NSString stringWithFormat:@"%@/files?sort_by=name", self.URLPath]
13                            delegate:self
14                           userState:@"URLPath"];
15         } else
16             noDeallocURLPath = nil;
17     }
18             afterDelay:3];
19     
20 } else {
21     
22     [[UIApplication sharedApplication] setNetworkActivityIndicatorVisible:YES];
23     _reloading = YES;
24     [liveClient getWithPath:[NSString stringWithFormat:@"%@/files?sort_by=name", self.URLPath]
25                    delegate:self
26                   userState:@"URLPath"];
27     
28 }
29     
30 [_refreshHeaderView refreshLastUpdatedDate];

由于,现时尚未实现缓存,所以每次进入到新目录就立即进行刷新。至于旧的目录,就继续延迟刷新,_reloading是下拉刷新库的一个变量,负责判断现在是否正在刷新中。同时判断,是否为当前目录,否就不刷新,不浪费资源,因为SDK同时处理请求的能力是有限的。刷新完成后就老规矩,将该还原的变量都全部设回一个正常值。

posted @ 2012-09-17 10:49  Pinka  阅读(349)  评论(0编辑  收藏  举报