这个类库提供一个UIImageView类别以支持加载来自网络的远程图片。具有缓存管理、异步下载、同一个URL下载次数控制和优化等特征。

地址:https://github.com/rs/SDWebImage

原理图:

各个类的交互图:

一 插件的运用

针对这部分的理论知识可以查看文章《SDWebImage 图片下载缓存框架 常用方法及原理》,已经针对SDWebImage的相关知识点都有相应介绍;并且把相关的类都有注解,接下来将会简单介绍一些属性及小知识点:

1.1 设置存储路径

可以在项目AppDelegate设置存储路径,SDWebImage默认使用磁盘缓存,在 沙盒/Library/Cache中可以找到带WebImageCache字眼的目录,可以找到缓存的图片,下面这个可以设置一个只读的存储路径:

  1. NSString *bundledPath = [[[NSBundle mainBundle] resourcePath] stringByAppendingPathComponent:@"CustomPathImages"];
  2. [[SDImageCache sharedImageCache] addReadOnlyCachePath:bundledPath];

1.2 SDWebImage的最大并发数是多少

在类SDWebImageDownloader中,默认并发数为6(_downloadQueue.maxConcurrentOperationCount = 6);也可以修改maxConcurrentDownloads设置其下载并发数;

1.3 SDWebImage缓存周期

SDWebImage缓存周期为一周,可以在类SDImageCache里面有kDefaultCacheMaxCacheAge常量,定义的缓存时间;static const NSInteger kDefaultCacheMaxCacheAge = 60 * 60 * 24 * 7; // 1 week

1.4 SDWebImage 缓存图片命名规则

为了防止名称重复,对其进行 md5 运算;

1.5 默认下载的超时时长是多少?

默认为15秒,可以在类SDWebImageDownloader中设置downloadTimeout

1.6 SDWebImage用什么类型缓存图片?

NSCache,SDImageCache内处理内存警告,以通知的方式,clearMemory

1.7 cleanDisk的执行过程

  1. i. 先遍历所有的缓存文件,记录过期的文件,计算缓存文件的总大小
  2. ii. 删除过期的文件
  3. iii. 判断maxCacheSize的值是否>,如果大于0再判断缓存的文件总大小是否大于maxCacheSize
  4. iv.如果缓存文件的总大小超过maxCacheSize,删除最早的文件
  5. 注意:.jpg、.gif等文件需要把扩展名填上,png不需要

1.8 SDWebImage获得缓存大小,并对它进行清除

  1. float tmpSize = [[SDImageCache sharedImageCache] getSize];
  2. NSString *clearCacheName =@"当前缓存已清理";
  3. if (tmpSize>) {
  4. clearCacheName=tmpSize >= ? [NSString stringWithFormat:@"成功清理缓存(%.2fM)",tmpSize] : [NSString stringWithFormat:@"成功清理缓存(%.2fK)",tmpSize * ];
  5. }
  6. [[SDImageCache sharedImageCache] clearDisk];

1.9 SDWebImages是如何识别图片

NSData+ImageContentType.m中,根据图片文件十六进制数据的第一个字节判断

  1. + (NSString *)sd_contentTypeForImageData:(NSData *)data {
  2. uint8_t c;
  3. [data getBytes:&c length:];
  4. switch (c) {
  5. case 0xFF:
  6. return @"image/jpeg";
  7. case 0x89:
  8. return @"image/png";
  9. case 0x47:
  10. return @"image/gif";
  11. case 0x49:
  12. case 0x4D:
  13. return @"image/tiff";
  14. case 0x52:
  15. // R as RIFF for WEBP
  16. if ([data length] < ) {
  17. return nil;
  18. }
  19.  
  20. NSString *testString = [[NSString alloc] initWithData:[data subdataWithRange:NSMakeRange(, )] encoding:NSASCIIStringEncoding];
  21. if ([testString hasPrefix:@"RIFF"] && [testString hasSuffix:@"WEBP"]) {
  22. return @"image/webp";
  23. }
  24.  
  25. return nil;
  26. }
  27. return nil;
  28. }

1.10 SDImageCacheType 缓存类型

  1. SDImageCacheTypeNone 永不缓存,但是从网上下载
  2. SDImageCacheTypeDisk 只缓存到磁盘上
  3. SDImageCacheTypeMemory 只缓存到内存中

