您的位置:首页 > 产品设计 > UI/UE

iOS开发笔记--异步加载UIImageView----AsyImageView

2016-01-27 10:05 591 查看

能够异步加载图片的UIImageView,通过调用方法loadImageWithUrl:与loadImageWithUrl:andDefaultImage:来进行异步加载。用到了NSCache、文件缓存、NSOperation、NSQueue来完成。首先是头文件的定义

[objc] view
plain copy







//定义缓存地址的宏

#define kCIVCache [NSHomeDirectory() stringByAppendingString:@"/Library/Caches/CIVCache.txt"]

定义加载Operation接口

[objc] view
plain copy







@protocol AsyImageLoadOperation <NSObject>

- (void)cancel;

@end

定义单例队列Manager

[objc] view
plain copy







@interface QueueManager : NSObject

+(id)sharedManager;

@property (nonatomic,retain) NSOperationQueue *queue;

@end

定义缓存模型

[objc] view
plain copy







@interface CIVImageCache : NSObject

@property (strong,nonatomic) NSURL *url;

@property (strong,nonatomic) NSData *imageData;

@end

定义图片加载Operation

[objc] view
plain copy







@interface ImageLoadOperation : NSOperation<AsyImageLoadOperation>

-(id)initWithUrl:(NSURL *)url;

@property (nonatomic,strong) NSURL *url;

@property (nonatomic,strong) CIVImageCache *resultCache;

@end

定义AsyImageView

[objc] view
plain copy







@interface AsyImageView : UIImageView

-(void)loadImageWithUrl:(NSURL *)url;

-(void)loadImageWithUrl:(NSURL *)url andDefultImage:(UIImage *)image;

@end

接着就是实现头文件,.m文件中需要引入

[objc] view
plain copy







#import "objc/runtime.h"

#include <sys/sysctl.h>

定义静态的operationKey字符变量

[objc] view
plain copy







static char operationKey;

QueueManager的实现

[objc] view
plain copy







@implementation QueueManager

unsigned int countOfCores() {

unsigned int ncpu;

size_t len = sizeof(ncpu);

sysctlbyname("hw.ncpu", &ncpu, &len, NULL, 0);

return ncpu;

}

static QueueManager *instance;

+(id)sharedManager{

static dispatch_once_t onceToken;

dispatch_once(&onceToken, ^{

instance=[[QueueManager alloc] init];

});

return instance;

}

-(id)init{

self=[super init];

if (self) {

_queue=[[NSOperationQueue alloc] init];

_queue.maxConcurrentOperationCount=countOfCores();

}

return self;

}

countOfCores方法用来获取当前机器的CPU核数。在init初始化中初始化一个NSOperationQueue,并为这个队列指定最大并发操作数(最好是CPU有多少核就设置为多少的最大并发数)。单例的实现使用GCD的dispatch_once。

实现缓存模型对象

[objc] view
plain copy







@implementation CIVImageCache

-(id)init{

self=[super init];

if (self){

_imageData=[[NSData alloc] init];

_url=[[NSURL alloc] init];

}

return self;

}

-(id)initWithCoder:(NSCoder *)aDecoder{

self=[super init];

if (self){

_imageData = [aDecoder decodeObjectForKey:@"_imageData"];

_url = [aDecoder decodeObjectForKey:@"_url"];

}

return self;

}

-(void)encodeWithCoder:(NSCoder *)aCoder{

[aCoder encodeObject:_imageData forKey:@"_imageData"];

[aCoder encodeObject:_url forKey:@"_url"];

}

-(id)copyWithZone:(NSZone *)zone{

CIVImageCache *uObject=[[[self class] allocWithZone:zone] init];

uObject.imageData=self.imageData;

uObject.url=self.url;

return uObject;

}

@end

这个没有什么好说的,这个对象主要用来在加载完成后传递数据以及将数据保存在本地。 接下来实现图片加载操作(ImageLoadOperation)

[objc] view
plain copy







@implementation ImageLoadOperation

