zoukankan      html  css  js  c++  java
  • 绕开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同时处理请求的能力是有限的。刷新完成后就老规矩,将该还原的变量都全部设回一个正常值。

  • 相关阅读:
    Spring、Spring Boot 和 Spring Cloud 的关系
    Spring Boot 如何设置支持跨域请求?
    什么是嵌入式服务器?我们为什么要使用嵌入式服务器呢?
    nginx配置域名,不要端口
    域名解析以及nginx服务器设置
    为什么使用nginx
    RabbitMQ下载与安装(window版)
    spring的@ComponentScan注解
    spring的@EnableScheduling注解
    日志切割: logrotate、python、shell实现
  • 原文地址:https://www.cnblogs.com/ipinka/p/2687799.html
Copyright © 2011-2022 走看看