1.11 SDWebImageDownloaderProgressBlock 下载进度

  1. typedef void(^SDWebImageDownloaderProgressBlock)(NSInteger receivedSize, NSInteger expectedSize);
  2. progress 参数:
  3. receivedSize 接收到的字节数
  4. expectedSize 期望下载的字节数
  5. //乘1.0是为了转换成float类型
  6. float progress = receivedSize * 1.0 / expectedSize;
  1. NSURL *url = [NSURL URLWithString:@"http://picview01.baomihua.com/photos/20120624/m_14_634761470842343750_15728444.jpg"];
  2. [self.imageView sd_setImageWithURL:url placeholderImage:nil options: progress:^(NSInteger receivedSize, NSInteger expectedSize) {
  3. //乘1.0是为了转换成float类型
  4. float progress = receivedSize * 1.0 / expectedSize;
  5. NSLog(@"下载进度 %f",progress);
  6. } completed:^(UIImage *image, NSError *error, SDImageCacheType cacheType, NSURL *imageURL) {
  7. NSLog(@"完成");
  8. }];

1.12 SDWebImageOptions 属性

  1.  

typedef NS_OPTIONS(NSUInteger, SDWebImageOptions) {

  1.  

// 默认情况下,当URL下载失败时,URL会被列入黑名单,导致库不会再去重试,该标记用于禁用黑名单
SDWebImageRetryFailed = 1 << 0,

  1.  

// 默认情况下,图片下载开始于UI交互,该标记禁用这一特性,这样下载延迟到UIScrollView减速时
SDWebImageLowPriority = 1 << 1,

  1.  

// 该标记禁用磁盘缓存
SDWebImageCacheMemoryOnly = 1 << 2,

  1.  

// 该标记启用渐进式下载,图片在下载过程中是渐渐显示的,如同浏览器一下。
// 默认情况下,图像在下载完成后一次性显示
SDWebImageProgressiveDownload = 1 << 3,

  1.  

// 即使图片缓存了,也期望HTTP响应cache control,并在需要的情况下从远程刷新图片。
// 磁盘缓存将被NSURLCache处理而不是SDWebImage,因为SDWebImage会导致轻微的性能下载。
// 该标记帮助处理在相同请求URL后面改变的图片。如果缓存图片被刷新,则完成block会使用缓存图片调用一次
// 然后再用最终图片调用一次
SDWebImageRefreshCached = 1 << 4,

  1.  

// 在iOS 4+系统中,当程序进入后台后继续下载图片。这将要求系统给予额外的时间让请求完成
// 如果后台任务超时,则操作被取消
SDWebImageContinueInBackground = 1 << 5,

  1.  

// 通过设置NSMutableURLRequest.HTTPShouldHandleCookies = YES;来处理存储在NSHTTPCookieStore中的cookie
SDWebImageHandleCookies = 1 << 6,

  1.  

// 允许不受信任的SSL认证
SDWebImageAllowInvalidSSLCertificates = 1 << 7,

  1.  

// 默认情况下,图片下载按入队的顺序来执行。该标记将其移到队列的前面,
// 以便图片能立即下载而不是等到当前队列被加载
SDWebImageHighPriority = 1 << 8,

  1.  

// 默认情况下,占位图片在加载图片的同时被加载。该标记延迟占位图片的加载直到图片已以被加载完成
SDWebImageDelayPlaceholder = 1 << 9,

  1.  

// 通常我们不调用动画图片的transformDownloadedImage代理方法,因为大多数转换代码可以管理它。
// 使用这个票房则不任何情况下都进行转换。
SDWebImageTransformAnimatedImage = 1 << 10,
};

  1.  

运用如下:

  1. 运用上面的两个
  2.  
  3. SDWebImageRetryFailed : 下载失败后,会自动重新下载
  4. SDWebImageLowPriority : 当正在进行UI交互时,自动暂停内部的一些下载操作
  5. SDWebImageRetryFailed | SDWebImageLowPriority : 拥有上面2个功能
  6.  
  7. - (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
  8. {
  9. static NSString *ID = @"app";
  10. UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:ID];
  11. if (!cell) {
  12. cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleSubtitle reuseIdentifier:ID];
  13. }
  14.  
  15. // 取出模型
  16. HMApp *app = self.apps[indexPath.row];
  17.  
  18. // 设置基本信息
  19. cell.textLabel.text = app.name;
  20. cell.detailTextLabel.text = app.download;
  21.  
  22. // 下载图片
  23. NSURL *url = [NSURL URLWithString:app.icon];
  24. UIImage *placeholder = [UIImage imageNamed:@"placeholder"];
  25.  
  26. SDWebImageOptions options = SDWebImageRetryFailed | SDWebImageLowPriority;
  27.  
  28. [cell.imageView sd_setImageWithURL:url placeholderImage:placeholder options:options progress:^(NSInteger receivedSize, NSInteger expectedSize) { // 这个block可能会被调用多次
  29. NSLog(@"下载进度:%f", (double)receivedSize / expectedSize);
  30. } completed:^(UIImage *image, NSError *error, SDImageCacheType cacheType, NSURL *imageURL) {
  31. NSLog(@"----图片加载完毕---%@", image);
  32. }];
  33. return cell;
  34. }

1.1.3 下载顺序SDWebImageDownloaderExecutionOrder

  1. typedef NS_ENUM(NSInteger, SDWebImageDownloaderExecutionOrder) {
  2.  
  3. // 以队列的方式,按照先进先出的顺序下载。这是默认的下载顺序
  4. SDWebImageDownloaderFIFOExecutionOrder,
  5.  
  6. // 以栈的方式,按照后进先出的顺序下载。
  7. SDWebImageDownloaderLIFOExecutionOrder
  8. };

1.1.4 SDWebImageDecoder类异步对图像进行了一次解压

由于UIImage的imageWithData函数是每次画图的时候才将Data解压成ARGB的图像,所以在每次画图的时候,会有一个解压操作,这样效率很低,但是只有瞬时的内存需求。为了提高效率通过SDWebImageDecoder将包装在Data下的资源解压,然后画在另外一张图片上,这样这张新图片就不再需要重复解压了。这种做法是典型的空间换时间的做法。

1.1.5 SDImageCache是怎么做数据管理的

SDImageCache分两个部分,一个是内存层面的,一个是硬盘层面的。内存层面的相当是个缓存器,以Key-Value的形式存储图片。当内存不够的时候会清除所有缓存图片。用搜索文件系统的方式做管理,文件替换方式是以时间为单位,剔除时间大于一周的图片文件。当SDWebImageManager向SDImageCache要资源时,先搜索内存层面的数据,如果有直接返回,没有的话去访问磁盘,将图片从磁盘读取出来,然后调SDWebImageDecoder做Decoder,将图片对象放到内存层面做备份,再返回调用层。

1.1.6 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 可以预先下载图片,方便后续使用。

二 知识点

2.1 typedef定义Block

这样定义方法时直接用typedef的类型,只要有引入此头文件,都可以使用到,在block里面进行主线程操作,这样就可以省得在调用时每个地方都写;

  1. SDImageCache.h文件:
  2.  
  3. typedef void(^SDWebImageQueryCompletedBlock)(UIImage *image, SDImageCacheType cacheType);
  4.  
  5. @interface SDImageCache : NSObject
  6.  
  7. - (NSOperation *)queryDiskCacheForKey:(NSString *)key done:(SDWebImageQueryCompletedBlock)doneBlock;
  8.  
  9. @end
  10.  
  11. SDImageCache.m文件
  12.  
  13. - (NSOperation *)queryDiskCacheForKey:(NSString *)key done:(SDWebImageQueryCompletedBlock)doneBlock {
  14. if (!doneBlock) {
  15. return nil;
  16. }
  17.  
  18. if (!key) {
  19. doneBlock(nil, SDImageCacheTypeNone);
  20. return nil;
  21. }
  22.  
  23. // First check the in-memory cache...
  24. UIImage *image = [self imageFromMemoryCacheForKey:key];
  25. if (image) {
  26. doneBlock(image, SDImageCacheTypeMemory);
  27. return nil;
  28. }
  29.  
  30. NSOperation *operation = [NSOperation new];
  31. dispatch_async(self.ioQueue, ^{
  32. if (operation.isCancelled) {
  33. return;
  34. }
  35.  
  36. @autoreleasepool {
  37. UIImage *diskImage = [self diskImageForKey:key];
  38. if (diskImage && self.shouldCacheImagesInMemory) {
  39. NSUInteger cost = SDCacheCostForImage(diskImage);
  40. [self.memCache setObject:diskImage forKey:key cost:cost];
  41. }
  42.  
  43. dispatch_async(dispatch_get_main_queue(), ^{
  44. doneBlock(diskImage, SDImageCacheTypeDisk);
  45. });
  46. }
  47. });
  48.  
  49. return operation;
  50. }