-(id)initWithUrl:(NSURL *)url{

self=[super init];

if (self) {

_url=url;

}

return self;

}

-(void)main{

NSData *cacheData=[NSData dataWithContentsOfFile:kCIVCache];

NSDictionary *cacheDic=[NSKeyedUnarchiver unarchiveObjectWithData:cacheData];

if (cacheDic!=nil) {

if ([[cacheDic allKeys] containsObject:_url.description]) {

CIVImageCache *cache=[[CIVImageCache alloc] init];

cache.url=_url;

cache.imageData=[cacheDic objectForKey:_url.description];

_resultCache=cache;

}else{

[self loadFromInternet];

}

}else{

[self loadFromInternet];

}

}

-(void)loadFromInternet{

NSData *imageData=[NSData dataWithContentsOfURL:_url];

UIImage *image=[UIImage imageWithData:imageData];

imageData = UIImageJPEGRepresentation(image, 0.0000001);

CIVImageCache *cache=[[CIVImageCache alloc] init];

cache.url=_url;

cache.imageData=imageData;

_resultCache=cache;

}

@end

main函数中为主要的加载操作,首先从本地缓存中获取数据,判断是否已经存在URL的请求缓存,如果没有调用loadFromInternet方法从网络下载图片。

最后来实现异步

[objc] view
plain copy







@interface AsyImageView ()

@property (nonatomic,weak) NSOperation *lastOperation;

@property (strong, nonatomic) NSCache *memCache;

@property (assign, nonatomic) dispatch_queue_t ioQueue;

@end

定义AsyImageView的“私有”变量,lastOperation用来关联operation的顺序(这个最后好像没有用到)memCache则用来进行缓存,ioQueue是用来缓存文件的操作队列。

AsyImageView使用两种初始化方法来初始化:

[objc] view
plain copy







-(id)init{

self=[super init];

if (self) {

_ioQueue = dispatch_queue_create("com.noez.AsyImageCache", DISPATCH_QUEUE_SERIAL);

NSString *fullNamespace = [@"com.noez.AsyImageCache." stringByAppendingString:@"1"];

_memCache = [[NSCache alloc] init];

_memCache.name = fullNamespace;

}

return self;

}

-(id)initWithFrame:(CGRect)frame{

self=[super initWithFrame:frame];

if (self) {

_ioQueue = dispatch_queue_create("com.noez.AsyImageCache", DISPATCH_QUEUE_SERIAL);

NSString *fullNamespace = [@"com.noez.AsyImageCache." stringByAppendingString:@"1"];

_memCache = [[NSCache alloc] init];

_memCache.name = fullNamespace;

}

return self;

}

在AsyImageView中的layoutSubviews中也需要初始化ioQueue与memCache,如果是直接在XIB中直接设置AsyImageView的话并不会调用两个初始化方法。

loadImageWithUrl方法中开始异步的加载过程:

[objc] view
plain copy







-(void)loadImageWithUrl:(NSURL *)url{

[self cancelCurrentImageLoad];

NSOperationQueue *queue=[[QueueManager sharedManager] queue];

__weak AsyImageView *weakSelf=self;

id<AsyImageLoadOperation> operation=[self downloadWithURL:url queue:queue completed:^(UIImage *image, NSData *data, NSError *error, BOOL isFinished) {

void (^block)(void) = ^{

__strong AsyImageView *sself = weakSelf;

if (!sself) return;

if (image){

sself.image = image;

[sself setNeedsLayout];

}

};

if ([NSThread isMainThread]){

block();

}

else{

dispatch_sync(dispatch_get_main_queue(), block);

}

}];

objc_setAssociatedObject(self, &operationKey, operation, OBJC_ASSOCIATION_RETAIN_NONATOMIC);

}

在这个方法中首先调用cancelCurrentImageLoad来取消当前的加载操作:

[objc] view
plain copy







- (void)cancelCurrentImageLoad

