图片下载—>显示封装类思路

本文介绍了一种iOS开发中高效处理图片异步加载的方法,包括使用NSOperation进行图片下载、图片缓存策略以及沙盒缓存技术。通过详细的代码示例展示了如何实现图片缓存及操作缓存来提高加载效率并避免图片错位。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >


图片下载-->显示:

异步下载: NSOperation + 操作队列

业务逻辑: 图片缓存(防止图片错位,提升效率) + 操作缓存(防止重复创建操作) + 沙盒缓存(磁盘缓存)

业务拆分: 不同的功能写在不同的类中.高层次的封装.

要求:一句话自动实现异步图片的下载并且显示图片. 沙盒缓存(磁盘缓存)

一句话代码的接口: UIImageView 的一个分类.这个分类专门负责提供一句话代码的接口.

异步图片下载: 自定义的操作.专门负责下载图片

显示图片: 管理工具类.负责将图片下载和图片显示联系起来(业务功能逻辑由它实现(协调)). 图片缓存+操作缓存

沙盒缓存: 自定义的沙盒缓存工具类.专门负责沙盒中图片的读和写.


// 解决图片错位问题,需要判定 cell 对应的图片地址已经改变!

给每一个imageView 都绑定一个下载地址. 如果外界传入的下载地址改变, iamgeView 绑定的地址变成新的地址,原来的下载操作取消.开始新的下载操作.

// 如何给 imageView 绑定下载地址:利用运行时,在分类中动态的为 imageView 添加一个属性(urlString).

具体的NSOperation(操作)
#import <Foundation/Foundation.h>
#import <UIKit/UIKit.h>

// 1. 定义block类型
@class SHDownloadWebImageOperation;

typedef void(^downloadBlock)(SHDownloadWebImageOperation *op);
@interface SHDownloadWebImageOperation : NSOperation

// 负责下载图片

// 图片下载地址
@property(nonatomic ,copy)NSString *urlString;

// 下载好的图片
@property(nonatomic ,strong)UIImage *image;

// 添加block属性
@property(nonatomic ,copy) downloadBlock block;

// 网络图片下载完成之后的回调.(将图片添加到内存缓存中)
-(void)downloadWebImageWithBlock:(downloadBlock)blk;
@end
#import "SHDownloadWebImageOperation.h"
#import "SHReadAndWriteSandbox.h"

@implementation SHDownloadWebImageOperation

-(void)main
{
    @autoreleasepool {
        
        // 下载图片
        self.image = [self downloadWebImageWithUrlString:self.urlString];
        
        // 图片下载完成之后,回到主线程执行图片下载完成之后的回调.
        dispatch_async(dispatch_get_main_queue(), ^{
            
            if (self.block) {
                // 3. 执行 self.block ,需要的参数 self.
                self.block(self);
            }
        });
        
    }
    
}

// 2. 确定 self.block 中执行的内容.
-(void)downloadWebImageWithBlock:(downloadBlock)blk
{
    if (blk) {
        self.block = blk;
    }
}


// 下载网络图片.
-(UIImage *)downloadWebImageWithUrlString:(NSString *)urlString
{
    NSLog(@"下载图片:%@",[NSThread currentThread]);
    
    if (self.isCancelled) {
        
        return nil;
    }
    
    // 模拟网络过程...
    [NSThread sleepForTimeInterval:3];
    
    if (self.isCancelled) {
        
        return nil;
    }
    
    NSURL *url = [NSURL URLWithString:urlString];
    
    NSData *data = [NSData dataWithContentsOfURL:url];
    
    if (data) {
        
        // 将图片存入沙盒.
        // [[SHReadAndWriteSandbox sharedSandbox] writeDataToSandbox:data urlString:urlString];
        
    }
    
    if (self.isCancelled) {
        
        return nil;
    }
    
    
    UIImage *image = [UIImage imageWithData:data];
    
    return image;
}

@end

具体的工具类(管理类)解决图片缓存(防止图片错位,提升效率) + 操作缓存(防止重复创建操作)

//
//  SHWebImageManager.h
//  SHWebImage
//
//  Created by teacher on 16/1/9.
//  Copyright © 2016年 teacher. All rights reserved.
//

#import <Foundation/Foundation.h>
#import <UIKit/UIKit.h>

// 1.定义block类型(显示图片)

typedef void(^setUpImageBlock)(UIImage *image);

@interface SHWebImageManager : NSObject

// 图片缓存
@property(nonatomic ,strong) NSCache *images;

// 操作缓存
@property(nonatomic ,strong) NSMutableDictionary *operations;


//管理类.负责图片缓存和操作缓存. 单例.

+(instancetype)sharedManager;

// 下载图片的接口
// urlString: 图片的下载地址
// setUpImageBlk :图片下载完成之后的回调.(显示图片)
-(void)downloadWebImageWithUrlString:(NSString *)urlString setUpImageWithBlock:(setUpImageBlock)setUpImageBlk;

// 取消下载操作
-(void)cancelOperationWithUrlString:(NSString *)urlString;

@end

.m文件

//
//  SHWebImageManager.m
//  SHWebImage
//
//  Created by teacher on 16/1/9.
//  Copyright © 2016年 teacher. All rights reserved.
//

#import "SHWebImageManager.h"
#import "SHDownloadWebImageOperation.h"

@interface SHWebImageManager ()

// 操作队列
@property(nonatomic ,strong) NSOperationQueue *queue;

@end

@implementation SHWebImageManager


-(void)cancelOperationWithUrlString:(NSString *)urlString
{
    NSLog(@"取消原来的操作");
    // 1. 取出 urlString 对应的操作]
    SHDownloadWebImageOperation *op = [self.operations objectForKey:urlString];
    
    // 2. 取消操作
    [op cancel];
    
    // 3. 将操作从操作缓存中移除(可以重新下载)
    [self.operations removeObjectForKey:urlString];
    
}


// 下载图片
-(void)downloadWebImageWithUrlString:(NSString *)urlString setUpImageWithBlock:(setUpImageBlock)setUpImageBlk
{
    // 1. 创建操作
    SHDownloadWebImageOperation *downOp = [[SHDownloadWebImageOperation alloc] init];
    
    // 2. 告诉操作下载哪一张图片
    downOp.urlString = urlString;
    
    // 3. 图片下载完成之后的回调.
    [downOp downloadWebImageWithBlock:^(SHDownloadWebImageOperation *op) {
        if (op.image) {
            
            // 显示图片
            if (setUpImageBlk) {
                NSLog(@"图片下载完毕之后,直接显示图片");
                setUpImageBlk(op.image);
            }
            
            // 将图片添加到图片缓存中
            [self.images setObject:op.image forKey:urlString];
            
        }

        // 操作完成之后,清空操作缓存中的对应的操作
        [self.operations removeObjectForKey:urlString];
        
    }];
    
    // 4. 将操作添加到操作缓存中
    [self.operations setObject:downOp forKey:urlString];
    
    // 5. 将操作添加到操作队列中
    [self.queue addOperation:downOp];
    
}

//懒加载
-(NSMutableDictionary *)operations
{
    if (!_operations) {
        _operations = [NSMutableDictionary dictionary];
    }
    return _operations;
}

<pre name="code" class="objc" style="color: rgb(0, 132, 0); font-size: 18px;">//懒加载
-(NSCache *)images{ if (!_images) { _images = [[NSCache alloc] init]; } return _images;}-(NSOperationQueue *)queue{ if (!_queue) { _queue = [[NSOperationQueue alloc] init];

       //设置最大并发数
        [_queue setMaxConcurrentOperationCount:6];
    }
    return _queue;
}
//初始化单例对象
+(instancetype)sharedManager
{
    static id _instance;
    
    static dispatch_once_t onceToken;
    dispatch_once(&onceToken, ^{
        
        _instance = [[self alloc] init];
        
    });
    
    return _instance;
}

@end

沙盒缓存----解决读取数据问题

#import <Foundation/Foundation.h>
#import <UIKit/UIKit.h>

@interface SHReadAndWriteSandbox : NSObject

// 工具类: 单例.

// 提供实现单例的方法:
+(instancetype)sharedSandbox;

// 1.获得具体的文件路径(沙盒路径)
-(NSString *)getFilePathWithUrlString:(NSString *)urlString;

// 2.直接从沙盒中获取图片
-(UIImage *)getImageFromSandboxWithUrlString:(NSString *)urlString;

// 3.将数据写入沙盒
-(void)writeDataToSandbox:(NSData *)data urlString:(NSString *)urlString;

@end

.m文件

#import "SHReadAndWriteSandbox.h"

@implementation SHReadAndWriteSandbox

+(instancetype)sharedSandbox
{
    static id _instance;
    
    static dispatch_once_t onceToken;
    dispatch_once(&onceToken, ^{
        
        _instance = [[self alloc] init];
        
    });
    
    return _instance;
}

// 返回值: 具体的文件路径.
-(NSString *)getFilePathWithUrlString:(NSString *)urlString
{
    NSString *filePath = NSSearchPathForDirectoriesInDomains(NSCachesDirectory, NSUserDomainMask, YES).lastObject;
    
    static dispatch_once_t onceToken;
    dispatch_once(&onceToken, ^{
        NSLog(@"%@",filePath);
    });
    
    filePath = [filePath stringByAppendingPathComponent:urlString.lastPathComponent];

    return filePath;
}

-(UIImage *)getImageFromSandboxWithUrlString:(NSString *)urlString
{
    // 1. 文件路径
    NSString *filePath = [self getFilePathWithUrlString:urlString];
    
    UIImage *image = [UIImage imageWithContentsOfFile:filePath];
    
    return image;
}

-(void)writeDataToSandbox:(NSData *)data urlString:(NSString *)urlString
{
    [data writeToFile:[self getFilePathWithUrlString:urlString] atomically:YES];
}

@end


UIImageView 的一个分类.这个分类专门负责提供一句话代码的接口

//
//  UIImageView+WebImage.h
//  SHWebImage
//
//  Created by teacher on 16/1/9.
//  Copyright © 2016年 teacher. All rights reserved.
//

#import <UIKit/UIKit.h>
#import <objc/runtime.h>

@interface UIImageView (WebImage)

// @property :自动添加 set 和 get 方法.
// 分类中需要手动实现,利用运行时实现.动态的添加.
@property(nonatomic ,copy) NSString *urlString;


// 一句话代码下载图片的接口:
// urlString : 图片的下载地址.
// placeholderImage :占位图片.
-(void)downloadWebImageWithUrlString:(NSString *)urlString placeholderImage:(UIImage *)placeholderImage;

@end

.m文件
//
//  UIImageView+WebImage.m
//  SHWebImage
//
//  Created by teacher on 16/1/9.
//  Copyright © 2016年 teacher. All rights reserved.
//

#import "UIImageView+WebImage.h"
#import "SHWebImageManager.h"
#import "SHReadAndWriteSandbox.h"
#import "SHDownloadWebImageOperation.h"

@implementation UIImageView (WebImage)

const void * urlStringKey = "urlStringKey";

-(void)setUrlString:(NSString *)urlString
{
    // 参数:
    // 1. 为谁增加属性? self.
    // 2. 取值的 key .
    // 3. 需要设置的值 value.
    // 4. 安全策略(定时属性的时候选择的策略(原子/非原子,copy/retain/strong))
    
    objc_setAssociatedObject(self, urlStringKey, urlString, OBJC_ASSOCIATION_COPY_NONATOMIC);
    
}

-(NSString *)urlString
{
    return objc_getAssociatedObject(self, urlStringKey);
}


-(void)downloadWebImageWithUrlString:(NSString *)urlString placeholderImage:(UIImage *)placeholderImage
{
    // 下载并且显示图片.
    // self.image = image;
    
    // 1. 实例化图片下载管理类
    SHWebImageManager *manager = [SHWebImageManager sharedManager];
    
    // 什么时候才需要下载图片?
    
    // 1. 检查内存缓存中是否存在图片.
    UIImage *image = [manager.images objectForKey:urlString];
    
    if (image) {
        
        NSLog(@"直接从内存缓存中取出图片");
        
        self.image = image;
        return;
    }
    
    
    // 2. 内存缓存中没有图片,检查沙盒缓存中的图片
    UIImage *sandboxImage = [[SHReadAndWriteSandbox sharedSandbox] getImageFromSandboxWithUrlString:urlString];
    
    if (sandboxImage) {
        
        NSLog(@"从沙盒中读取图片");
        // 1.显示图片
        self.image = sandboxImage;
        
        // 2.将沙盒缓存中的图片添加到内存缓存中
        // 目的: 减少 I/O 操作.
        [manager.images setObject:sandboxImage forKey:urlString];
        
        return;
    }
    
    // 3. 添加占位图片
    if (placeholderImage) {
        
        self.image = placeholderImage;
    }
    
    
    // 判断这个操作是否已经存在?(判断跟imageView 绑定的图片下载地址是否改变!)
    if (self.urlString && [self.urlString isEqualToString:urlString]) {
        // self.urlString 存在并且传入的新地址跟原来的一样! 等待下载完毕.
        NSLog(@"操作已经存在,等待下载完毕...");
        return;
    }
    
    if (self.urlString && ![self.urlString isEqualToString:urlString]) {
        // self.urlString 存在,传入的新地址和原来的不同
        NSLog(@"取消原来的操作,开始下载新的图片");
        // 取消原来的下载操作
        [manager cancelOperationWithUrlString:self.urlString];
        
        // 给 self.urlString 绑定新的值.
        self.urlString = urlString;
        
        //开始新的下载操作 // 重新下载
        [self downloadWebImageWithUrlString:self.urlString];
        
        return;
    }
    
    if (!self.urlString) { // 第一次下载.
        
        NSLog(@"第一次下载图片");
        // imageView 绑定图片下载地址
        self.urlString = urlString;

        [self downloadWebImageWithUrlString:self.urlString];
    }
}


// 重新开始下载图片
- (void)downloadWebImageWithUrlString:(NSString *)urlString
{
    SHWebImageManager *manager = [SHWebImageManager sharedManager];
    
    // 4. 检查操作缓存中是否存在这个操作.
    SHDownloadWebImageOperation *op = [manager.operations objectForKey:urlString];
    
    if (op) {
        
        // 操作存在,等待下载结束.
        return;
    }
    
    // 5. 下载图片
    [manager downloadWebImageWithUrlString:urlString setUpImageWithBlock:^(UIImage *image) {
        
        // 显示图片.
        self.image = image;
    }];
}




@end




评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值