2.2 SDWebImage 有两个宏 来判断程序在主线程运行(sync同步 async异步)

  1. #define dispatch_main_sync_safe(block)\
  2. if ([NSThread isMainThread]) {\
  3. block();\
  4. } else {\
  5. dispatch_sync(dispatch_get_main_queue(), block);\
  6. }
  7.  
  8. #define dispatch_main_async_safe(block)\
  9. if ([NSThread isMainThread]) {\
  10. block();\
  11. } else {\
  12. dispatch_async(dispatch_get_main_queue(), block);\
  13. }

运用如下:

  1. if ([self showActivityIndicatorView]) {
  2. [self addActivityIndicator];
  3. }
  4.  
  5. __weak __typeof(self)wself = self;
  6. id <SDWebImageOperation> operation = [SDWebImageManager.sharedManager downloadImageWithURL:url options:options progress:progressBlock completed:^(UIImage *image, NSError *error, SDImageCacheType cacheType, BOOL finished, NSURL *imageURL) {
  7. [wself removeActivityIndicator];
  8. if (!wself) return;
  9. dispatch_main_sync_safe(^{
  10. if (!wself) return;
  11. if (image && (options & SDWebImageAvoidAutoSetImage) && completedBlock)
  12. {
  13. completedBlock(image, error, cacheType, url);
  14. return;
  15. }
  16. else if (image) {
  17. wself.image = image;
  18. [wself setNeedsLayout];
  19. } else {
  20. if ((options & SDWebImageDelayPlaceholder)) {
  21. wself.image = placeholder;
  22. [wself setNeedsLayout];
  23. }
  24. }
  25. if (completedBlock && finished) {
  26. completedBlock(image, error, cacheType, url);
  27. }
  28. });
  29. }];
  30. [self sd_setImageLoadOperation:operation forKey:@"UIImageViewImageLoad"];

2.3 三元?:符号的运用

  1. [self sd_setImageWithURL:url placeholderImage:lastPreviousCachedImage ?: placeholder options:options progress:progressBlock completed:completedBlock];

若?后面的值 lastPreviousCachedImage则可以这么简单写

2.4 SDWebImage 部分清除缓存的原理