{

// Cancel in progress downloader from queue

id<AsyImageLoadOperation> operation = objc_getAssociatedObject(self, &operationKey);

if (operation)

{

[operation cancel];

objc_setAssociatedObject(self, &operationKey, nil, OBJC_ASSOCIATION_RETAIN_NONATOMIC);

}

}

之后获取单例的操作队列后调用downloadWithURL方法开始异步加载,加载完之后通过block将图片赋值给image属性。

[objc] view
plain copy







-(id<AsyImageLoadOperation>)downloadWithURL:(NSURL *)url queue:(NSOperationQueue *)queue completed:(void (^)(UIImage *image, NSData *data, NSError *error, BOOL isFinished))completedBlock{

ImageLoadOperation *op=[[ImageLoadOperation alloc] init];

[self queryDiskCacheForKey:url.description done:^(UIImage *image) {

if (image==nil) {

op.url=url;

__weak ImageLoadOperation *weakOp=op;

op.completionBlock=^{

CIVImageCache *cache=weakOp.resultCache;

UIImage *dimage=[UIImage imageWithData:cache.imageData];

completedBlock(dimage,nil,nil,YES);

[self storeImage:dimage imageData:cache.imageData forKey:cache.url.description toDisk:YES];

};

[self.lastOperation addDependency:op];//待定

self.lastOperation=op;

[queue addOperation:op];

}else{

completedBlock(image,nil,nil,YES);

}

}];

return op;

}

在加载前首先调用queryDiskCacheForKey方法从缓存中获取图片,如果缓存中没有图片,则使用图片加载操作加载图片,在操作完成时使用block保存图片并调用completedBlock显示图片。如果缓存中有图片则直接调用completedBlock显示图片。一下分别是保存图片与从缓存获取图片的方法:

[objc] view
plain copy







- (void)storeImage:(UIImage *)image imageData:(NSData *)imageData forKey:(NSString *)key toDisk:(BOOL)toDisk{

if (!image || !key){

return;

}

[self.memCache setObject:image forKey:key cost:image.size.height * image.size.width * image.scale];

if (toDisk){

dispatch_async(self.ioQueue, ^{

NSData *data = imageData;

if (!data){

if (image){

data = UIImageJPEGRepresentation(image, (CGFloat)1.0);

}

}

if (data){

NSData *cacheData=[NSData dataWithContentsOfFile:kCIVCache];

if (cacheData==nil) {

cacheData=[[NSData alloc] init];

}

NSMutableDictionary *dic=[NSKeyedUnarchiver unarchiveObjectWithData:cacheData];

if (dic==nil) {

dic=[[NSMutableDictionary alloc] init];

}

if (![[dic allKeys] containsObject:key]) {

[dic setObject:data forKey:key];

}

NSData *data=[NSKeyedArchiver archivedDataWithRootObject:dic];

[data writeToFile:kCIVCache atomically:YES];

}

});

}

}

- (void)queryDiskCacheForKey:(NSString *)key done:(void (^)(UIImage *image))doneBlock{

if (!doneBlock) return;

if (!key){

doneBlock(nil);

return;

}

UIImage *image = [self imageFromMemoryCacheForKey:key];

if (image){

doneBlock(image);

return;

}

if (_ioQueue==nil) {

_ioQueue = dispatch_queue_create("com.noez.AsyImageCache", DISPATCH_QUEUE_SERIAL);

}

dispatch_async(self.ioQueue, ^{

@autoreleasepool{

UIImage *diskImage = [self diskImageForKey:key];

if (diskImage){

CGFloat cost = diskImage.size.height * diskImage.size.width * diskImage.scale;

[self.memCache setObject:diskImage forKey:key cost:cost];

}

dispatch_async(dispatch_get_main_queue(), ^{

doneBlock(diskImage);

});

}

});

}

- (UIImage *)imageFromMemoryCacheForKey:(NSString *)key {

return [self.memCache objectForKey:key];

}

转自:http://segmentfault.com/a/1190000000313657?page=1
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: