• iOS_SDWebImage框架分析


    SDWebImage 支持异步的图片下载+缓存,提供了 UIImageView+WebCacha 的 category,方便使用。使用SDWebImage首先了解它加载图片的流程。
    1. 入口 setImageWithURL:placeholderImage:options: 会先把 placeholderImage 显示,然后 SDWebImageManager 根据 URL 开始处理图片。
    2. 进入 SDWebImageManager-downloadWithURL:delegate:options:userInfo:,交给 SDImageCache 从缓存查找图片是否已经下载 queryDiskCacheForKey:delegate:userInfo:.
    3. 先从内存图片缓存查找是否有图片,如果内存中已经有图片缓存,SDImageCacheDelegate 回调 imageCache:didFindImage:forKey:userInfo: 到 SDWebImageManager。
    4. SDWebImageManagerDelegate 回调 webImageManager:didFinishWithImage: 到 UIImageView+WebCache 等前端展示图片。
    5. 如果内存缓存中没有,生成 NSInvocationOperation 添加到队列开始从硬盘查找图片是否已经缓存。
    6. 根据 URLKey 在硬盘缓存目录下尝试读取图片文件。这一步是在 NSOperation 进行的操作,所以回主线程进行结果回调 notifyDelegate:。
    7. 如果上一操作从硬盘读取到了图片,将图片添加到内存缓存中(如果空闲内存过小,会先清空内存缓存)。SDImageCacheDelegate 回调 imageCache:didFindImage:forKey:userInfo:。进而回调展示图片。
    8. 如果从硬盘缓存目录读取不到图片,说明所有缓存都不存在该图片,需要下载图片,回调 imageCache:didNotFindImageForKey:userInfo:。
    9. 共享或重新生成一个下载器 SDWebImageDownloader 开始下载图片。
    10. 图片下载由 NSURLConnection 来做,实现相关 delegate 来判断图片下载中、下载完成和下载失败。
    11. connection:didReceiveData: 中利用 ImageIO 做了按图片下载进度加载效果。
    12. connectionDidFinishLoading: 数据下载完成后交给 SDWebImageDecoder 做图片解码处理。
    13. 图片解码处理在一个 NSOperationQueue 完成,不会拖慢主线程 UI。如果有需要对下载的图片进行二次处理,最好也在这里完成,效率会好很多。
    14. 在主线程 notifyDelegateOnMainThreadWithInfo: 宣告解码完成,imageDecoder:didFinishDecodingImage:userInfo: 回调给 SDWebImageDownloader。
    15. imageDownloader:didFinishWithImage: 回调给 SDWebImageManager 告知图片下载完成。
    16. 通知所有的 downloadDelegates 下载完成,回调给需要的地方展示图片。
    17. 将图片保存到 SDImageCache 中,内存缓存和硬盘缓存同时保存。写文件到硬盘也在以单独 NSInvocationOperation 完成,避免拖慢主线程。
    18. SDImageCache 在初始化的时候会注册一些消息通知,在内存警告或退到后台的时候清理内存图片缓存,应用结束的时候清理过期图片。
    19. SDWI 也提供了 UIButton+WebCache 和 MKAnnotationView+WebCache,方便使用。
    20. SDWebImagePrefetcher 可以预先下载图片,方便后续使用
     
    SDWebImage的案例1:在tableView中使用SDWebImage
    #import <SDWebImage/UIImageView+WebCache.h>
    
    ...
    
    - (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
    {
        static NSString *MyIdentifier = @"MyIdentifier";
    
        UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:MyIdentifier];
    
        if (cell == nil)
        {
            cell = [[[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault
                                           reuseIdentifier:MyIdentifier] autorelease];
        }
    
        // Here we use the new provided sd_setImageWithURL: method to load the web image
        [cell.imageView sd_setImageWithURL:[NSURL URLWithString:@"http://www.domain.com/path/to/image.jpg"]
                          placeholderImage:[UIImage imageNamed:@"placeholder.png"]];
      
        cell.textLabel.text = @"My Text";
        return cell;
    }

    The SDWebImageManager is the class behind the UIImageView+WebCache category. It ties the asynchronous downloader with the image cache store. You can use this class directly to benefit from web image downloading with caching in another context than a UIView (ie: with Cocoa).

     NSURL *url = [NSURL URLWithString:@"http://127.0.0.1/userManager/0.png"];
        //SDWebImageManager管理器继承与UIImageView+WedCache。已异步的方式进行下载图片到缓冲区。
        SDWebImageManager *manager = [SDWebImageManager sharedManager];
        [manager downloadImageWithURL:url options:0 progress:^(NSInteger receivedSize, NSInteger expectedSize) {
            NSLog(@"%ld,%ld",receivedSize,expectedSize);
        }
        completed:^(UIImage *image, NSError *error, SDImageCacheType cacheType, BOOL finished, NSURL *imageURL) {
            //为imageView设置图片为下载的图片
            [self.imageView setImage:image];
            //打印图片的来源
            NSLog(@"%@",imageURL);
        }];

    It's also possible to use the async image downloader independently:

    [SDWebImageDownloader.sharedDownloader downloadImageWithURL:imageURL
                                                        options:0
                                                       progress:^(NSInteger receivedSize, NSInteger expectedSize)
                                                       {
                                                           // progression tracking code
                                                       }
                                                       completed:^(UIImage *image, NSData *data, NSError *error, BOOL finished)
                                                       {
                                                           if (image && finished)
                                                           {
                                                               // do something with image
                                                           }
                                                       }];

     在自己的案例中使用SDWebImage代码段:

    添加自定义的只读缓存路径,其中这是可选的,如果没有指定,系统会默认
    #import "SDWebImage/SDImageCache.h"
    #import "AppDelegate.h"
    ......
    - (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
    {
    //添加自定义的只读缓存路径:可选的
        NSString *bundledPath = [[[NSBundle mainBundle] resourcePath] stringByAppendingPathComponent:@"CustomPathImages"];
        [[SDImageCache sharedImageCache] addReadOnlyCachePath:bundledPath];
        ......    
    }

    在使用缓存时,如果内存不够使用时,需要进行清理以释放内存。

    #import "MasterViewController.h"
    #import "SDWebImage/UIImageView+WebCache.h"
    ......
    //清理缓存,
    - (void)flushCache
    {
        /**
         *  1、SDImageCache是怎么做数据管理的?
         SDImageCache分两个部分,一个是内存层面的,一个是硬盘层面的。
         内存层面的相当是个缓存器,以Key-Value的形式存储图片。
         */
        //清理内存缓存
        [SDWebImageManager.sharedManager.imageCache clearMemory];
        //清理硬盘缓存
        [SDWebImageManager.sharedManager.imageCache clearDisk];
    }
    //支持屏幕旋转
    - (BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)interfaceOrientation
    {
        return (interfaceOrientation != UIInterfaceOrientationPortraitUpsideDown);
    }
    ......

    //为了显示大图片,替将连接中small替换成source;

        NSString *largeImageURL = [[_objects objectAtIndex:indexPath.row] stringByReplacingOccurrencesOfString:@"small" withString:@"source"];

    ......

     

    仪表器的使用方法。

    #import "DetailViewController.h"
    #import "SDWebImage/UIImageView+WebCache.h"
    ......
    - (void)configureView
    {
        if (self.imageURL) {
            //创建仪表器
            __block UIActivityIndicatorView *activityIndicator;
            //为防止循环引用问题,所以在block中使用imageView时要设置成弱引用,imageView中使用block,在block中如果再调用imageView时,不设置为弱引用,会出现循环引用问题。
            __weak UIImageView *weakImageView = self.imageView;
            [self.imageView sd_setImageWithURL:self.imageURL
                              placeholderImage:nil
                                       options:SDWebImageProgressiveDownload
                                      progress:^(NSInteger receivedSize, NSInteger expectedSize) {
                                          //检测是否已经存在啦!,如果没有在新创建出一个
                                          if (!activityIndicator) {
                                              //将新创建的仪表器添加到imageView视图中
                                              [weakImageView addSubview:activityIndicator = [UIActivityIndicatorView.alloc initWithActivityIndicatorStyle:UIActivityIndicatorViewStyleGray]];
                                              activityIndicator.center = weakImageView.center;
                                              //开启动画,转吧!小宇宙
                                              [activityIndicator startAnimating];
                                          }
                                      }
                                     completed:^(UIImage *image, NSError *error, SDImageCacheType cacheType, NSURL *imageURL){
                                         //将仪表器从imageView视图中进行删除
                                         [activityIndicator removeFromSuperview];
                                         activityIndicator = nil;
                                     }];
        }
    }
    ......

    在Cell中使用异步图片下载。

    #import "MasterViewController.h"
    #import "SDWebImage/UIImageView+WebCache.h"
    ......
    /**
         *  参数解析:
            1.获取图片的路径
            2.设置占位图片
            3.options选项:当indexPath.row为0时,执行 SDWebImageRefreshCached:刷新缓存
         */
        [cell.imageView sd_setImageWithURL:[NSURL URLWithString:[_objects objectAtIndex:indexPath.row]]
                          placeholderImage:[UIImage imageNamed:@"placeholder"] options:indexPath.row == 0 ? SDWebImageRefreshCached : 0];
    ......

    在上段代码中,有如下两行代码: 

    __weak UIImageView *weakImageView = self.imageView;

    __block UIActivityIndicatorView *activityIndicator;

    其中第一条为了避免循环引用问题,加上了__weak,第二条使用了block。现在补充一下循环引用、block的知识点:

    循环引用
      所有的引用计数系统,都存在循环应用的问题。例如下面的引用关系:
         1)对象a创建并引用到了对象b.
         2)对象b创建并引用到了对象c.
         3)对象c创建并引用到了对象b.
      这时候b和c的引用计数分别是2和1。当a不再使用b,调用release释放对b的所有权,因为c还引用了b,所以b的引用计数为1,

      b不会被释放。b不释放,c的引用计数就是1,c也不会被释放。从此,b和c永远留在内存中。这种情况,必须打断循环引用

      通过其他规则来维护引用关系。比如,我们常见的delegate往往是assign方式的属性而不是retain方式 的属性,

      赋值不会增加引用计数,就是为了防止delegation两端产生不必要的循环引用。如果一个UITableViewController对象a

      通过retain获取了UITableView对象b的所有权,这个UITableView对象b的delegate又是a,如果这个delegate是retain方式的,

      那基本上就没有机会释放这两个对象了。自己在设计使用delegate模式时,也要注意这点。

    Block

      是可以获取其他函数局部变量的匿名函数,其不但方便开发,并且可以大幅提高应用的执行效率(多核心CPU可直接处理Block指令),如果希望进行修改局部变量的值,可以在定义局部变量之前使用__block。

  • 相关阅读:
    flutter项目目录介绍
    flutter真机调试出现flutter Launching 'app' on No Devices.
    flutter run出现 Deprecated Gradle features were used in this build, making it incompatible with Gradle 7.0.
    小程序uni-app中uview中select选择器第二次无法选中设置的默认值
    android studio编译flutter项目
    如何解决:Android Studio (version 4.1); Flutter plugin not installed and Dart plugin not installed errors
    CF Round 87
    CF #643(div.2)
    CF #642(div.3)
    CF #638(div.2)
  • 原文地址:https://www.cnblogs.com/xjf125/p/4842089.html
Copyright © 2020-2023  润新知