部分清理则是根据我们设定的一些参数值来移除一些文件,这里主要有两个指标:文件的缓存有效期及最大缓存空间大小。文件的缓存有效期可以通过maxCacheAge属性来设置,默认是1周的时间。如果文件的缓存时间超过这个时间值,则将其移除。而最大缓存空间大小是通过maxCacheSize属性来设置的,如果所有缓存文件的总大小超过这一大小,则会按照文件最后修改时间的逆序,以每次一半的递归来移除那些过早的文件,直到缓存的实际大小小于我们设置的最大使用空间。清理的操作在-cleanDiskWithCompletionBlock:方法中,其实现如下:

  1. - (void)cleanDiskWithCompletionBlock:(SDWebImageNoParamsBlock)completionBlock {
  2. dispatch_async(self.ioQueue, ^{
  3. NSURL *diskCacheURL = [NSURL fileURLWithPath:self.diskCachePath isDirectory:YES];
  4. NSArray *resourceKeys = @[NSURLIsDirectoryKey, NSURLContentModificationDateKey, NSURLTotalFileAllocatedSizeKey];
  5.  
  6. // 1. 该枚举器预先获取缓存文件的有用的属性
  7. NSDirectoryEnumerator *fileEnumerator = [_fileManager enumeratorAtURL:diskCacheURL
  8. includingPropertiesForKeys:resourceKeys
  9. options:NSDirectoryEnumerationSkipsHiddenFiles
  10. errorHandler:NULL];
  11.  
  12. NSDate *expirationDate = [NSDate dateWithTimeIntervalSinceNow:-self.maxCacheAge];
  13. NSMutableDictionary *cacheFiles = [NSMutableDictionary dictionary];
  14. NSUInteger currentCacheSize = ;
  15.  
  16. // 2. 枚举缓存文件夹中所有文件,该迭代有两个目的:移除比过期日期更老的文件;存储文件属性以备后面执行基于缓存大小的清理操作
  17. NSMutableArray *urlsToDelete = [[NSMutableArray alloc] init];
  18. for (NSURL *fileURL in fileEnumerator) {
  19. NSDictionary *resourceValues = [fileURL resourceValuesForKeys:resourceKeys error:NULL];
  20.  
  21. // 3. 跳过文件夹
  22. if ([resourceValues[NSURLIsDirectoryKey] boolValue]) {
  23. continue;
  24. }
  25.  
  26. // 4. 移除早于有效期的老文件
  27. NSDate *modificationDate = resourceValues[NSURLContentModificationDateKey];
  28. if ([[modificationDate laterDate:expirationDate] isEqualToDate:expirationDate]) {
  29. [urlsToDelete addObject:fileURL];
  30. continue;
  31. }
  32.  
  33. // 5. 存储文件的引用并计算所有文件的总大小,以备后用
  34. NSNumber *totalAllocatedSize = resourceValues[NSURLTotalFileAllocatedSizeKey];
  35. currentCacheSize += [totalAllocatedSize unsignedIntegerValue];
  36. [cacheFiles setObject:resourceValues forKey:fileURL];
  37. }
  38.  
  39. for (NSURL *fileURL in urlsToDelete) {
  40. [_fileManager removeItemAtURL:fileURL error:nil];
  41. }
  42.  
  43. // 6.如果磁盘缓存的大小大于我们配置的最大大小,则执行基于文件大小的清理,我们首先删除最老的文件
  44. if (self.maxCacheSize > && currentCacheSize > self.maxCacheSize) {
  45. // 7. 以设置的最大缓存大小的一半作为清理目标
  46. const NSUInteger desiredCacheSize = self.maxCacheSize / ;
  47.  
  48. // 8. 按照最后修改时间来排序剩下的缓存文件
  49. NSArray *sortedFiles = [cacheFiles keysSortedByValueWithOptions:NSSortConcurrent
  50. usingComparator:^NSComparisonResult(id obj1, id obj2) {
  51. return [obj1[NSURLContentModificationDateKey] compare:obj2[NSURLContentModificationDateKey]];
  52. }];
  53.  
  54. // 9. 删除文件,直到缓存总大小降到我们期望的大小
  55. for (NSURL *fileURL in sortedFiles) {
  56. if ([_fileManager removeItemAtURL:fileURL error:nil]) {
  57. NSDictionary *resourceValues = cacheFiles[fileURL];
  58. NSNumber *totalAllocatedSize = resourceValues[NSURLTotalFileAllocatedSizeKey];
  59. currentCacheSize -= [totalAllocatedSize unsignedIntegerValue];
  60.  
  61. if (currentCacheSize < desiredCacheSize) {
  62. break;
  63. }
  64. }
  65. }
  66. }
  67. if (completionBlock) {
  68. dispatch_async(dispatch_get_main_queue(), ^{
  69. completionBlock();
  70. });
  71. }
  72. });
  73. }

2.5  查看IOS沙盒中文件的属性(修改日期,创建日期,大小等)

  1. NSString *strPath =[[NSBundle mainBundle] pathForResource:@"lomo.jpg" ofType:nil];
  2. NSLog(@"path:%@", strPath);
  3. NSFileManager *fileManager = [NSFileManager defaultManager];
  4. NSString *path = strPath;//@"/tmp/List";
  5. NSError *error = nil;
  6. NSDictionary *fileAttributes = [fileManager attributesOfItemAtPath:path error:&error];
  7.  
  8. if (fileAttributes != nil) {
  9. NSNumber *fileSize = [fileAttributes objectForKey:NSFileSize];
  10. NSString *fileOwner = [fileAttributes objectForKey:NSFileOwnerAccountName];
  11. NSDate *fileModDate = [fileAttributes objectForKey:NSFileModificationDate];
  12. NSDate *fileCreateDate = [fileAttributes objectForKey:NSFileCreationDate];
  13. if (fileSize) {
  14. NSLog(@"File size: %qi\n", [fileSize unsignedLongLongValue]);
  15. }
  16. if (fileOwner) {
  17. NSLog(@"Owner: %@\n", fileOwner);
  18. }
  19. if (fileModDate) {
  20. NSLog(@"Modification date: %@\n", fileModDate);
  21. }
  22. if (fileCreateDate) {
  23. NSLog(@"create date:%@\n", fileModDate);
  24. }
  25. }
  26. else {
  27. NSLog(@"Path (%@) is invalid.", path);
  28. }

2.6 遍历文件NSDirectoryEnumerator

需要获得目录的内容列表,使用enumeratorAtPath:方法或者directoryC ontentsAtPath:方法,可以完成枚举过程。如果使用第一种enumeratorAtPath:方法,一次可以枚举指定目录中的每个文件。默认情况下,如果其中一个文件为目录,那么也会递归枚举它的内容。在这个过程中,通过向枚举对象发送一条skipDescendants消息,可以动态地阻止递归过程,从而不再枚举目录中的内容。对于directoryContentsAtPath:方法,使用这个方法,可以枚举指定目录的内容,并在一个数组中返回文件列表。如果这个目录中的任何文件本身是个目录,这个方法并不递归枚举它的内容。

  1. NSString *path;
  2. NSFileManager *fm;
  3. NSDirectoryEnumerator *dirEnum;
  4. NSArray *dirArray;
  5.  
  6. fm = [NSFileManager defaultManager];
  7.  
  8. //获取当前的工作目录的路径
  9. path = [fm currentDirectoryPath];
  10.  
  11. //遍历这个目录的第一种方法:(深度遍历,会递归枚举它的内容)
  12. dirEnum = [fm enumeratorAtPath:path];
  13.  
  14. NSLog(@"1.Contents of %@:",path);
  15. while ((path = [dirEnum nextObject]) != nil)
  16. {
  17. NSLog(@"%@",path);
  18. }
  19.  
  20. //遍历目录的另一种方法:(不递归枚举文件夹种的内容)
  21. dirArray = [fm directoryContentsAtPath:[fm currentDirectoryPath]];
  22. NSLog(@"2.Contents using directoryContentsAtPath:");
  23.  
  24. for(path in dirArray)
  25. NSLog(@"%@",path);

通过以上程序变例如下文件路径:

如果对上述代码while循环做如下修改,可以阻止任何子目录中的枚举

  1. while ((path = [dirEnum nextObject]) != nil)
  2. {
  3.  
  4. NSLog(@"%@",path);
  5.  
  6. BOOL flag;
  7. [fm fileExistsAtPath:path isDirectory:&flag];
  8. if(flag == YES)
  9. [dirEnum skipDescendants];
  10. }

这里flag是一个BOOL类型的变量。如果指定的路径是目录,则fileExistsAtPath:在flag中存储YES,否则存储NO。

2.7 SDImageCache 后台运行通知注册

先说说iOS 应用程序5个状态;

停止运行-应用程序已经终止,或者还未启动。

不活动-应用程序处于前台但不再接收事件(例如,用户在app处于活动时锁住了设备)。

活动-app处于“使用中”的状态。

后台-app不再屏幕上显示,但它仍然执行代码。

挂起-app仍然驻留内存但不再执行代码。

按下Home键时,app从活动状态转入后台,绝大部分app通常在几秒内就从后台变成了挂起。从 iOS 4 开始,应用就可以在退到后台后,继续运行一小段时间(10 分钟);iOS 7 需要注意的区别:iOS 7 以前,应用进入后台继续运行时,如果用户锁屏了,那么 iOS 会等待应用运行完,才进入睡眠状态。而在 iOS 7 上,系统会很快进入睡眠状态,那些后台应用也就暂停了。如果收到事件被唤醒(例如定时事件、推送、位置更新等),后台应用才能继续运行一会。因为处理过程变成了断断续续的。更多关于后台运行的知识可以查看这文章这文章

a:如果是在程序的AppDelegate里面,就可以直接在这个方法里面处理;

  1. // 当应用程序掉到后台时,执行该方法
  2. - (void)applicationDidEnterBackground:(UIApplication *)application
  3. {
  4.     
  5. }

b:如果是自个的类或控制器要处理,比如进入后台当前页或类的一些信息要进行保存;那么就要注册系统的通知;下面SDImageCache 里面的实现代码进行讲解;

  1. - (id)initWithNamespace:(NSString *)ns diskCacheDirectory:(NSString *)directory {
  2. if ((self = [super init])) {
  3.  
  4. [[NSNotificationCenter defaultCenter] addObserver:self
  5. selector:@selector(backgroundCleanDisk)
  6. name:UIApplicationDidEnterBackgroundNotification
  7. object:nil];
  8. }
  9.  
  10. return self;
  11. }
  12.  
  13. - (void)backgroundCleanDisk {
  14. Class UIApplicationClass = NSClassFromString(@"UIApplication");
  15. if(!UIApplicationClass || ![UIApplicationClass respondsToSelector:@selector(sharedApplication)]) {
  16. return;
  17. }
  18. UIApplication *application = [UIApplication performSelector:@selector(sharedApplication)];
  19. __block UIBackgroundTaskIdentifier bgTask = [application beginBackgroundTaskWithExpirationHandler:^{
  20. //完成后,要告诉iOS,任务完成,提交完成申请“好借好还”
  21. //标记指定的后台任务完成
  22. [application endBackgroundTask:bgTask];
  23. //销毁后台任务标识符
  24. bgTask = UIBackgroundTaskInvalid;
  25. }];
  26.  
  27. // 调用本地的方法,在完成后再进行进入挂起,完成后,要告诉iOS,任务完成,提交完成申请“好借好还”
  28. [self cleanDiskWithCompletionBlock:^{
  29. [application endBackgroundTask:bgTask];
  30. bgTask = UIBackgroundTaskInvalid;
  31. }];
  32. }
  33.  
  34. //记得删除通知
  35. - (void)dealloc {
  36. [[NSNotificationCenter defaultCenter] removeObserver:self];
  37. }

注意:UIApplication这边是通过运行时获取;UIBackgroundTaskIdentifier知识点可以看介绍;当一个 iOS 应用被送到后台,它的主线程会被暂停。你用 NSThread 的 detachNewThreadSelector:toTar get:withObject:类方法创建的线程也被挂起了。如果你想在后台完成一个长期任务,就必须调用 UIApplication 的beginBackgroundTaskWithExpirationHandler:实例方法,来向 iOS 借点时间。默认情况下,如果在这个期限内,长期任务没有被完成,iOS 将终止程序。怎么办?可以使用 beginBackgroundTaskWithExpirationHandler:实例方法,来向 iOS 再借点时间。借和换必须成双成对!程序提前完成了,也可以提前结束(当然上面还是要用application):

  1. [[UIApplication sharedApplication] endBackgroundTask:self.backgroundTaskIdentifier];
  2. self.backgroundTaskIdentifier = UIBackgroundTaskInvalid;

最近有个妹子弄的一个关于扩大眼界跟内含的订阅号,每天都会更新一些深度内容,在这里如果你感兴趣也可以关注一下(嘿对美女跟知识感兴趣),当然可以关注后输入:github 会有我的微信号,如果有问题你也可以在那找到我;当然不感兴趣无视此信息;

(20160602)开源第三方学习之SDWebImage的更多相关文章

  1. (20160604)开源第三方学习之CocoaLumberjack

    CocoaLumberjack是一个很好用的日志打印工具,它可以帮助我们把工程中的日志信息打印到终端或者输出到文件中. 地址:https://github.com/CocoaLumberjack/Co ...

  2. (20160601)开源第三方学习之SVProgressHUD

    SVProgressHUD相信在很多项目中都有运用,运用于弹出窗提示效果: 地址:https://github.com/SVProgressHUD/SVProgressHUD 一:插件的运用 1.1 ...

  3. (20170207)开源第三方学习之JSONModel

    1:仓库地址:https://github.com/jsonmodel/jsonmodel   主要作用是把JSON字符串转成Model实体,也可以把实体转化成JSON字符串:还包含一些转字典的内容: ...

  4. 开源深度学习架构Caffe

    Caffe 全称为 Convolutional Architecture for Fast Feature Embedding,是一个被广泛使用的开源深度学习框架(在 TensorFlow 出现之前一 ...

  5. GitHub 上 57 款最流行的开源深度学习项目

    转载:https://www.oschina.net/news/79500/57-most-popular-deep-learning-project-at-github GitHub 上 57 款最 ...

  6. 转:从开源项目学习 C 语言基本的编码规则

    从开源项目学习 C 语言基本的编码规则 每个项目都有自己的风格指南:一组有关怎样为那个项目编码约定.一些经理选择基本的编码规则,另一些经理则更偏好非常高级的规则,对许多项目而言则没有特定的编码规则,项 ...

  7. 推荐GitHub上10 个开源深度学习框架

    推荐GitHub上10 个开源深度学习框架   日前,Google 开源了 TensorFlow(GitHub),此举在深度学习领域影响巨大,因为 Google 在人工智能领域的研发成绩斐然,有着雄厚 ...

  8. Computational Network Toolkit (CNTK) 是微软出品的开源深度学习工具包

    Computational Network Toolkit (CNTK) 是微软出品的开源深度学习工具包 用 CNTK 搞深度学习 (一) 入门 Computational Network Toolk ...

  9. 谷歌重磅开源强化学习框架Dopamine吊打OpenAI

    谷歌重磅开源强化学习框架Dopamine吊打OpenAI 近日OpenAI在Dota 2上的表现,让强化学习又火了一把,但是 OpenAI 的强化学习训练环境 OpenAI Gym 却屡遭抱怨,比如不 ...

随机推荐

  1. Elasticsearch聚合 之 Ip Range IP地址范围聚合

    相对于range和date range,这个聚合就是能够表示IP的范围. 普通IP模式 DSL命令: { "aggs":{ "ip_ranges":{ &quo ...

  2. SQL Server代理(6/12):作业里的工作流——深入作业步骤

    SQL Server代理是所有实时数据库的核心.代理有很多不明显的用法,因此系统的知识,对于开发人员还是DBA都是有用的.这系列文章会通俗介绍它的很多用法. 如我们在这里系列的前几篇文章所见,SQL ...

  3. 利用getBoundingClientRect方法实现简洁的sticky组件

    补充于2016-03-20: 本文实现有不足,不完美的地方,请在了解本文相关内容后,移步阅读<sticky组件的改进实现>了解更佳的实现. sticky组件,通常应用于导航条或者工具栏,当 ...

  4. Auto Mapper02《demo》

         学习这些基本上网上都有一些教程或者别人做的demo,我是按照这个方式去学习的.先做个demo,学会如何去使用它,接着去慢慢的了解它是如何的运行的,理解里面的一些基本的基础知识.我们不可以再像 ...

  5. DirectShow .Net 实现视频

    DirectShow .Net 实现视频 .获取视频采集设备IBaseFilter接口对象的方法 //获取所有视频设备名称 public ArrayList GetVideoInputDevice() ...

  6. 只用CSS实现容器内图片上下左右居中

    一直以来,大家都知道,DIV容器内设置 text-align:center 即可让图片居中,但是DIV内默认的图片是上对齐,不会上下居中,如果想要实现这样的效果,JS判断是比较麻烦的,因为DIV容器内 ...

  7. jquery属性选择器(匹配具有指定属性的元素)

    <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/ ...

  8. 改变 TMemo 的背景颜色 (Firemonkey)

    说明:展示使用程序码改变 Firemonkey TMemo 的背景颜色. 适用:XE6 源码下载:[原創]Memo改背景色_XE6.zip //---------------------------- ...

  9. JVM堆和栈的区别

    物理地址 堆的物理地址分配对对象是不连续的.因此性能慢些.在GC的时候也要考虑到不连续的分配,所以有各种算法.比如,标记-消除,复制,标记-压缩,分代(即新生代使用复制算法,老年代使用标记--压缩) ...

  10. nodejs中全局变量

    1.global 类似于客户端javascript运行环境中的window module1.js: module.exports={}; //耻辱的使用了全局变量 global.varA = &quo ...