First beta of SDWebImage 3.0

This version is a rewrite of the lib using iOS 5.0 as minimum target.
The lib as thus been refactored to use blocks/GCD from the ground up.
As a consequence, all delegate code style has been removed. The result
is a lot less lines of code and more flexibility.
This commit is contained in:
Olivier Poitrey 2012-11-04 09:50:23 +01:00
parent 8963f996e9
commit 2227266c41
25 changed files with 1055 additions and 1888 deletions

View File

@ -1,3 +1,4 @@
Web Image
=========
@ -8,9 +9,15 @@ It provides:
- An UIImageView category adding web image and cache management to the Cocoa Touch framework
- An asynchronous image downloader
- An asynchronous memory + disk image caching with automatic cache expiration handling
- A background image decompression
- A guarantee that the same URL won't be downloaded several times
- A guarantee that bogus URLs won't be retried again and again
- A guarantee that main thread will never be blocked
- Performances!
- Use GCD and ARC
NOTE: The version 3.0 of SDWebImage isn't fully backward compatible with 1.0 and requires iOS 5.0
minimum. If you need iOS < 5.0 support, please use the last 2.0 version.
Motivation
----------
@ -94,15 +101,14 @@ handled for you, from async downloads to caching management.
### Using blocks
If your project's deployement target is set to iOS 4+, you may want to use the success/failure blocks to be
notified when image have been retrieved from cache.
With blocks, you can be notified about the image download progress and whenever the image retrival
has completed with success or not:
```objective-c
// Here we use the new provided setImageWithURL: method to load the web image
[cell.imageView setImageWithURL:[NSURL URLWithString:@"http://www.domain.com/path/to/image.jpg"]
placeholderImage:[UIImage imageNamed:@"placeholder.png"]
success:^(UIImage *image, BOOL cached) {... success code here ...}
failure:^(NSError *error) {... failure code here ...}];
];
completed:^(UIImage *image, NSError *error, BOOL fromCache) {... completion code here ...}];
```
Note: neither your success nor failure block will be call if your image request is canceled before completion.
@ -118,29 +124,43 @@ Here is a simple example of how to use SDWebImageManager:
```objective-c
SDWebImageManager *manager = [SDWebImageManager sharedManager];
[manager downloadWithURL:imageURL
delegate:self
options:0
success:^(UIImage *image, BOOL cached)
progress:^(NSUInteger receivedSize, long long expectedSize)
{
// do something with image
// progression tracking code
}
failure:nil];
completed:^(UIImage *image, NSError *error, BOOL fromCache)
{
if (image)
{
// do something with image
}
}];
```
### Using Asynchronous Image Downloader Independently
It is possible to use the async image downloader independently. You just have to create an instance
of SDWebImageDownloader using its convenience constructor downloaderWithURL:delegate:.
```objective-c
downloader = [SDWebImageDownloader downloaderWithURL:url delegate:self];
```
It's also possible to use the async image downloader independently:
The download will start immediately and the imageDownloader:didFinishWithImage: method from the
SDWebImageDownloaderDelegate protocol will be called as soon as the download of image is completed.
```objective-c
[SDWebImageDownloader.sharedDownloader downloadImageWithURL:imageURL
options:0
progress:^(NSUInteger receivedSize, long long expectedSize)
{
// progression tracking code
}
completed:^(UIImage *image, NSError *error, BOOL finished)
{
if (image && finished)
{
// do something with image
}
}];
```
### Using Asynchronous Image Caching Independently
It is also possible to use the NSOperation based image cache store independently. SDImageCache
It is also possible to use the aync based image cache store independently. SDImageCache
maintains a memory cache and an optional disk cache. Disk cache write operations are performed
asynchronous so it doesn't add unnecessary latency to the UI.
@ -153,7 +173,11 @@ key is an application unique identifier for the image to cache. It is generally
the image.
```objective-c
UIImage *myCachedImage = [[SDImageCache sharedImageCache] imageFromKey:myCacheKey];
SDImageCache *imageCache = [SDImageCache.alloc initWithNamespace:@"myNamespace"];
[imageCache queryDiskCacheForKey:myCacheKey done:^(UIImage *image)
{
// image is not nil if image was found
}];
```
By default SDImageCache will lookup the disk cache if an image can't be found in the memory cache.
@ -182,11 +206,11 @@ the URL before to use it as a cache key:
```objective-c
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
{
[[SDWebImageManager sharedManager] setCacheKeyFilter:^(NSURL *url)
SDWebImageManager.sharedManager.cacheKeyFilter:^(NSURL *url)
{
url = [[[NSURL alloc] initWithScheme:url.scheme host:url.host path:url.path] autorelease];
return [url absoluteString];
}];
};
// Your app init code...
return YES;
@ -205,12 +229,6 @@ The following article gives a way to workaround this issue:
[http://www.wrichards.com/blog/2011/11/sdwebimage-fixed-width-cell-images/](http://www.wrichards.com/blog/2011/11/sdwebimage-fixed-width-cell-images/)
Automatic Reference Counting (ARC)
----------------------------------
You can use either style in your Cocoa project. SDWebImage Will figure out which you are using at compile
time and do the right thing.
Installation
------------

View File

@ -22,6 +22,12 @@
/* End PBXAggregateTarget section */
/* Begin PBXBuildFile section */
530E49E816464C25002868E7 /* SDWebImageOperation.h in Headers */ = {isa = PBXBuildFile; fileRef = 530E49E71646388E002868E7 /* SDWebImageOperation.h */; settings = {ATTRIBUTES = (Public, ); }; };
530E49E916464C26002868E7 /* SDWebImageOperation.h in Headers */ = {isa = PBXBuildFile; fileRef = 530E49E71646388E002868E7 /* SDWebImageOperation.h */; settings = {ATTRIBUTES = (Public, ); }; };
530E49EA16464C7C002868E7 /* SDWebImageDownloaderOperation.h in Headers */ = {isa = PBXBuildFile; fileRef = 530E49E316460AE2002868E7 /* SDWebImageDownloaderOperation.h */; settings = {ATTRIBUTES = (Public, ); }; };
530E49EB16464C7F002868E7 /* SDWebImageDownloaderOperation.h in Headers */ = {isa = PBXBuildFile; fileRef = 530E49E316460AE2002868E7 /* SDWebImageDownloaderOperation.h */; settings = {ATTRIBUTES = (Public, ); }; };
530E49EC16464C84002868E7 /* SDWebImageDownloaderOperation.m in Sources */ = {isa = PBXBuildFile; fileRef = 530E49E416460AE2002868E7 /* SDWebImageDownloaderOperation.m */; };
530E49ED16464C84002868E7 /* SDWebImageDownloaderOperation.m in Sources */ = {isa = PBXBuildFile; fileRef = 530E49E416460AE2002868E7 /* SDWebImageDownloaderOperation.m */; };
531041C4157EAFA400BBABC3 /* SDImageCache.m in Sources */ = {isa = PBXBuildFile; fileRef = 53922D86148C56230056699D /* SDImageCache.m */; };
531041C5157EAFA400BBABC3 /* SDWebImageDecoder.m in Sources */ = {isa = PBXBuildFile; fileRef = 53922D8A148C56230056699D /* SDWebImageDecoder.m */; };
531041C6157EAFA400BBABC3 /* SDWebImageDownloader.m in Sources */ = {isa = PBXBuildFile; fileRef = 53922D8C148C56230056699D /* SDWebImageDownloader.m */; };
@ -34,13 +40,10 @@
531041CE157EAFA400BBABC3 /* Foundation.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 53922D72148C55820056699D /* Foundation.framework */; };
531041CF157EAFA400BBABC3 /* CoreGraphics.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 53FB893F14D35D1A0020B787 /* CoreGraphics.framework */; };
531041D1157EAFA400BBABC3 /* SDImageCache.h in Headers */ = {isa = PBXBuildFile; fileRef = 53922D85148C56230056699D /* SDImageCache.h */; settings = {ATTRIBUTES = (Public, ); }; };
531041D2157EAFA400BBABC3 /* SDImageCacheDelegate.h in Headers */ = {isa = PBXBuildFile; fileRef = 53922D87148C56230056699D /* SDImageCacheDelegate.h */; settings = {ATTRIBUTES = (Public, ); }; };
531041D3157EAFA400BBABC3 /* SDWebImageCompat.h in Headers */ = {isa = PBXBuildFile; fileRef = 53922D88148C56230056699D /* SDWebImageCompat.h */; settings = {ATTRIBUTES = (Public, ); }; };
531041D4157EAFA400BBABC3 /* SDWebImageDecoder.h in Headers */ = {isa = PBXBuildFile; fileRef = 53922D89148C56230056699D /* SDWebImageDecoder.h */; settings = {ATTRIBUTES = (Public, ); }; };
531041D5157EAFA400BBABC3 /* SDWebImageDownloader.h in Headers */ = {isa = PBXBuildFile; fileRef = 53922D8B148C56230056699D /* SDWebImageDownloader.h */; settings = {ATTRIBUTES = (Public, ); }; };
531041D6157EAFA400BBABC3 /* SDWebImageDownloaderDelegate.h in Headers */ = {isa = PBXBuildFile; fileRef = 53922D8D148C56230056699D /* SDWebImageDownloaderDelegate.h */; settings = {ATTRIBUTES = (Public, ); }; };
531041D7157EAFA400BBABC3 /* SDWebImageManager.h in Headers */ = {isa = PBXBuildFile; fileRef = 53922D8E148C56230056699D /* SDWebImageManager.h */; settings = {ATTRIBUTES = (Public, ); }; };
531041D8157EAFA400BBABC3 /* SDWebImageManagerDelegate.h in Headers */ = {isa = PBXBuildFile; fileRef = 53922D90148C56230056699D /* SDWebImageManagerDelegate.h */; settings = {ATTRIBUTES = (Public, ); }; };
531041D9157EAFA400BBABC3 /* SDWebImagePrefetcher.h in Headers */ = {isa = PBXBuildFile; fileRef = 53922D91148C56230056699D /* SDWebImagePrefetcher.h */; settings = {ATTRIBUTES = (Public, ); }; };
531041DA157EAFA400BBABC3 /* UIButton+WebCache.h in Headers */ = {isa = PBXBuildFile; fileRef = 53922D93148C56230056699D /* UIButton+WebCache.h */; settings = {ATTRIBUTES = (Public, ); }; };
531041DB157EAFA400BBABC3 /* UIImageView+WebCache.h in Headers */ = {isa = PBXBuildFile; fileRef = 53922D95148C56230056699D /* UIImageView+WebCache.h */; settings = {ATTRIBUTES = (Public, ); }; };
@ -56,37 +59,13 @@
53761313155AD0D5005750A4 /* Foundation.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 53922D72148C55820056699D /* Foundation.framework */; };
53761314155AD0D5005750A4 /* CoreGraphics.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 53FB893F14D35D1A0020B787 /* CoreGraphics.framework */; };
53761316155AD0D5005750A4 /* SDImageCache.h in Headers */ = {isa = PBXBuildFile; fileRef = 53922D85148C56230056699D /* SDImageCache.h */; settings = {ATTRIBUTES = (Public, ); }; };
53761317155AD0D5005750A4 /* SDImageCacheDelegate.h in Headers */ = {isa = PBXBuildFile; fileRef = 53922D87148C56230056699D /* SDImageCacheDelegate.h */; settings = {ATTRIBUTES = (Public, ); }; };
53761318155AD0D5005750A4 /* SDWebImageCompat.h in Headers */ = {isa = PBXBuildFile; fileRef = 53922D88148C56230056699D /* SDWebImageCompat.h */; settings = {ATTRIBUTES = (Public, ); }; };
53761319155AD0D5005750A4 /* SDWebImageDecoder.h in Headers */ = {isa = PBXBuildFile; fileRef = 53922D89148C56230056699D /* SDWebImageDecoder.h */; settings = {ATTRIBUTES = (Public, ); }; };
5376131A155AD0D5005750A4 /* SDWebImageDownloader.h in Headers */ = {isa = PBXBuildFile; fileRef = 53922D8B148C56230056699D /* SDWebImageDownloader.h */; settings = {ATTRIBUTES = (Public, ); }; };
5376131B155AD0D5005750A4 /* SDWebImageDownloaderDelegate.h in Headers */ = {isa = PBXBuildFile; fileRef = 53922D8D148C56230056699D /* SDWebImageDownloaderDelegate.h */; settings = {ATTRIBUTES = (Public, ); }; };
5376131C155AD0D5005750A4 /* SDWebImageManager.h in Headers */ = {isa = PBXBuildFile; fileRef = 53922D8E148C56230056699D /* SDWebImageManager.h */; settings = {ATTRIBUTES = (Public, ); }; };
5376131D155AD0D5005750A4 /* SDWebImageManagerDelegate.h in Headers */ = {isa = PBXBuildFile; fileRef = 53922D90148C56230056699D /* SDWebImageManagerDelegate.h */; settings = {ATTRIBUTES = (Public, ); }; };
5376131E155AD0D5005750A4 /* SDWebImagePrefetcher.h in Headers */ = {isa = PBXBuildFile; fileRef = 53922D91148C56230056699D /* SDWebImagePrefetcher.h */; settings = {ATTRIBUTES = (Public, ); }; };
5376131F155AD0D5005750A4 /* UIButton+WebCache.h in Headers */ = {isa = PBXBuildFile; fileRef = 53922D93148C56230056699D /* UIButton+WebCache.h */; settings = {ATTRIBUTES = (Public, ); }; };
53761320155AD0D5005750A4 /* UIImageView+WebCache.h in Headers */ = {isa = PBXBuildFile; fileRef = 53922D95148C56230056699D /* UIImageView+WebCache.h */; settings = {ATTRIBUTES = (Public, ); }; };
53922D73148C55820056699D /* Foundation.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 53922D72148C55820056699D /* Foundation.framework */; };
53922D97148C56230056699D /* SDImageCache.h in Headers */ = {isa = PBXBuildFile; fileRef = 53922D85148C56230056699D /* SDImageCache.h */; settings = {ATTRIBUTES = (Public, ); }; };
53922D98148C56230056699D /* SDImageCache.m in Sources */ = {isa = PBXBuildFile; fileRef = 53922D86148C56230056699D /* SDImageCache.m */; };
53922D99148C56230056699D /* SDImageCacheDelegate.h in Headers */ = {isa = PBXBuildFile; fileRef = 53922D87148C56230056699D /* SDImageCacheDelegate.h */; settings = {ATTRIBUTES = (Public, ); }; };
53922D9A148C56230056699D /* SDWebImageCompat.h in Headers */ = {isa = PBXBuildFile; fileRef = 53922D88148C56230056699D /* SDWebImageCompat.h */; settings = {ATTRIBUTES = (Public, ); }; };
53922D9B148C56230056699D /* SDWebImageDecoder.h in Headers */ = {isa = PBXBuildFile; fileRef = 53922D89148C56230056699D /* SDWebImageDecoder.h */; settings = {ATTRIBUTES = (Public, ); }; };
53922D9C148C56230056699D /* SDWebImageDecoder.m in Sources */ = {isa = PBXBuildFile; fileRef = 53922D8A148C56230056699D /* SDWebImageDecoder.m */; };
53922D9D148C56230056699D /* SDWebImageDownloader.h in Headers */ = {isa = PBXBuildFile; fileRef = 53922D8B148C56230056699D /* SDWebImageDownloader.h */; settings = {ATTRIBUTES = (Public, ); }; };
53922D9E148C56230056699D /* SDWebImageDownloader.m in Sources */ = {isa = PBXBuildFile; fileRef = 53922D8C148C56230056699D /* SDWebImageDownloader.m */; };
53922D9F148C56230056699D /* SDWebImageDownloaderDelegate.h in Headers */ = {isa = PBXBuildFile; fileRef = 53922D8D148C56230056699D /* SDWebImageDownloaderDelegate.h */; settings = {ATTRIBUTES = (Public, ); }; };
53922DA0148C56230056699D /* SDWebImageManager.h in Headers */ = {isa = PBXBuildFile; fileRef = 53922D8E148C56230056699D /* SDWebImageManager.h */; settings = {ATTRIBUTES = (Public, ); }; };
53922DA1148C56230056699D /* SDWebImageManager.m in Sources */ = {isa = PBXBuildFile; fileRef = 53922D8F148C56230056699D /* SDWebImageManager.m */; };
53922DA2148C56230056699D /* SDWebImageManagerDelegate.h in Headers */ = {isa = PBXBuildFile; fileRef = 53922D90148C56230056699D /* SDWebImageManagerDelegate.h */; settings = {ATTRIBUTES = (Public, ); }; };
53922DA3148C56230056699D /* SDWebImagePrefetcher.h in Headers */ = {isa = PBXBuildFile; fileRef = 53922D91148C56230056699D /* SDWebImagePrefetcher.h */; settings = {ATTRIBUTES = (Public, ); }; };
53922DA4148C56230056699D /* SDWebImagePrefetcher.m in Sources */ = {isa = PBXBuildFile; fileRef = 53922D92148C56230056699D /* SDWebImagePrefetcher.m */; };
53922DA5148C56230056699D /* UIButton+WebCache.h in Headers */ = {isa = PBXBuildFile; fileRef = 53922D93148C56230056699D /* UIButton+WebCache.h */; settings = {ATTRIBUTES = (Public, ); }; };
53922DA6148C56230056699D /* UIButton+WebCache.m in Sources */ = {isa = PBXBuildFile; fileRef = 53922D94148C56230056699D /* UIButton+WebCache.m */; };
53922DA7148C56230056699D /* UIImageView+WebCache.h in Headers */ = {isa = PBXBuildFile; fileRef = 53922D95148C56230056699D /* UIImageView+WebCache.h */; settings = {ATTRIBUTES = (Public, ); }; };
53922DA8148C56230056699D /* UIImageView+WebCache.m in Sources */ = {isa = PBXBuildFile; fileRef = 53922D96148C56230056699D /* UIImageView+WebCache.m */; };
53FB894714D35E820020B787 /* CoreGraphics.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 53FB893F14D35D1A0020B787 /* CoreGraphics.framework */; };
53FB894914D35E9E0020B787 /* UIKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 53FB894814D35E9E0020B787 /* UIKit.framework */; };
/* End PBXBuildFile section */
/* Begin PBXContainerItemProxy section */
@ -100,24 +79,23 @@
/* End PBXContainerItemProxy section */
/* Begin PBXFileReference section */
531041E0157EAFA400BBABC3 /* libSDWebImageARC+MKAnnotation.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = "libSDWebImageARC+MKAnnotation.a"; sourceTree = BUILT_PRODUCTS_DIR; };
530E49E316460AE2002868E7 /* SDWebImageDownloaderOperation.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SDWebImageDownloaderOperation.h; sourceTree = "<group>"; };
530E49E416460AE2002868E7 /* SDWebImageDownloaderOperation.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = SDWebImageDownloaderOperation.m; sourceTree = "<group>"; };
530E49E71646388E002868E7 /* SDWebImageOperation.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = SDWebImageOperation.h; sourceTree = "<group>"; };
531041E0157EAFA400BBABC3 /* libSDWebImage+MKAnnotation.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = "libSDWebImage+MKAnnotation.a"; sourceTree = BUILT_PRODUCTS_DIR; };
535699B415113E7300A4C397 /* MKAnnotationView+WebCache.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = "MKAnnotationView+WebCache.h"; path = "SDWebImage/MKAnnotationView+WebCache.h"; sourceTree = SOURCE_ROOT; };
535699B515113E7300A4C397 /* MKAnnotationView+WebCache.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = "MKAnnotationView+WebCache.m"; path = "SDWebImage/MKAnnotationView+WebCache.m"; sourceTree = SOURCE_ROOT; };
53761325155AD0D5005750A4 /* libSDWebImage.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = libSDWebImage.a; sourceTree = BUILT_PRODUCTS_DIR; };
53922D6F148C55820056699D /* libSDWebImage.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = libSDWebImage.a; sourceTree = BUILT_PRODUCTS_DIR; };
53922D72148C55820056699D /* Foundation.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Foundation.framework; path = System/Library/Frameworks/Foundation.framework; sourceTree = SDKROOT; };
53922D85148C56230056699D /* SDImageCache.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = SDImageCache.h; path = SDWebImage/SDImageCache.h; sourceTree = SOURCE_ROOT; };
53922D86148C56230056699D /* SDImageCache.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = SDImageCache.m; path = SDWebImage/SDImageCache.m; sourceTree = SOURCE_ROOT; };
53922D87148C56230056699D /* SDImageCacheDelegate.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = SDImageCacheDelegate.h; path = SDWebImage/SDImageCacheDelegate.h; sourceTree = SOURCE_ROOT; };
53922D88148C56230056699D /* SDWebImageCompat.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = SDWebImageCompat.h; path = SDWebImage/SDWebImageCompat.h; sourceTree = SOURCE_ROOT; };
53922D89148C56230056699D /* SDWebImageDecoder.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = SDWebImageDecoder.h; path = SDWebImage/SDWebImageDecoder.h; sourceTree = SOURCE_ROOT; };
53922D8A148C56230056699D /* SDWebImageDecoder.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = SDWebImageDecoder.m; path = SDWebImage/SDWebImageDecoder.m; sourceTree = SOURCE_ROOT; };
53922D8B148C56230056699D /* SDWebImageDownloader.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = SDWebImageDownloader.h; path = SDWebImage/SDWebImageDownloader.h; sourceTree = SOURCE_ROOT; };
53922D8C148C56230056699D /* SDWebImageDownloader.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = SDWebImageDownloader.m; path = SDWebImage/SDWebImageDownloader.m; sourceTree = SOURCE_ROOT; };
53922D8D148C56230056699D /* SDWebImageDownloaderDelegate.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = SDWebImageDownloaderDelegate.h; path = SDWebImage/SDWebImageDownloaderDelegate.h; sourceTree = SOURCE_ROOT; };
53922D8E148C56230056699D /* SDWebImageManager.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = SDWebImageManager.h; path = SDWebImage/SDWebImageManager.h; sourceTree = SOURCE_ROOT; };
53922D8F148C56230056699D /* SDWebImageManager.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = SDWebImageManager.m; path = SDWebImage/SDWebImageManager.m; sourceTree = SOURCE_ROOT; };
53922D90148C56230056699D /* SDWebImageManagerDelegate.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = SDWebImageManagerDelegate.h; path = SDWebImage/SDWebImageManagerDelegate.h; sourceTree = SOURCE_ROOT; };
53922D91148C56230056699D /* SDWebImagePrefetcher.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = SDWebImagePrefetcher.h; path = SDWebImage/SDWebImagePrefetcher.h; sourceTree = SOURCE_ROOT; };
53922D92148C56230056699D /* SDWebImagePrefetcher.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = SDWebImagePrefetcher.m; path = SDWebImage/SDWebImagePrefetcher.m; sourceTree = SOURCE_ROOT; };
53922D93148C56230056699D /* UIButton+WebCache.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = "UIButton+WebCache.h"; path = "SDWebImage/UIButton+WebCache.h"; sourceTree = SOURCE_ROOT; };
@ -149,16 +127,6 @@
);
runOnlyForDeploymentPostprocessing = 0;
};
53922D6C148C55810056699D /* Frameworks */ = {
isa = PBXFrameworksBuildPhase;
buildActionMask = 2147483647;
files = (
53FB894914D35E9E0020B787 /* UIKit.framework in Frameworks */,
53922D73148C55820056699D /* Foundation.framework in Frameworks */,
53FB894714D35E820020B787 /* CoreGraphics.framework in Frameworks */,
);
runOnlyForDeploymentPostprocessing = 0;
};
/* End PBXFrameworksBuildPhase section */
/* Begin PBXGroup section */
@ -174,9 +142,8 @@
53922D70148C55820056699D /* Products */ = {
isa = PBXGroup;
children = (
53922D6F148C55820056699D /* libSDWebImage.a */,
53761325155AD0D5005750A4 /* libSDWebImage.a */,
531041E0157EAFA400BBABC3 /* libSDWebImageARC+MKAnnotation.a */,
531041E0157EAFA400BBABC3 /* libSDWebImage+MKAnnotation.a */,
);
name = Products;
sourceTree = "<group>";
@ -195,6 +162,7 @@
isa = PBXGroup;
children = (
53922D88148C56230056699D /* SDWebImageCompat.h */,
530E49E71646388E002868E7 /* SDWebImageOperation.h */,
53922DAB148C56810056699D /* Downloader */,
53922DAA148C56470056699D /* Cache */,
53922DAC148C56DD0056699D /* Utils */,
@ -221,7 +189,6 @@
children = (
53922D85148C56230056699D /* SDImageCache.h */,
53922D86148C56230056699D /* SDImageCache.m */,
53922D87148C56230056699D /* SDImageCacheDelegate.h */,
);
name = Cache;
sourceTree = "<group>";
@ -231,7 +198,8 @@
children = (
53922D8B148C56230056699D /* SDWebImageDownloader.h */,
53922D8C148C56230056699D /* SDWebImageDownloader.m */,
53922D8D148C56230056699D /* SDWebImageDownloaderDelegate.h */,
530E49E316460AE2002868E7 /* SDWebImageDownloaderOperation.h */,
530E49E416460AE2002868E7 /* SDWebImageDownloaderOperation.m */,
);
name = Downloader;
sourceTree = "<group>";
@ -241,7 +209,6 @@
children = (
53922D8E148C56230056699D /* SDWebImageManager.h */,
53922D8F148C56230056699D /* SDWebImageManager.m */,
53922D90148C56230056699D /* SDWebImageManagerDelegate.h */,
53922D89148C56230056699D /* SDWebImageDecoder.h */,
53922D8A148C56230056699D /* SDWebImageDecoder.m */,
53922D91148C56230056699D /* SDWebImagePrefetcher.h */,
@ -258,17 +225,16 @@
buildActionMask = 2147483647;
files = (
531041D1157EAFA400BBABC3 /* SDImageCache.h in Headers */,
531041D2157EAFA400BBABC3 /* SDImageCacheDelegate.h in Headers */,
531041D3157EAFA400BBABC3 /* SDWebImageCompat.h in Headers */,
531041D4157EAFA400BBABC3 /* SDWebImageDecoder.h in Headers */,
531041D5157EAFA400BBABC3 /* SDWebImageDownloader.h in Headers */,
531041D6157EAFA400BBABC3 /* SDWebImageDownloaderDelegate.h in Headers */,
531041D7157EAFA400BBABC3 /* SDWebImageManager.h in Headers */,
531041D8157EAFA400BBABC3 /* SDWebImageManagerDelegate.h in Headers */,
531041D9157EAFA400BBABC3 /* SDWebImagePrefetcher.h in Headers */,
531041DA157EAFA400BBABC3 /* UIButton+WebCache.h in Headers */,
531041DB157EAFA400BBABC3 /* UIImageView+WebCache.h in Headers */,
531041DC157EAFA400BBABC3 /* MKAnnotationView+WebCache.h in Headers */,
530E49E916464C26002868E7 /* SDWebImageOperation.h in Headers */,
530E49EB16464C7F002868E7 /* SDWebImageDownloaderOperation.h in Headers */,
);
runOnlyForDeploymentPostprocessing = 0;
};
@ -277,43 +243,24 @@
buildActionMask = 2147483647;
files = (
53761316155AD0D5005750A4 /* SDImageCache.h in Headers */,
53761317155AD0D5005750A4 /* SDImageCacheDelegate.h in Headers */,
53761318155AD0D5005750A4 /* SDWebImageCompat.h in Headers */,
53761319155AD0D5005750A4 /* SDWebImageDecoder.h in Headers */,
5376131A155AD0D5005750A4 /* SDWebImageDownloader.h in Headers */,
5376131B155AD0D5005750A4 /* SDWebImageDownloaderDelegate.h in Headers */,
5376131C155AD0D5005750A4 /* SDWebImageManager.h in Headers */,
5376131D155AD0D5005750A4 /* SDWebImageManagerDelegate.h in Headers */,
5376131E155AD0D5005750A4 /* SDWebImagePrefetcher.h in Headers */,
5376131F155AD0D5005750A4 /* UIButton+WebCache.h in Headers */,
53761320155AD0D5005750A4 /* UIImageView+WebCache.h in Headers */,
);
runOnlyForDeploymentPostprocessing = 0;
};
53922D6D148C55810056699D /* Headers */ = {
isa = PBXHeadersBuildPhase;
buildActionMask = 2147483647;
files = (
53922D97148C56230056699D /* SDImageCache.h in Headers */,
53922D99148C56230056699D /* SDImageCacheDelegate.h in Headers */,
53922D9A148C56230056699D /* SDWebImageCompat.h in Headers */,
53922D9B148C56230056699D /* SDWebImageDecoder.h in Headers */,
53922D9D148C56230056699D /* SDWebImageDownloader.h in Headers */,
53922D9F148C56230056699D /* SDWebImageDownloaderDelegate.h in Headers */,
53922DA0148C56230056699D /* SDWebImageManager.h in Headers */,
53922DA2148C56230056699D /* SDWebImageManagerDelegate.h in Headers */,
53922DA3148C56230056699D /* SDWebImagePrefetcher.h in Headers */,
53922DA5148C56230056699D /* UIButton+WebCache.h in Headers */,
53922DA7148C56230056699D /* UIImageView+WebCache.h in Headers */,
530E49E816464C25002868E7 /* SDWebImageOperation.h in Headers */,
530E49EA16464C7C002868E7 /* SDWebImageDownloaderOperation.h in Headers */,
);
runOnlyForDeploymentPostprocessing = 0;
};
/* End PBXHeadersBuildPhase section */
/* Begin PBXNativeTarget section */
531041C2157EAFA400BBABC3 /* SDWebImage ARC+MKAnnotation */ = {
531041C2157EAFA400BBABC3 /* SDWebImage+MKAnnotation */ = {
isa = PBXNativeTarget;
buildConfigurationList = 531041DD157EAFA400BBABC3 /* Build configuration list for PBXNativeTarget "SDWebImage ARC+MKAnnotation" */;
buildConfigurationList = 531041DD157EAFA400BBABC3 /* Build configuration list for PBXNativeTarget "SDWebImage+MKAnnotation" */;
buildPhases = (
531041C3157EAFA400BBABC3 /* Sources */,
531041CC157EAFA400BBABC3 /* Frameworks */,
@ -323,14 +270,14 @@
);
dependencies = (
);
name = "SDWebImage ARC+MKAnnotation";
name = "SDWebImage+MKAnnotation";
productName = SDWebImage;
productReference = 531041E0157EAFA400BBABC3 /* libSDWebImageARC+MKAnnotation.a */;
productReference = 531041E0157EAFA400BBABC3 /* libSDWebImage+MKAnnotation.a */;
productType = "com.apple.product-type.library.static";
};
53761307155AD0D5005750A4 /* SDWebImage ARC */ = {
53761307155AD0D5005750A4 /* SDWebImage */ = {
isa = PBXNativeTarget;
buildConfigurationList = 53761322155AD0D5005750A4 /* Build configuration list for PBXNativeTarget "SDWebImage ARC" */;
buildConfigurationList = 53761322155AD0D5005750A4 /* Build configuration list for PBXNativeTarget "SDWebImage" */;
buildPhases = (
53761308155AD0D5005750A4 /* Sources */,
53761311155AD0D5005750A4 /* Frameworks */,
@ -341,26 +288,9 @@
);
dependencies = (
);
name = "SDWebImage ARC";
productName = SDWebImage;
productReference = 53761325155AD0D5005750A4 /* libSDWebImage.a */;
productType = "com.apple.product-type.library.static";
};
53922D6E148C55810056699D /* SDWebImage */ = {
isa = PBXNativeTarget;
buildConfigurationList = 53922D7C148C55820056699D /* Build configuration list for PBXNativeTarget "SDWebImage" */;
buildPhases = (
53922D6B148C55810056699D /* Sources */,
53922D6C148C55810056699D /* Frameworks */,
53922D6D148C55810056699D /* Headers */,
);
buildRules = (
);
dependencies = (
);
name = SDWebImage;
productName = SDWebImage;
productReference = 53922D6F148C55820056699D /* libSDWebImage.a */;
productReference = 53761325155AD0D5005750A4 /* libSDWebImage.a */;
productType = "com.apple.product-type.library.static";
};
/* End PBXNativeTarget section */
@ -384,9 +314,8 @@
projectDirPath = "";
projectRoot = "";
targets = (
53922D6E148C55810056699D /* SDWebImage */,
53761307155AD0D5005750A4 /* SDWebImage ARC */,
531041C2157EAFA400BBABC3 /* SDWebImage ARC+MKAnnotation */,
53761307155AD0D5005750A4 /* SDWebImage */,
531041C2157EAFA400BBABC3 /* SDWebImage+MKAnnotation */,
539F912B16316D2D00160719 /* SDWebImageFramework */,
);
};
@ -436,6 +365,7 @@
531041C9157EAFA400BBABC3 /* UIButton+WebCache.m in Sources */,
531041CA157EAFA400BBABC3 /* UIImageView+WebCache.m in Sources */,
531041CB157EAFA400BBABC3 /* MKAnnotationView+WebCache.m in Sources */,
530E49ED16464C84002868E7 /* SDWebImageDownloaderOperation.m in Sources */,
);
runOnlyForDeploymentPostprocessing = 0;
};
@ -450,20 +380,7 @@
5376130D155AD0D5005750A4 /* SDWebImagePrefetcher.m in Sources */,
5376130E155AD0D5005750A4 /* UIButton+WebCache.m in Sources */,
5376130F155AD0D5005750A4 /* UIImageView+WebCache.m in Sources */,
);
runOnlyForDeploymentPostprocessing = 0;
};
53922D6B148C55810056699D /* Sources */ = {
isa = PBXSourcesBuildPhase;
buildActionMask = 2147483647;
files = (
53922D98148C56230056699D /* SDImageCache.m in Sources */,
53922D9C148C56230056699D /* SDWebImageDecoder.m in Sources */,
53922D9E148C56230056699D /* SDWebImageDownloader.m in Sources */,
53922DA1148C56230056699D /* SDWebImageManager.m in Sources */,
53922DA4148C56230056699D /* SDWebImagePrefetcher.m in Sources */,
53922DA6148C56230056699D /* UIButton+WebCache.m in Sources */,
53922DA8148C56230056699D /* UIImageView+WebCache.m in Sources */,
530E49EC16464C84002868E7 /* SDWebImageDownloaderOperation.m in Sources */,
);
runOnlyForDeploymentPostprocessing = 0;
};
@ -472,7 +389,7 @@
/* Begin PBXTargetDependency section */
539F913016316D3700160719 /* PBXTargetDependency */ = {
isa = PBXTargetDependency;
target = 53761307155AD0D5005750A4 /* SDWebImage ARC */;
target = 53761307155AD0D5005750A4 /* SDWebImage */;
targetProxy = 539F912F16316D3700160719 /* PBXContainerItemProxy */;
};
/* End PBXTargetDependency section */
@ -489,9 +406,9 @@
GCC_PRECOMPILE_PREFIX_HEADER = YES;
GCC_PREFIX_HEADER = "";
INSTALL_PATH = "$(BUILT_PRODUCTS_DIR)";
IPHONEOS_DEPLOYMENT_TARGET = 4.0;
IPHONEOS_DEPLOYMENT_TARGET = 5.0;
OTHER_LDFLAGS = "-ObjC";
PRODUCT_NAME = "SDWebImageARC+MKAnnotation";
PRODUCT_NAME = "SDWebImage+MKAnnotation";
SKIP_INSTALL = YES;
STRIP_STYLE = "non-global";
};
@ -508,9 +425,9 @@
GCC_PRECOMPILE_PREFIX_HEADER = YES;
GCC_PREFIX_HEADER = "";
INSTALL_PATH = "$(BUILT_PRODUCTS_DIR)";
IPHONEOS_DEPLOYMENT_TARGET = 4.0;
IPHONEOS_DEPLOYMENT_TARGET = 5.0;
OTHER_LDFLAGS = "-ObjC";
PRODUCT_NAME = "SDWebImageARC+MKAnnotation";
PRODUCT_NAME = "SDWebImage+MKAnnotation";
SKIP_INSTALL = YES;
STRIP_STYLE = "non-global";
};
@ -527,7 +444,7 @@
GCC_PRECOMPILE_PREFIX_HEADER = YES;
GCC_PREFIX_HEADER = "";
INSTALL_PATH = "$(BUILT_PRODUCTS_DIR)";
IPHONEOS_DEPLOYMENT_TARGET = 4.0;
IPHONEOS_DEPLOYMENT_TARGET = 5.0;
OTHER_LDFLAGS = "-ObjC";
PRODUCT_NAME = SDWebImage;
SKIP_INSTALL = YES;
@ -546,7 +463,7 @@
GCC_PRECOMPILE_PREFIX_HEADER = YES;
GCC_PREFIX_HEADER = "";
INSTALL_PATH = "$(BUILT_PRODUCTS_DIR)";
IPHONEOS_DEPLOYMENT_TARGET = 4.0;
IPHONEOS_DEPLOYMENT_TARGET = 5.0;
OTHER_LDFLAGS = "-ObjC";
PRODUCT_NAME = SDWebImage;
SKIP_INSTALL = YES;
@ -581,7 +498,7 @@
GCC_WARN_UNUSED_LABEL = YES;
GCC_WARN_UNUSED_PARAMETER = NO;
GCC_WARN_UNUSED_VARIABLE = YES;
IPHONEOS_DEPLOYMENT_TARGET = 4.3;
IPHONEOS_DEPLOYMENT_TARGET = 5.0;
PUBLIC_HEADERS_FOLDER_PATH = include/SDWebImage;
RUN_CLANG_STATIC_ANALYZER = YES;
SDKROOT = iphoneos;
@ -608,7 +525,7 @@
GCC_WARN_UNUSED_LABEL = YES;
GCC_WARN_UNUSED_PARAMETER = NO;
GCC_WARN_UNUSED_VARIABLE = YES;
IPHONEOS_DEPLOYMENT_TARGET = 4.3;
IPHONEOS_DEPLOYMENT_TARGET = 5.0;
PUBLIC_HEADERS_FOLDER_PATH = include/SDWebImage;
RUN_CLANG_STATIC_ANALYZER = YES;
SDKROOT = iphoneos;
@ -616,42 +533,6 @@
};
name = Release;
};
53922D7D148C55820056699D /* Debug */ = {
isa = XCBuildConfiguration;
buildSettings = {
CLANG_ENABLE_OBJC_ARC = NO;
CLANG_WARN_OBJCPP_ARC_ABI = NO;
COPY_PHASE_STRIP = NO;
DEAD_CODE_STRIPPING = NO;
DSTROOT = /tmp/SDWebImage.dst;
GCC_PRECOMPILE_PREFIX_HEADER = YES;
GCC_PREFIX_HEADER = "";
INSTALL_PATH = "$(BUILT_PRODUCTS_DIR)";
OTHER_LDFLAGS = "-ObjC";
PRODUCT_NAME = "$(TARGET_NAME)";
SKIP_INSTALL = YES;
STRIP_STYLE = "non-global";
};
name = Debug;
};
53922D7E148C55820056699D /* Release */ = {
isa = XCBuildConfiguration;
buildSettings = {
CLANG_ENABLE_OBJC_ARC = NO;
CLANG_WARN_OBJCPP_ARC_ABI = NO;
COPY_PHASE_STRIP = NO;
DEAD_CODE_STRIPPING = NO;
DSTROOT = /tmp/SDWebImage.dst;
GCC_PRECOMPILE_PREFIX_HEADER = YES;
GCC_PREFIX_HEADER = "";
INSTALL_PATH = "$(BUILT_PRODUCTS_DIR)";
OTHER_LDFLAGS = "-ObjC";
PRODUCT_NAME = "$(TARGET_NAME)";
SKIP_INSTALL = YES;
STRIP_STYLE = "non-global";
};
name = Release;
};
539F912D16316D2D00160719 /* Debug */ = {
isa = XCBuildConfiguration;
buildSettings = {
@ -669,7 +550,7 @@
/* End XCBuildConfiguration section */
/* Begin XCConfigurationList section */
531041DD157EAFA400BBABC3 /* Build configuration list for PBXNativeTarget "SDWebImage ARC+MKAnnotation" */ = {
531041DD157EAFA400BBABC3 /* Build configuration list for PBXNativeTarget "SDWebImage+MKAnnotation" */ = {
isa = XCConfigurationList;
buildConfigurations = (
531041DE157EAFA400BBABC3 /* Debug */,
@ -678,7 +559,7 @@
defaultConfigurationIsVisible = 0;
defaultConfigurationName = Release;
};
53761322155AD0D5005750A4 /* Build configuration list for PBXNativeTarget "SDWebImage ARC" */ = {
53761322155AD0D5005750A4 /* Build configuration list for PBXNativeTarget "SDWebImage" */ = {
isa = XCConfigurationList;
buildConfigurations = (
53761323155AD0D5005750A4 /* Debug */,
@ -696,15 +577,6 @@
defaultConfigurationIsVisible = 0;
defaultConfigurationName = Release;
};
53922D7C148C55820056699D /* Build configuration list for PBXNativeTarget "SDWebImage" */ = {
isa = XCConfigurationList;
buildConfigurations = (
53922D7D148C55820056699D /* Debug */,
53922D7E148C55820056699D /* Release */,
);
defaultConfigurationIsVisible = 0;
defaultConfigurationName = Release;
};
539F912C16316D2D00160719 /* Build configuration list for PBXAggregateTarget "SDWebImageFramework" */ = {
isa = XCConfigurationList;
buildConfigurations = (

View File

@ -7,14 +7,12 @@
//
#import "MapKit/MapKit.h"
#import "SDWebImageCompat.h"
#import "SDWebImageManagerDelegate.h"
#import "SDWebImageManager.h"
/**
* Integrates SDWebImage async downloading and caching of remote images with MKAnnotationView.
*/
@interface MKAnnotationView (WebCache) <SDWebImageManagerDelegate>
@interface MKAnnotationView (WebCache)
/**
* Set the imageView `image` with an `url`.
@ -47,17 +45,18 @@
*/
- (void)setImageWithURL:(NSURL *)url placeholderImage:(UIImage *)placeholder options:(SDWebImageOptions)options;
#if NS_BLOCKS_AVAILABLE
/**
* Set the imageView `image` with an `url`.
*
* The downloand is asynchronous and cached.
*
* @param url The url for the image.
* @param success A block to be executed when the image request succeed This block has no return value and takes a Boolean as parameter indicating if the image was cached or not.
* @param failure A block object to be executed when the image request failed. This block has no return value and takes the error object describing the network or parsing error that occurred (may be nil).
* @param completedBlock A block called when operation has been completed. This block as no return value
* and takes the requested UIImage as first parameter. In case of error the image parameter
* is nil and the second parameter may contain an NSError. The third parameter is a Boolean
* indicating if the image was retrived from the local cache of from the network.
*/
- (void)setImageWithURL:(NSURL *)url success:(SDWebImageSuccessBlock)success failure:(SDWebImageFailureBlock)failure;
- (void)setImageWithURL:(NSURL *)url completed:(SDWebImageCompletedBlock)completedBlock;
/**
* Set the imageView `image` with an `url`, placeholder.
@ -66,10 +65,12 @@
*
* @param url The url for the image.
* @param placeholder The image to be set initially, until the image request finishes.
* @param success A block to be executed when the image request succeed This block has no return value and takes a Boolean as parameter indicating if the image was cached or not.
* @param failure A block object to be executed when the image request failed. This block has no return value and takes the error object describing the network or parsing error that occurred (may be nil).
* @param completedBlock A block called when operation has been completed. This block as no return value
* and takes the requested UIImage as first parameter. In case of error the image parameter
* is nil and the second parameter may contain an NSError. The third parameter is a Boolean
* indicating if the image was retrived from the local cache of from the network.
*/
- (void)setImageWithURL:(NSURL *)url placeholderImage:(UIImage *)placeholder success:(SDWebImageSuccessBlock)success failure:(SDWebImageFailureBlock)failure;
- (void)setImageWithURL:(NSURL *)url placeholderImage:(UIImage *)placeholder completed:(SDWebImageCompletedBlock)completedBlock;
/**
* Set the imageView `image` with an `url`, placeholder and custom options.
@ -79,11 +80,12 @@
* @param url The url for the image.
* @param placeholder The image to be set initially, until the image request finishes.
* @param options The options to use when downloading the image. @see SDWebImageOptions for the possible values.
* @param success A block to be executed when the image request succeed This block has no return value and takes a Boolean as parameter indicating if the image was cached or not.
* @param failure A block object to be executed when the image request failed. This block has no return value and takes the error object describing the network or parsing error that occurred (may be nil).
* @param completedBlock A block called when operation has been completed. This block as no return value
* and takes the requested UIImage as first parameter. In case of error the image parameter
* is nil and the second parameter may contain an NSError. The third parameter is a Boolean
* indicating if the image was retrived from the local cache of from the network.
*/
- (void)setImageWithURL:(NSURL *)url placeholderImage:(UIImage *)placeholder options:(SDWebImageOptions)options success:(SDWebImageSuccessBlock)success failure:(SDWebImageFailureBlock)failure;
#endif
- (void)setImageWithURL:(NSURL *)url placeholderImage:(UIImage *)placeholder options:(SDWebImageOptions)options completed:(SDWebImageCompletedBlock)completedBlock;
/**
* Cancel the current download

View File

@ -7,74 +7,63 @@
//
#import "MKAnnotationView+WebCache.h"
#import "objc/runtime.h"
static char operationKey;
@implementation MKAnnotationView (WebCache)
- (void)setImageWithURL:(NSURL *)url
{
[self setImageWithURL:url placeholderImage:nil];
[self setImageWithURL:url placeholderImage:nil options:0 completed:nil];
}
- (void)setImageWithURL:(NSURL *)url placeholderImage:(UIImage *)placeholder
{
[self setImageWithURL:url placeholderImage:placeholder options:0];
[self setImageWithURL:url placeholderImage:placeholder options:0 completed:nil];
}
- (void)setImageWithURL:(NSURL *)url placeholderImage:(UIImage *)placeholder options:(SDWebImageOptions)options
{
SDWebImageManager *manager = [SDWebImageManager sharedManager];
// Remove in progress downloader from queue
[manager cancelForDelegate:self];
[self setImageWithURL:url placeholderImage:placeholder options:options completed:nil];
}
- (void)setImageWithURL:(NSURL *)url completed:(SDWebImageCompletedBlock)completedBlock
{
[self setImageWithURL:url placeholderImage:nil options:0 completed:completedBlock];
}
- (void)setImageWithURL:(NSURL *)url placeholderImage:(UIImage *)placeholder completed:(SDWebImageCompletedBlock)completedBlock
{
[self setImageWithURL:url placeholderImage:placeholder options:0 completed:completedBlock];
}
- (void)setImageWithURL:(NSURL *)url placeholderImage:(UIImage *)placeholder options:(SDWebImageOptions)options completed:(SDWebImageCompletedBlock)completedBlock
{
[self cancelCurrentImageLoad];
self.image = placeholder;
if (url)
{
[manager downloadWithURL:url delegate:self options:options];
id<SDWebImageOperation> operation = [SDWebImageManager.sharedManager downloadWithURL:url options:options progress:nil completed:^(UIImage *image, NSError *error, BOOL fromCache)
{
if (image) self.image = image;
if (completedBlock) completedBlock(image, error, fromCache);
}];
objc_setAssociatedObject(self, &operationKey, operation, OBJC_ASSOCIATION_RETAIN_NONATOMIC);
}
}
#if NS_BLOCKS_AVAILABLE
- (void)setImageWithURL:(NSURL *)url success:(SDWebImageSuccessBlock)success failure:(SDWebImageFailureBlock)failure;
{
[self setImageWithURL:url placeholderImage:nil success:success failure:failure];
}
- (void)setImageWithURL:(NSURL *)url placeholderImage:(UIImage *)placeholder success:(SDWebImageSuccessBlock)success failure:(SDWebImageFailureBlock)failure;
{
[self setImageWithURL:url placeholderImage:placeholder options:0 success:success failure:failure];
}
- (void)setImageWithURL:(NSURL *)url placeholderImage:(UIImage *)placeholder options:(SDWebImageOptions)options success:(SDWebImageSuccessBlock)success failure:(SDWebImageFailureBlock)failure;
{
SDWebImageManager *manager = [SDWebImageManager sharedManager];
// Remove in progress downloader from queue
[manager cancelForDelegate:self];
self.image = placeholder;
if (url)
{
[manager downloadWithURL:url delegate:self options:options success:success failure:failure];
}
}
#endif
- (void)cancelCurrentImageLoad
{
[[SDWebImageManager sharedManager] cancelForDelegate:self];
}
- (void)webImageManager:(SDWebImageManager *)imageManager didProgressWithPartialImage:(UIImage *)image forURL:(NSURL *)url
{
self.image = image;
}
- (void)webImageManager:(SDWebImageManager *)imageManager didFinishWithImage:(UIImage *)image
{
self.image = image;
// Cancel in progress downloader from queue
id<SDWebImageOperation> operation = objc_getAssociatedObject(self, &operationKey);
if (operation)
{
[operation cancel];
objc_setAssociatedObject(self, &operationKey, nil, OBJC_ASSOCIATION_RETAIN_NONATOMIC);
}
}
@end

View File

@ -7,18 +7,18 @@
*/
#import <Foundation/Foundation.h>
#import "SDImageCacheDelegate.h"
#import "SDWebImageCompat.h"
/**
* SDImageCache maintains a memory cache and an optional disk cache. Disk cache write operations are performed
* asynchronous so it doesnt add unnecessary latency to the UI.
*/
@interface SDImageCache : NSObject
{
NSCache *memCache;
NSString *diskCachePath;
NSOperationQueue *cacheInQueue, *cacheOutQueue;
}
/**
* The maximum length of time to keep an image in the cache, in seconds
*/
@property (assign, nonatomic) NSInteger maxCacheAge;
/**
* Returns global shared cache instance
@ -28,11 +28,11 @@
+ (SDImageCache *)sharedImageCache;
/**
* Sets the global maximum cache age
* Init a new cache store with a specific namespace
*
* @param maxCacheAge The maximum length of time to keep an image in the cache, in seconds
* @return ns The namespace to use for this cache store
*/
+ (void) setMaxCacheAge:(NSInteger) maxCacheAge;
- (id)initWithNamespace:(NSString *)ns;
/**
* Store an image into memory and disk cache at the given key.
@ -63,36 +63,12 @@
*/
- (void)storeImage:(UIImage *)image imageData:(NSData *)data forKey:(NSString *)key toDisk:(BOOL)toDisk;
/**
* Query the memory cache for an image at a given key and fallback to disk cache
* synchronousely if not found in memory.
*
* @warning This method may perform some synchronous IO operations
*
* @param key The unique key used to store the wanted image
*/
- (UIImage *)imageFromKey:(NSString *)key;
/**
* Query the memory cache for an image at a given key and optionnaly fallback to disk cache
* synchronousely if not found in memory.
*
* @warning This method may perform some synchronous IO operations if fromDisk is YES
*
* @param key The unique key used to store the wanted image
* @param fromDisk Try to retrive the image from disk if not found in memory if YES
*/
- (UIImage *)imageFromKey:(NSString *)key fromDisk:(BOOL)fromDisk;
/**
* Query the disk cache asynchronousely.
*
* @param key The unique key used to store the wanted image
* @param delegate The delegate object to send response to
* @param info An NSDictionary with some user info sent back to the delegate
*/
- (void)queryDiskCacheForKey:(NSString *)key delegate:(id <SDImageCacheDelegate>)delegate userInfo:(NSDictionary *)info;
- (void)queryDiskCacheForKey:(NSString *)key done:(void (^)(UIImage *image))doneBlock;
/**
* Remove the image from memory and disk cache synchronousely

View File

@ -13,39 +13,48 @@
#import <mach/mach.h>
#import <mach/mach_host.h>
static SDImageCache *instance;
const char *kDiskIOQueueName = "com.hackemist.SDWebImageDiskCache";
static const NSInteger kDefaultCacheMaxCacheAge = 60 * 60 * 24 * 7; // 1 week
@interface SDImageCache ()
@property (strong, nonatomic) NSCache *memCache;
@property (strong, nonatomic) NSString *diskCachePath;
@end
static NSInteger cacheMaxCacheAge = 60*60*24*7; // 1 week
@implementation SDImageCache
#pragma mark NSObject
+ (SDImageCache *)sharedImageCache
{
static dispatch_once_t once;
static id instance;
dispatch_once(&once, ^{instance = self.new;});
return instance;
}
- (id)init
{
return [self initWithNamespace:@"default"];
}
- (id)initWithNamespace:(NSString *)ns
{
if ((self = [super init]))
{
NSString *fullNamespace = [@"com.hackemist.SDWebImageCache." stringByAppendingString:ns];
// Init default values
_maxCacheAge = kDefaultCacheMaxCacheAge;
// Init the memory cache
memCache = [[NSCache alloc] init];
memCache.name = @"ImageCache";
_memCache = [[NSCache alloc] init];
_memCache.name = fullNamespace;
// Init the disk cache
NSArray *paths = NSSearchPathForDirectoriesInDomains(NSCachesDirectory, NSUserDomainMask, YES);
diskCachePath = SDWIReturnRetained([[paths objectAtIndex:0] stringByAppendingPathComponent:@"ImageCache"]);
if (![[NSFileManager defaultManager] fileExistsAtPath:diskCachePath])
{
[[NSFileManager defaultManager] createDirectoryAtPath:diskCachePath
withIntermediateDirectories:YES
attributes:nil
error:NULL];
}
// Init the operation queue
cacheInQueue = [[NSOperationQueue alloc] init];
cacheInQueue.maxConcurrentOperationCount = 1;
cacheOutQueue = [[NSOperationQueue alloc] init];
cacheOutQueue.maxConcurrentOperationCount = 1;
_diskCachePath = [paths[0] stringByAppendingPathComponent:fullNamespace];
#if TARGET_OS_IPHONE
// Subscribe to app events
@ -78,30 +87,7 @@ static NSInteger cacheMaxCacheAge = 60*60*24*7; // 1 week
- (void)dealloc
{
SDWISafeRelease(memCache);
SDWISafeRelease(diskCachePath);
SDWISafeRelease(cacheInQueue);
[[NSNotificationCenter defaultCenter] removeObserver:self];
SDWISuperDealoc;
}
#pragma mark SDImageCache (class methods)
+ (SDImageCache *)sharedImageCache
{
if (instance == nil)
{
instance = [[SDImageCache alloc] init];
}
return instance;
}
+ (void) setMaxCacheAge:(NSInteger)maxCacheAge
{
cacheMaxCacheAge = maxCacheAge;
}
#pragma mark SDImageCache (private)
@ -114,115 +100,71 @@ static NSInteger cacheMaxCacheAge = 60*60*24*7; // 1 week
NSString *filename = [NSString stringWithFormat:@"%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x",
r[0], r[1], r[2], r[3], r[4], r[5], r[6], r[7], r[8], r[9], r[10], r[11], r[12], r[13], r[14], r[15]];
return [diskCachePath stringByAppendingPathComponent:filename];
}
- (void)storeKeyWithDataToDisk:(NSArray *)keyAndData
{
// Can't use defaultManager another thread
NSFileManager *fileManager = [[NSFileManager alloc] init];
NSString *key = [keyAndData objectAtIndex:0];
NSData *data = [keyAndData count] > 1 ? [keyAndData objectAtIndex:1] : nil;
if (data)
{
[fileManager createFileAtPath:[self cachePathForKey:key] contents:data attributes:nil];
}
else
{
// If no data representation given, convert the UIImage in JPEG and store it
// This trick is more CPU/memory intensive and doesn't preserve alpha channel
UIImage *image = SDWIReturnRetained([self imageFromKey:key fromDisk:YES]); // be thread safe with no lock
if (image)
{
#if TARGET_OS_IPHONE
[fileManager createFileAtPath:[self cachePathForKey:key] contents:UIImageJPEGRepresentation(image, (CGFloat)1.0) attributes:nil];
#else
NSArray* representations = [image representations];
NSData* jpegData = [NSBitmapImageRep representationOfImageRepsInArray: representations usingType: NSJPEGFileType properties:nil];
[fileManager createFileAtPath:[self cachePathForKey:key] contents:jpegData attributes:nil];
#endif
SDWIRelease(image);
}
}
SDWIRelease(fileManager);
}
- (void)notifyDelegate:(NSDictionary *)arguments
{
NSString *key = [arguments objectForKey:@"key"];
id <SDImageCacheDelegate> delegate = [arguments objectForKey:@"delegate"];
NSDictionary *info = [arguments objectForKey:@"userInfo"];
UIImage *image = [arguments objectForKey:@"image"];
if (image)
{
[memCache setObject:image forKey:key cost:image.size.height * image.size.width * image.scale];
if ([delegate respondsToSelector:@selector(imageCache:didFindImage:forKey:userInfo:)])
{
[delegate imageCache:self didFindImage:image forKey:key userInfo:info];
}
}
else
{
if ([delegate respondsToSelector:@selector(imageCache:didNotFindImageForKey:userInfo:)])
{
[delegate imageCache:self didNotFindImageForKey:key userInfo:info];
}
}
}
- (void)queryDiskCacheOperation:(NSDictionary *)arguments
{
NSString *key = [arguments objectForKey:@"key"];
NSMutableDictionary *mutableArguments = SDWIReturnAutoreleased([arguments mutableCopy]);
UIImage *image = SDScaledImageForPath(key, [NSData dataWithContentsOfFile:[self cachePathForKey:key]]);
if (image)
{
UIImage *decodedImage = [UIImage decodedImageWithImage:image];
if (decodedImage)
{
image = decodedImage;
}
[mutableArguments setObject:image forKey:@"image"];
}
[self performSelectorOnMainThread:@selector(notifyDelegate:) withObject:mutableArguments waitUntilDone:NO];
return [self.diskCachePath stringByAppendingPathComponent:filename];
}
#pragma mark ImageCache
- (void)storeImage:(UIImage *)image imageData:(NSData *)data forKey:(NSString *)key toDisk:(BOOL)toDisk
- (void)storeImage:(UIImage *)image imageData:(NSData *)imageData forKey:(NSString *)key toDisk:(BOOL)toDisk
{
if (!image || !key)
{
return;
}
[memCache setObject:image forKey:key cost:image.size.height * image.size.width * image.scale];
[self.memCache setObject:image forKey:key cost:image.size.height * image.size.width * image.scale];
if (toDisk)
{
NSArray *keyWithData;
if (data)
{
keyWithData = [NSArray arrayWithObjects:key, data, nil];
}
else
{
keyWithData = [NSArray arrayWithObjects:key, nil];
}
dispatch_queue_t queue = dispatch_queue_create(kDiskIOQueueName, nil);
NSInvocationOperation *operation = SDWIReturnAutoreleased([[NSInvocationOperation alloc] initWithTarget:self
selector:@selector(storeKeyWithDataToDisk:)
object:keyWithData]);
[cacheInQueue addOperation:operation];
dispatch_async(queue, ^
{
NSData *data = imageData;
if (!data)
{
if (image)
{
#if TARGET_OS_IPHONE
data = UIImageJPEGRepresentation(image, (CGFloat)1.0);
#else
data = [NSBitmapImageRep representationOfImageRepsInArray:image.representations usingType: NSJPEGFileType properties:nil];
#endif
}
}
if (data)
{
if (![[NSFileManager defaultManager] fileExistsAtPath:_diskCachePath])
{
[[NSFileManager defaultManager] createDirectoryAtPath:_diskCachePath
withIntermediateDirectories:YES
attributes:nil
error:NULL];
}
NSString *path = [self cachePathForKey:key];
dispatch_io_t ioChannel = dispatch_io_create_with_path(DISPATCH_IO_STREAM, [path UTF8String], O_RDWR | O_CREAT, S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH, queue, nil);
dispatch_data_t dispatchData = dispatch_data_create(data.bytes, data.length, queue, ^{[data self];});
dispatch_io_write(ioChannel, 0, dispatchData, queue, ^(bool done, dispatch_data_t dispatchedData, int error)
{
if (error != 0)
{
NSLog(@"SDWebImageCache: Error writing image from disk cache: errno=%d", error);
}
if(done)
{
dispatch_io_close(ioChannel, 0);
}
});
dispatch_release(dispatchData);
dispatch_release(ioChannel);
}
});
dispatch_release(queue);
}
}
@ -236,72 +178,63 @@ static NSInteger cacheMaxCacheAge = 60*60*24*7; // 1 week
[self storeImage:image imageData:nil forKey:key toDisk:toDisk];
}
- (UIImage *)imageFromKey:(NSString *)key
- (void)queryDiskCacheForKey:(NSString *)key done:(void (^)(UIImage *image))doneBlock
{
return [self imageFromKey:key fromDisk:YES];
}
- (UIImage *)imageFromKey:(NSString *)key fromDisk:(BOOL)fromDisk
{
if (key == nil)
{
return nil;
}
UIImage *image = [memCache objectForKey:key];
if (!image && fromDisk)
{
image = SDScaledImageForPath(key, [NSData dataWithContentsOfFile:[self cachePathForKey:key]]);
if (image)
{
[memCache setObject:image forKey:key cost:image.size.height * image.size.width * image.scale];
}
}
return image;
}
- (void)queryDiskCacheForKey:(NSString *)key delegate:(id <SDImageCacheDelegate>)delegate userInfo:(NSDictionary *)info
{
if (!delegate)
{
return;
}
if (!doneBlock) return;
if (!key)
{
if ([delegate respondsToSelector:@selector(imageCache:didNotFindImageForKey:userInfo:)])
{
[delegate imageCache:self didNotFindImageForKey:key userInfo:info];
}
doneBlock(nil);
return;
}
// First check the in-memory cache...
UIImage *image = [memCache objectForKey:key];
UIImage *image = [self.memCache objectForKey:key];
if (image)
{
// ...notify delegate immediately, no need to go async
if ([delegate respondsToSelector:@selector(imageCache:didFindImage:forKey:userInfo:)])
{
[delegate imageCache:self didFindImage:image forKey:key userInfo:info];
}
doneBlock(image);
return;
}
NSMutableDictionary *arguments = [NSMutableDictionary dictionaryWithCapacity:3];
[arguments setObject:key forKey:@"key"];
[arguments setObject:delegate forKey:@"delegate"];
if (info)
NSString *path = [self cachePathForKey:key];
dispatch_queue_t queue = dispatch_queue_create(kDiskIOQueueName, nil);
dispatch_io_t ioChannel = dispatch_io_create_with_path(DISPATCH_IO_STREAM, path.UTF8String, O_RDONLY, 0, queue, nil);
dispatch_io_read(ioChannel, 0, SIZE_MAX, queue, ^(bool done, dispatch_data_t dispatchedData, int error)
{
[arguments setObject:info forKey:@"userInfo"];
}
NSInvocationOperation *operation = SDWIReturnAutoreleased([[NSInvocationOperation alloc] initWithTarget:self
selector:@selector(queryDiskCacheOperation:)
object:arguments]);
[cacheOutQueue addOperation:operation];
if (error)
{
if (error != 2)
{
NSLog(@"SDWebImageCache: Error reading image from disk cache: errno=%d", error);
}
doneBlock(nil);
return;
}
dispatch_data_apply(dispatchedData, (dispatch_data_applier_t)^(dispatch_data_t region, size_t offset, const void *buffer, size_t size)
{
UIImage *diskImage = SDScaledImageForPath(key, [NSData dataWithBytes:buffer length:size]);
if (image)
{
UIImage *decodedImage = [UIImage decodedImageWithImage:diskImage];
if (decodedImage)
{
diskImage = decodedImage;
}
[self.memCache setObject:diskImage forKey:key cost:image.size.height * image.size.width * image.scale];
}
doneBlock(diskImage);
return true;
});
});
dispatch_release(queue);
dispatch_release(ioChannel);
}
- (void)removeImageForKey:(NSString *)key
@ -316,52 +249,65 @@ static NSInteger cacheMaxCacheAge = 60*60*24*7; // 1 week
return;
}
[memCache removeObjectForKey:key];
[self.memCache removeObjectForKey:key];
if (fromDisk)
{
[[NSFileManager defaultManager] removeItemAtPath:[self cachePathForKey:key] error:nil];
dispatch_queue_t queue = dispatch_queue_create(kDiskIOQueueName, nil);
dispatch_async(queue, ^
{
[[NSFileManager defaultManager] removeItemAtPath:[self cachePathForKey:key] error:nil];
});
dispatch_release(queue);
}
}
- (void)clearMemory
{
[cacheInQueue cancelAllOperations]; // won't be able to complete
[memCache removeAllObjects];
[self.memCache removeAllObjects];
}
- (void)clearDisk
{
[cacheInQueue cancelAllOperations];
[[NSFileManager defaultManager] removeItemAtPath:diskCachePath error:nil];
[[NSFileManager defaultManager] createDirectoryAtPath:diskCachePath
withIntermediateDirectories:YES
attributes:nil
error:NULL];
dispatch_queue_t queue = dispatch_queue_create(kDiskIOQueueName, nil);
dispatch_async(queue, ^
{
[[NSFileManager defaultManager] removeItemAtPath:self.diskCachePath error:nil];
[[NSFileManager defaultManager] createDirectoryAtPath:self.diskCachePath
withIntermediateDirectories:YES
attributes:nil
error:NULL];
});
dispatch_release(queue);
}
- (void)cleanDisk
{
NSDate *expirationDate = [NSDate dateWithTimeIntervalSinceNow:-cacheMaxCacheAge];
NSDirectoryEnumerator *fileEnumerator = [[NSFileManager defaultManager] enumeratorAtPath:diskCachePath];
for (NSString *fileName in fileEnumerator)
dispatch_queue_t queue = dispatch_queue_create(kDiskIOQueueName, nil);
dispatch_async(queue, ^
{
NSString *filePath = [diskCachePath stringByAppendingPathComponent:fileName];
NSDictionary *attrs = [[NSFileManager defaultManager] attributesOfItemAtPath:filePath error:nil];
if ([[[attrs fileModificationDate] laterDate:expirationDate] isEqualToDate:expirationDate])
NSDate *expirationDate = [NSDate dateWithTimeIntervalSinceNow:-self.maxCacheAge];
NSDirectoryEnumerator *fileEnumerator = [[NSFileManager defaultManager] enumeratorAtPath:self.diskCachePath];
for (NSString *fileName in fileEnumerator)
{
[[NSFileManager defaultManager] removeItemAtPath:filePath error:nil];
NSString *filePath = [self.diskCachePath stringByAppendingPathComponent:fileName];
NSDictionary *attrs = [[NSFileManager defaultManager] attributesOfItemAtPath:filePath error:nil];
if ([[[attrs fileModificationDate] laterDate:expirationDate] isEqualToDate:expirationDate])
{
[[NSFileManager defaultManager] removeItemAtPath:filePath error:nil];
}
}
}
});
dispatch_release(queue);
}
-(int)getSize
{
int size = 0;
NSDirectoryEnumerator *fileEnumerator = [[NSFileManager defaultManager] enumeratorAtPath:diskCachePath];
NSDirectoryEnumerator *fileEnumerator = [[NSFileManager defaultManager] enumeratorAtPath:self.diskCachePath];
for (NSString *fileName in fileEnumerator)
{
NSString *filePath = [diskCachePath stringByAppendingPathComponent:fileName];
NSString *filePath = [self.diskCachePath stringByAppendingPathComponent:fileName];
NSDictionary *attrs = [[NSFileManager defaultManager] attributesOfItemAtPath:filePath error:nil];
size += [attrs fileSize];
}
@ -371,7 +317,7 @@ static NSInteger cacheMaxCacheAge = 60*60*24*7; // 1 week
- (int)getDiskCount
{
int count = 0;
NSDirectoryEnumerator *fileEnumerator = [[NSFileManager defaultManager] enumeratorAtPath:diskCachePath];
NSDirectoryEnumerator *fileEnumerator = [[NSFileManager defaultManager] enumeratorAtPath:self.diskCachePath];
for (NSString *fileName in fileEnumerator)
{
count += 1;

View File

@ -1,39 +0,0 @@
//
// SDImageCacheDelegate.h
// Dailymotion
//
// Created by Olivier Poitrey on 16/09/10.
// Copyright 2010 Dailymotion. All rights reserved.
//
#import "SDWebImageCompat.h"
@class SDImageCache;
/**
* Delegate protocol for SDImageCache
*/
@protocol SDImageCacheDelegate <NSObject>
@optional
/**
* Called when [SDImageCache queryDiskCacheForKey:delegate:userInfo:] retrived the image from cache
*
* @param imageCache The cache store instance
* @param image The requested image instance
* @param key The requested image cache key
* @param info The provided user info dictionary
*/
- (void)imageCache:(SDImageCache *)imageCache didFindImage:(UIImage *)image forKey:(NSString *)key userInfo:(NSDictionary *)info;
/**
* Called when [SDImageCache queryDiskCacheForKey:delegate:userInfo:] did not find the image in the cache
*
* @param imageCache The cache store instance
* @param key The requested image cache key
* @param info The provided user info dictionary
*/
- (void)imageCache:(SDImageCache *)imageCache didNotFindImageForKey:(NSString *)key userInfo:(NSDictionary *)info;
@end

View File

@ -9,6 +9,18 @@
#import <TargetConditionals.h>
#ifdef __OBJC_GC__
#error Dailymotion SDK does not support Objective-C Garbage Collection
#endif
#if !__has_feature(objc_arc)
#error Dailymotion SDK is ARC only. Either turn on ARC for the project or use -fobjc-arc flag
#endif
#if __IPHONE_OS_VERSION_MIN_REQUIRED < __IPHONE_5_0
#error SDWebImage doesn't support Deployement Target version < 5.0
#endif
#if !TARGET_OS_IPHONE
#import <AppKit/AppKit.h>
#ifndef UIImage
@ -21,34 +33,6 @@
#import <UIKit/UIKit.h>
#endif
#if ! __has_feature(objc_arc)
#define SDWIAutorelease(__v) ([__v autorelease]);
#define SDWIReturnAutoreleased SDWIAutorelease
#define SDWIRetain(__v) ([__v retain]);
#define SDWIReturnRetained SDWIRetain
#define SDWIRelease(__v) ([__v release]);
#define SDWISafeRelease(__v) ([__v release], __v = nil);
#define SDWISuperDealoc [super dealloc];
#define SDWIWeak
#else
// -fobjc-arc
#define SDWIAutorelease(__v)
#define SDWIReturnAutoreleased(__v) (__v)
#define SDWIRetain(__v)
#define SDWIReturnRetained(__v) (__v)
#define SDWIRelease(__v)
#define SDWISafeRelease(__v) (__v = nil);
#define SDWISuperDealoc
#define SDWIWeak __unsafe_unretained
#endif
NS_INLINE UIImage *SDScaledImageForPath(NSString *path, NSObject *imageOrData)
{
if (!imageOrData)
@ -63,7 +47,7 @@ NS_INLINE UIImage *SDScaledImageForPath(NSString *path, NSObject *imageOrData)
}
else if ([imageOrData isKindOfClass:[UIImage class]])
{
image = SDWIReturnRetained((UIImage *)imageOrData);
image = (UIImage *)imageOrData;
}
else
{
@ -83,10 +67,9 @@ NS_INLINE UIImage *SDScaledImageForPath(NSString *path, NSObject *imageOrData)
}
}
UIImage *scaledImage = [[UIImage alloc] initWithCGImage:image.CGImage scale:scale orientation:UIImageOrientationUp];
SDWISafeRelease(image)
UIImage *scaledImage = [[UIImage alloc] initWithCGImage:image.CGImage scale:scale orientation:image.imageOrientation];
image = scaledImage;
}
return SDWIReturnAutoreleased(image);
return image;
}

View File

@ -11,55 +11,6 @@
#import <Foundation/Foundation.h>
#import "SDWebImageCompat.h"
@protocol SDWebImageDecoderDelegate;
/**
* Decoding image data is the most expensive step, and it is performed on the main thread. SDWebImageDecoder force the
* image decoding in a separate thread so UIImage will have high chance to reuse the cached result when used by UI in
* the main thread.
*
* @see https://github.com/rs/SDWebImage/pull/18
*/
@interface SDWebImageDecoder : NSObject
{
NSOperationQueue *imageDecodingQueue;
}
/**
* Returns a shared global instance of image decoder
*
* @return An SDWebImageDecoder shared instance
*/
+ (SDWebImageDecoder *)sharedImageDecoder;
/**
* Pre-decode a given image in a separate thread.
*
* @param image The image to pre-decode
* @param delegate The object to notify once pre-decoding is completed
* @param info A user info object
*/
- (void)decodeImage:(UIImage *)image withDelegate:(id <SDWebImageDecoderDelegate>)delegate userInfo:(NSDictionary *)info;
@end
/**
* Delegate protocol for SDWebImageDecoder
*/
@protocol SDWebImageDecoderDelegate <NSObject>
/**
* Called when pre-decoding is completed
*
* @param decoder The image decoder instance
* @param image The pre-decoded image
* @param userInfo the provided user info dictionary
*/
- (void)imageDecoder:(SDWebImageDecoder *)decoder didFinishDecodingImage:(UIImage *)image userInfo:(NSDictionary *)userInfo;
@end
@interface UIImage (ForceDecode)
+ (UIImage *)decodedImageWithImage:(UIImage *)image;

View File

@ -10,88 +10,6 @@
#import "SDWebImageDecoder.h"
#define DECOMPRESSED_IMAGE_KEY @"decompressedImage"
#define DECODE_INFO_KEY @"decodeInfo"
#define IMAGE_KEY @"image"
#define DELEGATE_KEY @"delegate"
#define USER_INFO_KEY @"userInfo"
@implementation SDWebImageDecoder
static SDWebImageDecoder *sharedInstance;
- (void)notifyDelegateOnMainThreadWithInfo:(NSDictionary *)dict
{
SDWIRetain(dict);
NSDictionary *decodeInfo = [dict objectForKey:DECODE_INFO_KEY];
UIImage *decodedImage = [dict objectForKey:DECOMPRESSED_IMAGE_KEY];
id <SDWebImageDecoderDelegate> delegate = [decodeInfo objectForKey:DELEGATE_KEY];
NSDictionary *userInfo = [decodeInfo objectForKey:USER_INFO_KEY];
[delegate imageDecoder:self didFinishDecodingImage:decodedImage userInfo:userInfo];
SDWIRelease(dict);
}
- (void)decodeImageWithInfo:(NSDictionary *)decodeInfo
{
UIImage *image = [decodeInfo objectForKey:IMAGE_KEY];
UIImage *decompressedImage = [UIImage decodedImageWithImage:image];
if (!decompressedImage)
{
// If really have any error occurs, we use the original image at this moment
decompressedImage = image;
}
NSDictionary *dict = [NSDictionary dictionaryWithObjectsAndKeys:
decompressedImage, DECOMPRESSED_IMAGE_KEY,
decodeInfo, DECODE_INFO_KEY, nil];
[self performSelectorOnMainThread:@selector(notifyDelegateOnMainThreadWithInfo:) withObject:dict waitUntilDone:NO];
}
- (id)init
{
if ((self = [super init]))
{
// Initialization code here.
imageDecodingQueue = [[NSOperationQueue alloc] init];
}
return self;
}
- (void)decodeImage:(UIImage *)image withDelegate:(id<SDWebImageDecoderDelegate>)delegate userInfo:(NSDictionary *)info
{
NSDictionary *decodeInfo = [NSDictionary dictionaryWithObjectsAndKeys:
image, IMAGE_KEY,
delegate, DELEGATE_KEY,
info, USER_INFO_KEY, nil];
NSOperation *operation = [[NSInvocationOperation alloc] initWithTarget:self selector:@selector(decodeImageWithInfo:) object:decodeInfo];
[imageDecodingQueue addOperation:operation];
SDWIRelease(operation);
}
- (void)dealloc
{
SDWISafeRelease(imageDecodingQueue);
SDWISuperDealoc;
}
+ (SDWebImageDecoder *)sharedImageDecoder
{
if (!sharedInstance)
{
sharedInstance = [[SDWebImageDecoder alloc] init];
}
return sharedInstance;
}
@end
@implementation UIImage (ForceDecode)
+ (UIImage *)decodedImageWithImage:(UIImage *)image
@ -126,7 +44,7 @@ static SDWebImageDecoder *sharedInstance;
UIImage *decompressedImage = [[UIImage alloc] initWithCGImage:decompressedImageRef scale:image.scale orientation:image.imageOrientation];
CGImageRelease(decompressedImageRef);
return SDWIReturnAutoreleased(decompressedImage);
return decompressedImage;
}
@end

View File

@ -7,45 +7,29 @@
*/
#import <Foundation/Foundation.h>
#import "SDWebImageDownloaderDelegate.h"
#import "SDWebImageCompat.h"
#import "SDWebImageOperation.h"
typedef enum
{
SDWebImageDownloaderLowPriority = 1 << 0,
SDWebImageDownloaderProgressiveDownload = 1 << 1
} SDWebImageDownloaderOptions;
extern NSString *const SDWebImageDownloadStartNotification;
extern NSString *const SDWebImageDownloadStopNotification;
typedef void(^SDWebImageDownloaderProgressBlock)(NSUInteger receivedSize, long long expectedSize);
typedef void(^SDWebImageDownloaderCompletedBlock)(UIImage *image, NSError *error, BOOL finished);
/**
* Asynchronous downloader dedicated and optimized for image loading.
*/
@interface SDWebImageDownloader : NSObject
{
@private
NSURL *url;
SDWIWeak id<SDWebImageDownloaderDelegate> delegate;
NSURLConnection *connection;
NSMutableData *imageData;
id userInfo;
BOOL lowPriority;
NSUInteger expectedSize;
BOOL progressive;
size_t width, height;
}
@property (nonatomic, retain) NSURL *url;
@property (nonatomic, assign) id<SDWebImageDownloaderDelegate> delegate;
@property (nonatomic, retain) NSMutableData *imageData;
@property (nonatomic, retain) id userInfo;
@property (nonatomic, readwrite) BOOL lowPriority;
@property (assign, nonatomic) NSInteger maxConcurrentDownloads;
/**
* If set to YES, enables progressive download support.
*
* The [SDWebImageDownloaderDelegate imageDownloader:didUpdatePartialImage:] delegate method is then called
* while the image is downloaded with an image object containing the portion of the currently downloaded
* image.
*
* @see http://www.cocoaintheshell.com/2011/05/progressive-images-download-imageio/
*/
@property (nonatomic, readwrite) BOOL progressive;
+ (SDWebImageDownloader *)sharedDownloader;
/**
* Creates a SDWebImageDownloader async downloader instance with a given URL
@ -55,24 +39,22 @@ extern NSString *const SDWebImageDownloadStopNotification;
* @see SDWebImageDownloaderDelegate
*
* @param url The URL to the image to download
* @param delegate The delegate object
* @param userInfo A NSDictionary containing custom info
* @param lowPriority Ensure the download won't run during UI interactions
* @param options The options to be used for this download
* @param progress A block called repeatedly while the image is downloading
* @param completed A block called once the download is completed.
* If the download succeeded, the image parameter is set, in case of error,
* error parameter is set with the error. The last parameter is always YES
* if SDWebImageDownloaderProgressiveDownload isn't use. With the
* SDWebImageDownloaderProgressiveDownload option, this block is called
* repeatedly with the partial image object and the finished argument set to NO
* before to be called a last time with the full image and finished argument
* set to YES.
*
* @return A new SDWebImageDownloader instance
* @return A cancellable SDWebImageOperation
*/
+ (id)downloaderWithURL:(NSURL *)url delegate:(id<SDWebImageDownloaderDelegate>)delegate userInfo:(id)userInfo lowPriority:(BOOL)lowPriority;
+ (id)downloaderWithURL:(NSURL *)url delegate:(id<SDWebImageDownloaderDelegate>)delegate userInfo:(id)userInfo;
+ (id)downloaderWithURL:(NSURL *)url delegate:(id<SDWebImageDownloaderDelegate>)delegate;
- (void)start;
/**
* Cancel the download immediatelly
*/
- (void)cancel;
// This method is now no-op and is deprecated
+ (void)setMaxConcurrentDownloads:(NSUInteger)max __attribute__((deprecated));
- (id<SDWebImageOperation>)downloadImageWithURL:(NSURL *)url
options:(SDWebImageDownloaderOptions)options
progress:(SDWebImageDownloaderProgressBlock)progressBlock
completed:(SDWebImageDownloaderCompletedBlock)completedBlock;
@end

View File

@ -7,41 +7,31 @@
*/
#import "SDWebImageDownloader.h"
#import "SDWebImageDecoder.h"
#import "SDWebImageDownloaderOperation.h"
#import <ImageIO/ImageIO.h>
@interface SDWebImageDownloader (ImageDecoder) <SDWebImageDecoderDelegate>
@end
NSString *const SDWebImageDownloadStartNotification = @"SDWebImageDownloadStartNotification";
NSString *const SDWebImageDownloadStopNotification = @"SDWebImageDownloadStopNotification";
NSString *const kProgressCallbackKey = @"completed";
NSString *const kCompletedCallbackKey = @"completed";
@interface SDWebImageDownloader ()
@property (nonatomic, retain) NSURLConnection *connection;
@property (strong, nonatomic) NSOperationQueue *downloadQueue;
@property (strong, nonatomic) NSMutableDictionary *URLCallbacks;
@end
@implementation SDWebImageDownloader
@synthesize url, delegate, connection, imageData, userInfo, lowPriority, progressive;
#pragma mark Public Methods
+ (id)downloaderWithURL:(NSURL *)url delegate:(id<SDWebImageDownloaderDelegate>)delegate
{
return [self downloaderWithURL:url delegate:delegate userInfo:nil];
}
+ (id)downloaderWithURL:(NSURL *)url delegate:(id<SDWebImageDownloaderDelegate>)delegate userInfo:(id)userInfo
{
return [self downloaderWithURL:url delegate:delegate userInfo:userInfo lowPriority:NO];
}
+ (id)downloaderWithURL:(NSURL *)url delegate:(id<SDWebImageDownloaderDelegate>)delegate userInfo:(id)userInfo lowPriority:(BOOL)lowPriority
+ (void)initialize
{
// Bind SDNetworkActivityIndicator if available (download it here: http://github.com/rs/SDNetworkActivityIndicator )
// To use it, just add #import "SDNetworkActivityIndicator.h" in addition to the SDWebImage import
if (NSClassFromString(@"SDNetworkActivityIndicator"))
{
#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Warc-performSelector-leaks"
id activityIndicator = [NSClassFromString(@"SDNetworkActivityIndicator") performSelector:NSSelectorFromString(@"sharedActivityIndicator")];
@ -58,226 +48,95 @@ NSString *const SDWebImageDownloadStopNotification = @"SDWebImageDownloadStopNot
selector:NSSelectorFromString(@"stopActivity")
name:SDWebImageDownloadStopNotification object:nil];
}
SDWebImageDownloader *downloader = SDWIReturnAutoreleased([[SDWebImageDownloader alloc] init]);
downloader.url = url;
downloader.delegate = delegate;
downloader.userInfo = userInfo;
downloader.lowPriority = lowPriority;
[downloader performSelectorOnMainThread:@selector(start) withObject:nil waitUntilDone:YES];
return downloader;
}
+ (void)setMaxConcurrentDownloads:(NSUInteger)max
+ (SDWebImageDownloader *)sharedDownloader
{
// NOOP
static dispatch_once_t once;
static id instance;
dispatch_once(&once, ^{instance = self.new;});
return instance;
}
- (void)start
- (id)init
{
// In order to prevent from potential duplicate caching (NSURLCache + SDImageCache) we disable the cache for image requests
NSURLRequest *request = [[NSURLRequest alloc] initWithURL:url cachePolicy:NSURLRequestReloadIgnoringLocalCacheData timeoutInterval:15];
self.connection = SDWIReturnAutoreleased([[NSURLConnection alloc] initWithRequest:request delegate:self startImmediately:NO]);
// If not in low priority mode, ensure we aren't blocked by UI manipulations (default runloop mode for NSURLConnection is NSEventTrackingRunLoopMode)
if (!lowPriority)
if ((self = [super init]))
{
[connection scheduleInRunLoop:[NSRunLoop currentRunLoop] forMode:NSRunLoopCommonModes];
_downloadQueue = NSOperationQueue.new;
_downloadQueue.maxConcurrentOperationCount = 10;
_URLCallbacks = NSMutableDictionary.new;
}
[connection start];
SDWIRelease(request);
return self;
}
if (connection)
- (void)setMaxConcurrentDownloads:(NSInteger)maxConcurrentDownloads
{
_downloadQueue.maxConcurrentOperationCount = maxConcurrentDownloads;
}
- (NSInteger)maxConcurrentDownloads
{
return _downloadQueue.maxConcurrentOperationCount;
}
- (NSOperation *)downloadImageWithURL:(NSURL *)url options:(SDWebImageDownloaderOptions)options progress:(void (^)(NSUInteger, long long))progressBlock completed:(void (^)(UIImage *, NSError *, BOOL))completedBlock
{
__block SDWebImageDownloaderOperation *operation;
dispatch_async(dispatch_get_main_queue(), ^ // NSDictionary isn't thread safe
{
[[NSNotificationCenter defaultCenter] postNotificationName:SDWebImageDownloadStartNotification object:self];
}
else
{
if ([delegate respondsToSelector:@selector(imageDownloader:didFailWithError:)])
BOOL performDownload = NO;
if (!self.URLCallbacks[url])
{
[delegate performSelector:@selector(imageDownloader:didFailWithError:) withObject:self withObject:nil];
}
}
}
- (void)cancel
{
if (connection)
{
[connection cancel];
self.connection = nil;
[[NSNotificationCenter defaultCenter] postNotificationName:SDWebImageDownloadStopNotification object:self];
}
}
#pragma mark NSURLConnection (delegate)
- (void)connection:(NSURLConnection *)aConnection didReceiveResponse:(NSURLResponse *)response
{
if (![response respondsToSelector:@selector(statusCode)] || [((NSHTTPURLResponse *)response) statusCode] < 400)
{
expectedSize = response.expectedContentLength > 0 ? (NSUInteger)response.expectedContentLength : 0;
self.imageData = SDWIReturnAutoreleased([[NSMutableData alloc] initWithCapacity:expectedSize]);
}
else
{
[aConnection cancel];
[[NSNotificationCenter defaultCenter] postNotificationName:SDWebImageDownloadStopNotification object:nil];
if ([delegate respondsToSelector:@selector(imageDownloader:didFailWithError:)])
{
NSError *error = [[NSError alloc] initWithDomain:NSURLErrorDomain
code:[((NSHTTPURLResponse *)response) statusCode]
userInfo:nil];
[delegate performSelector:@selector(imageDownloader:didFailWithError:) withObject:self withObject:error];
SDWIRelease(error);
self.URLCallbacks[url] = NSMutableArray.new;
performDownload = YES;
}
self.connection = nil;
self.imageData = nil;
}
}
- (void)connection:(NSURLConnection *)aConnection didReceiveData:(NSData *)data
{
[imageData appendData:data];
if (CGImageSourceCreateImageAtIndex == NULL)
{
// ImageIO isn't present in iOS < 4
self.progressive = NO;
}
if (self.progressive && expectedSize > 0 && [delegate respondsToSelector:@selector(imageDownloader:didUpdatePartialImage:)])
{
// The following code is from http://www.cocoaintheshell.com/2011/05/progressive-images-download-imageio/
// Thanks to the author @Nyx0uf
// Get the total bytes downloaded
const NSUInteger totalSize = [imageData length];
// Update the data source, we must pass ALL the data, not just the new bytes
CGImageSourceRef imageSource = CGImageSourceCreateIncremental(NULL);
#if __has_feature(objc_arc)
CGImageSourceUpdateData(imageSource, (__bridge CFDataRef)imageData, totalSize == expectedSize);
#else
CGImageSourceUpdateData(imageSource, (CFDataRef)imageData, totalSize == expectedSize);
#endif
if (width + height == 0)
// Handle single download of simultaneous download request for the same URL
{
CFDictionaryRef properties = CGImageSourceCopyPropertiesAtIndex(imageSource, 0, NULL);
if (properties)
NSMutableArray *callbacksForURL = self.URLCallbacks[url];
NSMutableDictionary *callbacks = NSMutableDictionary.new;
if (progressBlock) callbacks[kProgressCallbackKey] = progressBlock;
if (completedBlock) callbacks[kCompletedCallbackKey] = completedBlock;
[callbacksForURL addObject:callbacks];
self.URLCallbacks[url] = callbacksForURL;
}
if (performDownload)
{
// In order to prevent from potential duplicate caching (NSURLCache + SDImageCache) we disable the cache for image requests
NSURLRequest *request = [[NSURLRequest alloc] initWithURL:url cachePolicy:NSURLRequestReloadIgnoringLocalCacheData timeoutInterval:15];
operation = [SDWebImageDownloaderOperation.alloc initWithRequest:request options:options progress:^(NSUInteger receivedSize, long long expectedSize)
{
CFTypeRef val = CFDictionaryGetValue(properties, kCGImagePropertyPixelHeight);
if (val) CFNumberGetValue(val, kCFNumberLongType, &height);
val = CFDictionaryGetValue(properties, kCGImagePropertyPixelWidth);
if (val) CFNumberGetValue(val, kCFNumberLongType, &width);
CFRelease(properties);
}
}
if (width + height > 0 && totalSize < expectedSize)
{
// Create the image
CGImageRef partialImageRef = CGImageSourceCreateImageAtIndex(imageSource, 0, NULL);
#ifdef TARGET_OS_IPHONE
// Workaround for iOS anamorphic image
if (partialImageRef)
{
const size_t partialHeight = CGImageGetHeight(partialImageRef);
CGColorSpaceRef colorSpace = CGColorSpaceCreateDeviceRGB();
CGContextRef bmContext = CGBitmapContextCreate(NULL, width, height, 8, width * 4, colorSpace, kCGBitmapByteOrderDefault | kCGImageAlphaPremultipliedFirst);
CGColorSpaceRelease(colorSpace);
if (bmContext)
dispatch_async(dispatch_get_main_queue(), ^
{
CGContextDrawImage(bmContext, (CGRect){.origin.x = 0.0f, .origin.y = 0.0f, .size.width = width, .size.height = partialHeight}, partialImageRef);
CGImageRelease(partialImageRef);
partialImageRef = CGBitmapContextCreateImage(bmContext);
CGContextRelease(bmContext);
}
else
{
CGImageRelease(partialImageRef);
partialImageRef = nil;
}
NSMutableArray *callbacksForURL = self.URLCallbacks[url];
for (NSDictionary *callbacks in callbacksForURL)
{
SDWebImageDownloaderProgressBlock callback = callbacks[kProgressCallbackKey];
if (callback) callback(receivedSize, expectedSize);
}
});
}
#endif
if (partialImageRef)
completed:^(UIImage *image, NSError *error, BOOL finished)
{
UIImage *image = SDScaledImageForPath(url.absoluteString, [UIImage imageWithCGImage:partialImageRef]);
[[SDWebImageDecoder sharedImageDecoder] decodeImage:image
withDelegate:self
userInfo:[NSDictionary dictionaryWithObject:@"partial" forKey:@"type"]];
CGImageRelease(partialImageRef);
}
dispatch_async(dispatch_get_main_queue(), ^
{
NSMutableArray *callbacksForURL = self.URLCallbacks[url];
[self.URLCallbacks removeObjectForKey:url];
for (NSDictionary *callbacks in callbacksForURL)
{
SDWebImageDownloaderCompletedBlock callback = callbacks[kCompletedCallbackKey];
if (callback) callback(image, error, finished);
}
});
}];
[self.downloadQueue addOperation:operation];
}
});
CFRelease(imageSource);
}
return operation;
}
#pragma GCC diagnostic ignored "-Wundeclared-selector"
- (void)connectionDidFinishLoading:(NSURLConnection *)aConnection
{
self.connection = nil;
[[NSNotificationCenter defaultCenter] postNotificationName:SDWebImageDownloadStopNotification object:nil];
if ([delegate respondsToSelector:@selector(imageDownloaderDidFinish:)])
{
[delegate performSelector:@selector(imageDownloaderDidFinish:) withObject:self];
}
if ([delegate respondsToSelector:@selector(imageDownloader:didFinishWithImage:)])
{
UIImage *image = SDScaledImageForPath(url.absoluteString, imageData);
[[SDWebImageDecoder sharedImageDecoder] decodeImage:image withDelegate:self userInfo:nil];
}
}
- (void)connection:(NSURLConnection *)connection didFailWithError:(NSError *)error
{
[[NSNotificationCenter defaultCenter] postNotificationName:SDWebImageDownloadStopNotification object:nil];
if ([delegate respondsToSelector:@selector(imageDownloader:didFailWithError:)])
{
[delegate performSelector:@selector(imageDownloader:didFailWithError:) withObject:self withObject:error];
}
self.connection = nil;
self.imageData = nil;
}
#pragma mark SDWebImageDecoderDelegate
- (void)imageDecoder:(SDWebImageDecoder *)decoder didFinishDecodingImage:(UIImage *)image userInfo:(NSDictionary *)aUserInfo
{
if ([[aUserInfo valueForKey:@"type"] isEqualToString:@"partial"])
{
[delegate imageDownloader:self didUpdatePartialImage:image];
}
else
{
[delegate performSelector:@selector(imageDownloader:didFinishWithImage:) withObject:self withObject:image];
}
}
#pragma mark NSObject
- (void)dealloc
{
[[NSNotificationCenter defaultCenter] removeObserver:self];
SDWISafeRelease(url);
SDWISafeRelease(connection);
SDWISafeRelease(imageData);
SDWISafeRelease(userInfo);
SDWISuperDealoc;
}
@end

View File

@ -1,46 +0,0 @@
/*
* This file is part of the SDWebImage package.
* (c) Olivier Poitrey <rs@dailymotion.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
#import "SDWebImageCompat.h"
@class SDWebImageDownloader;
/**
* Delegate protocol for SDWebImageDownloader
*/
@protocol SDWebImageDownloaderDelegate <NSObject>
@optional
- (void)imageDownloaderDidFinish:(SDWebImageDownloader *)downloader;
/**
* Called repeatedly while the image is downloading when [SDWebImageDownloader progressive] is enabled.
*
* @param downloader The SDWebImageDownloader instance
* @param image The partial image representing the currently download portion of the image
*/
- (void)imageDownloader:(SDWebImageDownloader *)downloader didUpdatePartialImage:(UIImage *)image;
/**
* Called when download completed successfuly.
*
* @param downloader The SDWebImageDownloader instance
* @param image The downloaded image object
*/
- (void)imageDownloader:(SDWebImageDownloader *)downloader didFinishWithImage:(UIImage *)image;
/**
* Called when an error occurred
*
* @param downloader The SDWebImageDownloader instance
* @param error The error details
*/
- (void)imageDownloader:(SDWebImageDownloader *)downloader didFailWithError:(NSError *)error;
@end

View File

@ -0,0 +1,23 @@
//
// SDWebImageDownloaderOperation.h
// SDWebImage
//
// Created by Olivier Poitrey on 04/11/12.
// Copyright (c) 2012 Dailymotion. All rights reserved.
//
#import <Foundation/Foundation.h>
#import "SDWebImageDownloader.h"
#import "SDWebImageOperation.h"
@interface SDWebImageDownloaderOperation : NSOperation <SDWebImageOperation>
@property (strong, nonatomic, readonly) NSURLRequest *request;
@property (assign, nonatomic, readonly) SDWebImageDownloaderOptions options;
- (id)initWithRequest:(NSURLRequest *)request
options:(SDWebImageDownloaderOptions)options
progress:(SDWebImageDownloaderProgressBlock)progressBlock
completed:(SDWebImageDownloaderCompletedBlock)completedBlock;
@end

View File

@ -0,0 +1,251 @@
//
// SDWebImageDownloaderOperation.m
// SDWebImage
//
// Created by Olivier Poitrey on 04/11/12.
// Copyright (c) 2012 Dailymotion. All rights reserved.
//
#import "SDWebImageDownloaderOperation.h"
#import "SDWebImageDecoder.h"
#import <ImageIO/ImageIO.h>
@interface SDWebImageDownloaderOperation ()
@property (strong, nonatomic) SDWebImageDownloaderProgressBlock progressBlock;
@property (strong, nonatomic) SDWebImageDownloaderCompletedBlock completedBlock;
@property (assign, nonatomic, getter = isExecuting) BOOL executing;
@property (assign, nonatomic, getter = isFinished) BOOL finished;
@property (assign, nonatomic) long long expectedSize;
@property (strong, nonatomic) NSMutableData *imageData;
@property (strong, nonatomic) NSURLConnection *connection;
@end
@implementation SDWebImageDownloaderOperation
{
size_t width, height;
}
- (id)initWithRequest:(NSURLRequest *)request options:(SDWebImageDownloaderOptions)options progress:(void (^)(NSUInteger, long long))progressBlock completed:(void (^)(UIImage *, NSError *, BOOL))completedBlock
{
if ((self = [super init]))
{
_request = request;
_options = options;
_progressBlock = progressBlock;
_completedBlock = completedBlock;
_executing = NO;
_finished = NO;
_expectedSize = 0;
}
return self;
}
- (void)start
{
if (self.isCancelled)
{
self.finished = YES;
return;
}
dispatch_async(dispatch_get_main_queue(), ^
{
self.connection = [NSURLConnection.alloc initWithRequest:self.request delegate:self startImmediately:NO];
[self.connection scheduleInRunLoop:[NSRunLoop currentRunLoop] forMode:NSRunLoopCommonModes];
[self.connection start];
self.executing = YES;
// If not in low priority mode, ensure we aren't blocked by UI manipulations (default runloop mode for NSURLConnection is NSEventTrackingRunLoopMode)
if (self.options & SDWebImageDownloaderLowPriority)
{
[self.connection scheduleInRunLoop:[NSRunLoop currentRunLoop] forMode:NSRunLoopCommonModes];
}
[self.connection start];
if (self.connection)
{
[[NSNotificationCenter defaultCenter] postNotificationName:SDWebImageDownloadStartNotification object:self];
}
else
{
if (self.completedBlock)
{
self.completedBlock(nil, [NSError errorWithDomain:NSURLErrorDomain code:0 userInfo:@{NSLocalizedDescriptionKey: @"Connection can't be initialized"}], NO);
}
}
});
}
- (void)cancel
{
if (self.isFinished) return;
[super cancel];
if (self.connection)
{
[self.connection cancel];
[[NSNotificationCenter defaultCenter] postNotificationName:SDWebImageDownloadStopNotification object:self];
// As we cancelled the connection, its callback won't be called and thus won't
// maintain the isFinished and isExecuting flags.
if (!self.isFinished) self.finished = YES;
if (self.isExecuting) self.executing = NO;
}
self.connection = nil;
self.imageData = nil;
}
- (void)done
{
self.finished = YES;
self.executing = NO;
self.completedBlock = nil;
self.progressBlock = nil;
self.connection = nil;
self.imageData = nil;
}
- (void)setFinished:(BOOL)finished
{
[self willChangeValueForKey:@"isFinished"];
_finished = finished;
[self didChangeValueForKey:@"isFinished"];
}
- (void)setExecuting:(BOOL)executing
{
[self willChangeValueForKey:@"isExecuting"];
_executing = executing;
[self didChangeValueForKey:@"isExecuting"];
}
- (BOOL)isConcurrent
{
return YES;
}
#pragma mark NSURLConnection (delegate)
- (void)connection:(NSURLConnection *)connection didReceiveResponse:(NSURLResponse *)response
{
if (![response respondsToSelector:@selector(statusCode)] || [((NSHTTPURLResponse *)response) statusCode] < 400)
{
self.expectedSize = response.expectedContentLength > 0 ? (NSUInteger)response.expectedContentLength : 0;
self.imageData = [NSMutableData.alloc initWithCapacity:self.expectedSize];
}
else
{
[self.connection cancel];
[[NSNotificationCenter defaultCenter] postNotificationName:SDWebImageDownloadStopNotification object:nil];
if (self.completedBlock)
{
self.completedBlock(nil, [NSError errorWithDomain:NSURLErrorDomain code:[((NSHTTPURLResponse *)response) statusCode] userInfo:nil], NO);
}
[self done];
}
}
- (void)connection:(NSURLConnection *)connection didReceiveData:(NSData *)data
{
[self.imageData appendData:data];
if ((self.options & SDWebImageDownloaderProgressiveDownload) && self.expectedSize > 0 && self.completedBlock)
{
// The following code is from http://www.cocoaintheshell.com/2011/05/progressive-images-download-imageio/
// Thanks to the author @Nyx0uf
// Get the total bytes downloaded
const NSUInteger totalSize = self.imageData.length;
// Update the data source, we must pass ALL the data, not just the new bytes
CGImageSourceRef imageSource = CGImageSourceCreateIncremental(NULL);
CGImageSourceUpdateData(imageSource, (__bridge CFDataRef)self.imageData, totalSize == self.expectedSize);
if (width + height == 0)
{
CFDictionaryRef properties = CGImageSourceCopyPropertiesAtIndex(imageSource, 0, NULL);
if (properties)
{
CFTypeRef val = CFDictionaryGetValue(properties, kCGImagePropertyPixelHeight);
if (val) CFNumberGetValue(val, kCFNumberLongType, &height);
val = CFDictionaryGetValue(properties, kCGImagePropertyPixelWidth);
if (val) CFNumberGetValue(val, kCFNumberLongType, &width);
CFRelease(properties);
}
}
if (width + height > 0 && totalSize < self.expectedSize)
{
// Create the image
CGImageRef partialImageRef = CGImageSourceCreateImageAtIndex(imageSource, 0, NULL);
#ifdef TARGET_OS_IPHONE
// Workaround for iOS anamorphic image
if (partialImageRef)
{
const size_t partialHeight = CGImageGetHeight(partialImageRef);
CGColorSpaceRef colorSpace = CGColorSpaceCreateDeviceRGB();
CGContextRef bmContext = CGBitmapContextCreate(NULL, width, height, 8, width * 4, colorSpace, kCGBitmapByteOrderDefault | kCGImageAlphaPremultipliedFirst);
CGColorSpaceRelease(colorSpace);
if (bmContext)
{
CGContextDrawImage(bmContext, (CGRect){.origin.x = 0.0f, .origin.y = 0.0f, .size.width = width, .size.height = partialHeight}, partialImageRef);
CGImageRelease(partialImageRef);
partialImageRef = CGBitmapContextCreateImage(bmContext);
CGContextRelease(bmContext);
}
else
{
CGImageRelease(partialImageRef);
partialImageRef = nil;
}
}
#endif
if (partialImageRef)
{
UIImage *image = [UIImage decodedImageWithImage:SDScaledImageForPath(self.request.URL.absoluteString, [UIImage imageWithCGImage:partialImageRef])];
CGImageRelease(partialImageRef);
self.completedBlock(image, nil, NO);
}
}
CFRelease(imageSource);
}
}
- (void)connectionDidFinishLoading:(NSURLConnection *)aConnection
{
self.connection = nil;
[[NSNotificationCenter defaultCenter] postNotificationName:SDWebImageDownloadStopNotification object:nil];
if (self.completedBlock)
{
UIImage *image = [UIImage decodedImageWithImage:SDScaledImageForPath(self.request.URL.absoluteString, self.imageData)];
self.completedBlock(image, nil, YES);
}
[self done];
}
- (void)connection:(NSURLConnection *)connection didFailWithError:(NSError *)error
{
[[NSNotificationCenter defaultCenter] postNotificationName:SDWebImageDownloadStopNotification object:nil];
if (self.completedBlock)
{
self.completedBlock(nil, error, NO);
}
[self done];
}
@end

View File

@ -7,9 +7,9 @@
*/
#import "SDWebImageCompat.h"
#import "SDWebImageDownloaderDelegate.h"
#import "SDWebImageManagerDelegate.h"
#import "SDImageCacheDelegate.h"
#import "SDWebImageOperation.h"
#import "SDWebImageDownloader.h"
#import "SDImageCache.h"
typedef enum
{
@ -19,10 +19,8 @@ typedef enum
SDWebImageProgressiveDownload = 1 << 3
} SDWebImageOptions;
#if NS_BLOCKS_AVAILABLE
typedef void(^SDWebImageSuccessBlock)(UIImage *image, BOOL cached);
typedef void(^SDWebImageFailureBlock)(NSError *error);
#endif
typedef void(^SDWebImageCompletedBlock)(UIImage *image, NSError *error, BOOL fromCache);
/**
* The SDWebImageManager is the class behind the UIImageView+WebCache category and likes.
@ -36,28 +34,22 @@ typedef void(^SDWebImageFailureBlock)(NSError *error);
* [manager downloadWithURL:imageURL
* delegate:self
* options:0
* success:^(UIImage *image, BOOL cached)
* {
* // do something with image
* }
* failure:nil];
* progress:nil
* completed:^(UIImage *image, NSError *error, BOOL fromCache)
* {
* if (image)
* {
* // do something with image
* }
* }];
*/
@interface SDWebImageManager : NSObject <SDWebImageDownloaderDelegate, SDImageCacheDelegate>
{
NSMutableArray *downloadInfo;
NSMutableArray *downloadDelegates;
NSMutableArray *downloaders;
NSMutableArray *cacheDelegates;
NSMutableArray *cacheURLs;
NSMutableDictionary *downloaderForURL;
NSMutableArray *failedURLs;
}
@interface SDWebImageManager : NSObject
#if NS_BLOCKS_AVAILABLE
typedef NSString *(^CacheKeyFilter)(NSURL *url);
@property (strong, nonatomic, readonly) SDImageCache *imageCache;
@property (strong, nonatomic, readonly) SDWebImageDownloader *imageDownloader;
/**
* The cache filter is a block used each time SDWebManager need to convert an URL into a cache key. This can
* The cache filter is a block used each time SDWebImageManager need to convert an URL into a cache key. This can
* be used to remove dynamic part of an image URL.
*
* The following example sets a filter in the application delegate that will remove any query-string from the
@ -69,28 +61,14 @@ typedef NSString *(^CacheKeyFilter)(NSURL *url);
* return [url absoluteString];
* }];
*/
@property (strong) CacheKeyFilter cacheKeyFilter;
#endif
@property (strong) NSString *(^cacheKeyFilter)(NSURL *url);
/**
* Returns global SDWebImageManager instance.
*
* @return SDWebImageManager shared instance
*/
+ (id)sharedManager;
- (UIImage *)imageWithURL:(NSURL *)url __attribute__ ((deprecated));
/**
* Downloads the image at the given URL if not present in cache or return the cached version otherwise.
*
* @param url The URL to the image
* @param delegate The delegate object used to send result back
* @see [SDWebImageManager downloadWithURL:delegate:options:userInfo:]
* @see [SDWebImageManager downloadWithURL:delegate:options:userInfo:success:failure:]
*/
- (void)downloadWithURL:(NSURL *)url delegate:(id<SDWebImageManagerDelegate>)delegate;
+ (SDWebImageManager *)sharedManager;
/**
* Downloads the image at the given URL if not present in cache or return the cached version otherwise.
@ -98,60 +76,18 @@ typedef NSString *(^CacheKeyFilter)(NSURL *url);
* @param url The URL to the image
* @param delegate The delegate object used to send result back
* @param options A mask to specify options to use for this request
* @see [SDWebImageManager downloadWithURL:delegate:options:userInfo:]
* @see [SDWebImageManager downloadWithURL:delegate:options:userInfo:success:failure:]
*/
- (void)downloadWithURL:(NSURL *)url delegate:(id<SDWebImageManagerDelegate>)delegate options:(SDWebImageOptions)options;
/**
* Downloads the image at the given URL if not present in cache or return the cached version otherwise.
* @param progressBlock A block called while image is downloading
* @param completedBlock A block called when operation has been completed. This block as no return value
* and takes the requested UIImage as first parameter. In case of error the image parameter
* is nil and the second parameter may contain an NSError. The third parameter is a Boolean
* indicating if the image was retrived from the local cache of from the network.
*
* @param url The URL to the image
* @param delegate The delegate object used to send result back
* @param options A mask to specify options to use for this request
* @param info An NSDictionnary passed back to delegate if provided
* @see [SDWebImageManager downloadWithURL:delegate:options:success:failure:]
* @return Returns a cancellable NSOperation
*/
- (void)downloadWithURL:(NSURL *)url delegate:(id<SDWebImageManagerDelegate>)delegate options:(SDWebImageOptions)options userInfo:(NSDictionary *)info;
// use options:SDWebImageRetryFailed instead
- (void)downloadWithURL:(NSURL *)url delegate:(id<SDWebImageManagerDelegate>)delegate retryFailed:(BOOL)retryFailed __attribute__ ((deprecated));
// use options:SDWebImageRetryFailed|SDWebImageLowPriority instead
- (void)downloadWithURL:(NSURL *)url delegate:(id<SDWebImageManagerDelegate>)delegate retryFailed:(BOOL)retryFailed lowPriority:(BOOL)lowPriority __attribute__ ((deprecated));
#if NS_BLOCKS_AVAILABLE
/**
* Downloads the image at the given URL if not present in cache or return the cached version otherwise.
*
* @param url The URL to the image
* @param delegate The delegate object used to send result back
* @param options A mask to specify options to use for this request
* @param success A block called when image has been retrived successfuly
* @param failure A block called when couldn't be retrived for some reason
* @see [SDWebImageManager downloadWithURL:delegate:options:]
*/
- (void)downloadWithURL:(NSURL *)url delegate:(id)delegate options:(SDWebImageOptions)options success:(SDWebImageSuccessBlock)success failure:(SDWebImageFailureBlock)failure;
/**
* Downloads the image at the given URL if not present in cache or return the cached version otherwise.
*
* @param url The URL to the image
* @param delegate The delegate object used to send result back
* @param options A mask to specify options to use for this request
* @param info An NSDictionnary passed back to delegate if provided
* @param success A block called when image has been retrived successfuly
* @param failure A block called when couldn't be retrived for some reason
* @see [SDWebImageManager downloadWithURL:delegate:options:]
*/
- (void)downloadWithURL:(NSURL *)url delegate:(id)delegate options:(SDWebImageOptions)options userInfo:(NSDictionary *)info success:(SDWebImageSuccessBlock)success failure:(SDWebImageFailureBlock)failure;
#endif
/**
* Cancel all pending download requests for a given delegate
*
* @param delegate The delegate to cancel requests for
*/
- (void)cancelForDelegate:(id<SDWebImageManagerDelegate>)delegate;
- (id<SDWebImageOperation>)downloadWithURL:(NSURL *)url
options:(SDWebImageOptions)options
progress:(SDWebImageDownloaderProgressBlock)progressBlock
completed:(SDWebImageCompletedBlock)completedBlock;
/**
* Cancel all current opreations

View File

@ -7,59 +7,49 @@
*/
#import "SDWebImageManager.h"
#import "SDImageCache.h"
#import "SDWebImageDownloader.h"
#import <objc/message.h>
static SDWebImageManager *instance;
@interface SDWebImageCombinedOperation : NSObject <SDWebImageOperation>
@property (assign, nonatomic, getter = isCancelled) BOOL cancelled;
@property (strong, nonatomic) void (^cancelBlock)();
@end
@interface SDWebImageManager ()
@property (strong, nonatomic, readwrite) SDImageCache *imageCache;
@property (strong, nonatomic, readwrite) SDWebImageDownloader *imageDownloader;
@property (strong, nonatomic) NSMutableArray *failedURLs;
@property (strong, nonatomic) NSMutableArray *runningOperations;
@end
@implementation SDWebImageManager
#if NS_BLOCKS_AVAILABLE
@synthesize cacheKeyFilter;
#endif
+ (id)sharedManager
{
static dispatch_once_t once;
static id instance;
dispatch_once(&once, ^{instance = self.new;});
return instance;
}
- (id)init
{
if ((self = [super init]))
{
downloadInfo = [[NSMutableArray alloc] init];
downloadDelegates = [[NSMutableArray alloc] init];
downloaders = [[NSMutableArray alloc] init];
cacheDelegates = [[NSMutableArray alloc] init];
cacheURLs = [[NSMutableArray alloc] init];
downloaderForURL = [[NSMutableDictionary alloc] init];
failedURLs = [[NSMutableArray alloc] init];
_imageCache = SDImageCache.new;
_imageDownloader = SDWebImageDownloader.new;
_failedURLs = NSMutableArray.new;
_runningOperations = NSMutableArray.new;
}
return self;
}
- (void)dealloc
{
SDWISafeRelease(downloadInfo);
SDWISafeRelease(downloadDelegates);
SDWISafeRelease(downloaders);
SDWISafeRelease(cacheDelegates);
SDWISafeRelease(cacheURLs);
SDWISafeRelease(downloaderForURL);
SDWISafeRelease(failedURLs);
SDWISuperDealoc;
}
+ (id)sharedManager
{
if (instance == nil)
{
instance = [[SDWebImageManager alloc] init];
}
return instance;
}
- (NSString *)cacheKeyForURL:(NSURL *)url
{
#if NS_BLOCKS_AVAILABLE
if (self.cacheKeyFilter)
{
return self.cacheKeyFilter(url);
@ -68,439 +58,101 @@ static SDWebImageManager *instance;
{
return [url absoluteString];
}
#else
return [url absoluteString];
#endif
}
/*
* @deprecated
*/
- (UIImage *)imageWithURL:(NSURL *)url
{
return [[SDImageCache sharedImageCache] imageFromKey:[self cacheKeyForURL:url]];
}
/*
* @deprecated
*/
- (void)downloadWithURL:(NSURL *)url delegate:(id<SDWebImageManagerDelegate>)delegate retryFailed:(BOOL)retryFailed
{
[self downloadWithURL:url delegate:delegate options:(retryFailed ? SDWebImageRetryFailed : 0)];
}
/*
* @deprecated
*/
- (void)downloadWithURL:(NSURL *)url delegate:(id<SDWebImageManagerDelegate>)delegate retryFailed:(BOOL)retryFailed lowPriority:(BOOL)lowPriority
{
SDWebImageOptions options = 0;
if (retryFailed) options |= SDWebImageRetryFailed;
if (lowPriority) options |= SDWebImageLowPriority;
[self downloadWithURL:url delegate:delegate options:options];
}
- (void)downloadWithURL:(NSURL *)url delegate:(id<SDWebImageManagerDelegate>)delegate
{
[self downloadWithURL:url delegate:delegate options:0];
}
- (void)downloadWithURL:(NSURL *)url delegate:(id<SDWebImageManagerDelegate>)delegate options:(SDWebImageOptions)options
{
[self downloadWithURL:url delegate:delegate options:options userInfo:nil];
}
- (void)downloadWithURL:(NSURL *)url delegate:(id<SDWebImageManagerDelegate>)delegate options:(SDWebImageOptions)options userInfo:(NSDictionary *)userInfo
{
- (id<SDWebImageOperation>)downloadWithURL:(NSURL *)url options:(SDWebImageOptions)options progress:(SDWebImageDownloaderProgressBlock)progressBlock completed:(SDWebImageCompletedBlock)completedBlock
{
// Very common mistake is to send the URL using NSString object instead of NSURL. For some strange reason, XCode won't
// throw any warning for this type mismatch. Here we failsafe this error by allowing URLs to be passed as NSString.
if ([url isKindOfClass:NSString.class])
{
url = [NSURL URLWithString:(NSString *)url];
}
else if (![url isKindOfClass:NSURL.class])
{
url = nil; // Prevent some common crashes due to common wrong values passed like NSNull.null for instance
}
if (!url || !delegate || (!(options & SDWebImageRetryFailed) && [failedURLs containsObject:url]))
{
return;
}
// Check the on-disk cache async so we don't block the main thread
[cacheDelegates addObject:delegate];
[cacheURLs addObject:url];
NSDictionary *info = [NSDictionary dictionaryWithObjectsAndKeys:
delegate, @"delegate",
url, @"url",
[NSNumber numberWithInt:options], @"options",
userInfo ? userInfo : [NSNull null], @"userInfo",
nil];
[[SDImageCache sharedImageCache] queryDiskCacheForKey:[self cacheKeyForURL:url] delegate:self userInfo:info];
}
#if NS_BLOCKS_AVAILABLE
- (void)downloadWithURL:(NSURL *)url delegate:(id)delegate options:(SDWebImageOptions)options success:(SDWebImageSuccessBlock)success failure:(SDWebImageFailureBlock)failure
{
[self downloadWithURL:url delegate:delegate options:options userInfo:nil success:success failure:failure];
}
- (void)downloadWithURL:(NSURL *)url delegate:(id)delegate options:(SDWebImageOptions)options userInfo:(NSDictionary *)userInfo success:(SDWebImageSuccessBlock)success failure:(SDWebImageFailureBlock)failure
{
// repeated logic from above due to requirement for backwards compatability for iOS versions without blocks
__block SDWebImageCombinedOperation *operation = SDWebImageCombinedOperation.new;
// Very common mistake is to send the URL using NSString object instead of NSURL. For some strange reason, XCode won't
// throw any warning for this type mismatch. Here we failsafe this error by allowing URLs to be passed as NSString.
if ([url isKindOfClass:NSString.class])
if (!url || !completedBlock || (!(options & SDWebImageRetryFailed) && [self.failedURLs containsObject:url]))
{
url = [NSURL URLWithString:(NSString *)url];
}
if (!url || !delegate || (!(options & SDWebImageRetryFailed) && [failedURLs containsObject:url]))
{
return;
}
// Check the on-disk cache async so we don't block the main thread
[cacheDelegates addObject:delegate];
[cacheURLs addObject:url];
SDWebImageSuccessBlock successCopy = [success copy];
SDWebImageFailureBlock failureCopy = [failure copy];
NSDictionary *info = [NSDictionary dictionaryWithObjectsAndKeys:
delegate, @"delegate",
url, @"url",
[NSNumber numberWithInt:options], @"options",
userInfo ? userInfo : [NSNull null], @"userInfo",
successCopy, @"success",
failureCopy, @"failure",
nil];
SDWIRelease(successCopy);
SDWIRelease(failureCopy);
[[SDImageCache sharedImageCache] queryDiskCacheForKey:[self cacheKeyForURL:url] delegate:self userInfo:info];
}
#endif
- (void)cancelForDelegate:(id<SDWebImageManagerDelegate>)delegate
{
NSUInteger idx;
while ((idx = [cacheDelegates indexOfObjectIdenticalTo:delegate]) != NSNotFound)
{
[cacheDelegates removeObjectAtIndex:idx];
[cacheURLs removeObjectAtIndex:idx];
if (completedBlock) completedBlock(nil, nil, NO);
return operation;
}
while ((idx = [downloadDelegates indexOfObjectIdenticalTo:delegate]) != NSNotFound)
[self.runningOperations addObject:operation];
NSString *key = [self cacheKeyForURL:url];
[self.imageCache queryDiskCacheForKey:key done:^(UIImage *image)
{
SDWebImageDownloader *downloader = SDWIReturnRetained([downloaders objectAtIndex:idx]);
if (operation.isCancelled) return;
[downloadInfo removeObjectAtIndex:idx];
[downloadDelegates removeObjectAtIndex:idx];
[downloaders removeObjectAtIndex:idx];
if (![downloaders containsObject:downloader])
if (image)
{
// No more delegate are waiting for this download, cancel it
[downloader cancel];
[downloaderForURL removeObjectForKey:downloader.url];
dispatch_async(dispatch_get_main_queue(), ^
{
completedBlock(image, nil, YES);
[self.runningOperations removeObject:operation];
});
}
else
{
SDWebImageDownloaderOptions downloaderOptions = 0;
if (options & SDWebImageLowPriority) downloaderOptions |= SDWebImageDownloaderLowPriority;
if (options & SDWebImageProgressiveDownload) downloaderOptions |= SDWebImageDownloaderProgressiveDownload;
id<SDWebImageOperation> subOperation = [self.imageDownloader downloadImageWithURL:url options:downloaderOptions progress:progressBlock completed:^(UIImage *downloadedImage, NSError *error, BOOL finished)
{
dispatch_async(dispatch_get_main_queue(), ^
{
if (error)
{
[self.failedURLs addObject:url];
}
completedBlock(downloadedImage, error, NO);
[self.runningOperations removeObject:operation];
if (downloadedImage)
{
[self.imageCache storeImage:downloadedImage forKey:key];
}
});
}];
operation.cancelBlock = ^{[subOperation cancel];};
}
}];
SDWIRelease(downloader);
}
return operation;
}
- (void)cancelAll
{
for (SDWebImageDownloader *downloader in downloaders)
dispatch_async(dispatch_get_main_queue(), ^
{
[downloader cancel];
}
[cacheDelegates removeAllObjects];
[cacheURLs removeAllObjects];
[downloadInfo removeAllObjects];
[downloadDelegates removeAllObjects];
[downloaders removeAllObjects];
[downloaderForURL removeAllObjects];
}
#pragma mark SDImageCacheDelegate
- (NSUInteger)indexOfDelegate:(id<SDWebImageManagerDelegate>)delegate waitingForURL:(NSURL *)url
{
// Do a linear search, simple (even if inefficient)
NSUInteger idx;
for (idx = 0; idx < [cacheDelegates count]; idx++)
{
if ([cacheDelegates objectAtIndex:idx] == delegate && [[cacheURLs objectAtIndex:idx] isEqual:url])
{
return idx;
}
}
return NSNotFound;
}
- (void)imageCache:(SDImageCache *)imageCache didFindImage:(UIImage *)image forKey:(NSString *)key userInfo:(NSDictionary *)info
{
NSURL *url = [info objectForKey:@"url"];
id<SDWebImageManagerDelegate> delegate = [info objectForKey:@"delegate"];
NSUInteger idx = [self indexOfDelegate:delegate waitingForURL:url];
if (idx == NSNotFound)
{
// Request has since been canceled
return;
}
if ([delegate respondsToSelector:@selector(webImageManager:didFinishWithImage:)])
{
[delegate performSelector:@selector(webImageManager:didFinishWithImage:) withObject:self withObject:image];
}
if ([delegate respondsToSelector:@selector(webImageManager:didFinishWithImage:forURL:)])
{
objc_msgSend(delegate, @selector(webImageManager:didFinishWithImage:forURL:), self, image, url);
}
if ([delegate respondsToSelector:@selector(webImageManager:didFinishWithImage:forURL:userInfo:)])
{
NSDictionary *userInfo = [info objectForKey:@"userInfo"];
if ([userInfo isKindOfClass:NSNull.class])
{
userInfo = nil;
}
objc_msgSend(delegate, @selector(webImageManager:didFinishWithImage:forURL:userInfo:), self, image, url, userInfo);
}
#if NS_BLOCKS_AVAILABLE
if ([info objectForKey:@"success"])
{
SDWebImageSuccessBlock success = [info objectForKey:@"success"];
success(image, YES);
}
#endif
[cacheDelegates removeObjectAtIndex:idx];
[cacheURLs removeObjectAtIndex:idx];
}
- (void)imageCache:(SDImageCache *)imageCache didNotFindImageForKey:(NSString *)key userInfo:(NSDictionary *)info
{
NSURL *url = [info objectForKey:@"url"];
id<SDWebImageManagerDelegate> delegate = [info objectForKey:@"delegate"];
SDWebImageOptions options = [[info objectForKey:@"options"] intValue];
NSUInteger idx = [self indexOfDelegate:delegate waitingForURL:url];
if (idx == NSNotFound)
{
// Request has since been canceled
return;
}
[cacheDelegates removeObjectAtIndex:idx];
[cacheURLs removeObjectAtIndex:idx];
// Share the same downloader for identical URLs so we don't download the same URL several times
SDWebImageDownloader *downloader = [downloaderForURL objectForKey:url];
if (!downloader)
{
downloader = [SDWebImageDownloader downloaderWithURL:url delegate:self userInfo:info lowPriority:(options & SDWebImageLowPriority)];
[downloaderForURL setObject:downloader forKey:url];
}
else
{
// Reuse shared downloader
downloader.lowPriority = (options & SDWebImageLowPriority);
}
if ((options & SDWebImageProgressiveDownload) && !downloader.progressive)
{
// Turn progressive download support on demand
downloader.progressive = YES;
}
[downloadInfo addObject:info];
[downloadDelegates addObject:delegate];
[downloaders addObject:downloader];
}
#pragma mark SDWebImageDownloaderDelegate
- (void)imageDownloader:(SDWebImageDownloader *)downloader didUpdatePartialImage:(UIImage *)image
{
// Notify all the downloadDelegates with this downloader
for (NSInteger idx = (NSInteger)[downloaders count] - 1; idx >= 0; idx--)
{
NSUInteger uidx = (NSUInteger)idx;
SDWebImageDownloader *aDownloader = [downloaders objectAtIndex:uidx];
if (aDownloader == downloader)
{
id<SDWebImageManagerDelegate> delegate = [downloadDelegates objectAtIndex:uidx];
SDWIRetain(delegate);
SDWIAutorelease(delegate);
if ([delegate respondsToSelector:@selector(webImageManager:didProgressWithPartialImage:forURL:)])
{
objc_msgSend(delegate, @selector(webImageManager:didProgressWithPartialImage:forURL:), self, image, downloader.url);
}
if ([delegate respondsToSelector:@selector(webImageManager:didProgressWithPartialImage:forURL:userInfo:)])
{
NSDictionary *userInfo = [[downloadInfo objectAtIndex:uidx] objectForKey:@"userInfo"];
if ([userInfo isKindOfClass:NSNull.class])
{
userInfo = nil;
}
objc_msgSend(delegate, @selector(webImageManager:didProgressWithPartialImage:forURL:userInfo:), self, image, downloader.url, userInfo);
}
}
}
}
- (void)imageDownloader:(SDWebImageDownloader *)downloader didFinishWithImage:(UIImage *)image
{
SDWIRetain(downloader);
SDWebImageOptions options = [[downloader.userInfo objectForKey:@"options"] intValue];
// Notify all the downloadDelegates with this downloader
for (NSInteger idx = (NSInteger)[downloaders count] - 1; idx >= 0; idx--)
{
NSUInteger uidx = (NSUInteger)idx;
SDWebImageDownloader *aDownloader = [downloaders objectAtIndex:uidx];
if (aDownloader == downloader)
{
id<SDWebImageManagerDelegate> delegate = [downloadDelegates objectAtIndex:uidx];
SDWIRetain(delegate);
SDWIAutorelease(delegate);
if (image)
{
if ([delegate respondsToSelector:@selector(webImageManager:didFinishWithImage:)])
{
[delegate performSelector:@selector(webImageManager:didFinishWithImage:) withObject:self withObject:image];
}
if ([delegate respondsToSelector:@selector(webImageManager:didFinishWithImage:forURL:)])
{
objc_msgSend(delegate, @selector(webImageManager:didFinishWithImage:forURL:), self, image, downloader.url);
}
if ([delegate respondsToSelector:@selector(webImageManager:didFinishWithImage:forURL:userInfo:)])
{
NSDictionary *userInfo = [[downloadInfo objectAtIndex:uidx] objectForKey:@"userInfo"];
if ([userInfo isKindOfClass:NSNull.class])
{
userInfo = nil;
}
objc_msgSend(delegate, @selector(webImageManager:didFinishWithImage:forURL:userInfo:), self, image, downloader.url, userInfo);
}
#if NS_BLOCKS_AVAILABLE
if ([[downloadInfo objectAtIndex:uidx] objectForKey:@"success"])
{
SDWebImageSuccessBlock success = [[downloadInfo objectAtIndex:uidx] objectForKey:@"success"];
success(image, NO);
}
#endif
}
else
{
if ([delegate respondsToSelector:@selector(webImageManager:didFailWithError:)])
{
[delegate performSelector:@selector(webImageManager:didFailWithError:) withObject:self withObject:nil];
}
if ([delegate respondsToSelector:@selector(webImageManager:didFailWithError:forURL:)])
{
objc_msgSend(delegate, @selector(webImageManager:didFailWithError:forURL:), self, nil, downloader.url);
}
if ([delegate respondsToSelector:@selector(webImageManager:didFailWithError:forURL:userInfo:)])
{
NSDictionary *userInfo = [[downloadInfo objectAtIndex:uidx] objectForKey:@"userInfo"];
if ([userInfo isKindOfClass:NSNull.class])
{
userInfo = nil;
}
objc_msgSend(delegate, @selector(webImageManager:didFailWithError:forURL:userInfo:), self, nil, downloader.url, userInfo);
}
#if NS_BLOCKS_AVAILABLE
if ([[downloadInfo objectAtIndex:uidx] objectForKey:@"failure"])
{
SDWebImageFailureBlock failure = [[downloadInfo objectAtIndex:uidx] objectForKey:@"failure"];
failure(nil);
}
#endif
}
[downloaders removeObjectAtIndex:uidx];
[downloadInfo removeObjectAtIndex:uidx];
[downloadDelegates removeObjectAtIndex:uidx];
}
}
if (image)
{
// Store the image in the cache
[[SDImageCache sharedImageCache] storeImage:image
imageData:downloader.imageData
forKey:[self cacheKeyForURL:downloader.url]
toDisk:!(options & SDWebImageCacheMemoryOnly)];
}
else if (!(options & SDWebImageRetryFailed))
{
// The image can't be downloaded from this URL, mark the URL as failed so we won't try and fail again and again
// (do this only if SDWebImageRetryFailed isn't activated)
[failedURLs addObject:downloader.url];
}
// Release the downloader
[downloaderForURL removeObjectForKey:downloader.url];
SDWIRelease(downloader);
}
- (void)imageDownloader:(SDWebImageDownloader *)downloader didFailWithError:(NSError *)error;
{
SDWIRetain(downloader);
// Notify all the downloadDelegates with this downloader
for (NSInteger idx = (NSInteger)[downloaders count] - 1; idx >= 0; idx--)
{
NSUInteger uidx = (NSUInteger)idx;
SDWebImageDownloader *aDownloader = [downloaders objectAtIndex:uidx];
if (aDownloader == downloader)
{
id<SDWebImageManagerDelegate> delegate = [downloadDelegates objectAtIndex:uidx];
SDWIRetain(delegate);
SDWIAutorelease(delegate);
if ([delegate respondsToSelector:@selector(webImageManager:didFailWithError:)])
{
[delegate performSelector:@selector(webImageManager:didFailWithError:) withObject:self withObject:error];
}
if ([delegate respondsToSelector:@selector(webImageManager:didFailWithError:forURL:)])
{
objc_msgSend(delegate, @selector(webImageManager:didFailWithError:forURL:), self, error, downloader.url);
}
if ([delegate respondsToSelector:@selector(webImageManager:didFailWithError:forURL:userInfo:)])
{
NSDictionary *userInfo = [[downloadInfo objectAtIndex:uidx] objectForKey:@"userInfo"];
if ([userInfo isKindOfClass:NSNull.class])
{
userInfo = nil;
}
objc_msgSend(delegate, @selector(webImageManager:didFailWithError:forURL:userInfo:), self, error, downloader.url, userInfo);
}
#if NS_BLOCKS_AVAILABLE
if ([[downloadInfo objectAtIndex:uidx] objectForKey:@"failure"])
{
SDWebImageFailureBlock failure = [[downloadInfo objectAtIndex:uidx] objectForKey:@"failure"];
failure(error);
}
#endif
[downloaders removeObjectAtIndex:uidx];
[downloadInfo removeObjectAtIndex:uidx];
[downloadDelegates removeObjectAtIndex:uidx];
}
}
// Release the downloader
[downloaderForURL removeObjectForKey:downloader.url];
SDWIRelease(downloader);
[self.runningOperations makeObjectsPerformSelector:@selector(cancel)];
[self.runningOperations removeAllObjects];
});
}
@end
@implementation SDWebImageCombinedOperation
- (void)setCancelBlock:(void (^)())cancelBlock
{
if (self.isCancelled)
{
if (cancelBlock) cancelBlock();
}
else
{
_cancelBlock = cancelBlock;
}
}
- (void)cancel
{
self.cancelled = YES;
if (self.cancelBlock)
{
self.cancelBlock();
self.cancelBlock = nil;
}
}
@end

View File

@ -1,55 +0,0 @@
/*
* This file is part of the SDWebImage package.
* (c) Olivier Poitrey <rs@dailymotion.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
@class SDWebImageManager;
@class UIImage;
/**
* Delegate protocol for SDWebImageManager
*/
@protocol SDWebImageManagerDelegate <NSObject>
@optional
/**
* Called while an image is downloading with an partial image object representing the currently downloaded portion of the image.
* This delegate is called only if ImageIO is available and `SDWebImageProgressiveDownload` option has been used.
*
* @param imageManager The image manager
* @param image The retrived image object
* @param url The image URL used to retrive the image
* @param info The user info dictionnary
*/
- (void)webImageManager:(SDWebImageManager *)imageManager didProgressWithPartialImage:(UIImage *)image forURL:(NSURL *)url userInfo:(NSDictionary *)info;
- (void)webImageManager:(SDWebImageManager *)imageManager didProgressWithPartialImage:(UIImage *)image forURL:(NSURL *)url;
/**
* Called when image download is completed successfuly.
*
* @param imageManager The image manager
* @param image The retrived image object
* @param url The image URL used to retrive the image
* @param info The user info dictionnary
*/
- (void)webImageManager:(SDWebImageManager *)imageManager didFinishWithImage:(UIImage *)image forURL:(NSURL *)url userInfo:(NSDictionary *)info;
- (void)webImageManager:(SDWebImageManager *)imageManager didFinishWithImage:(UIImage *)image forURL:(NSURL *)url;
- (void)webImageManager:(SDWebImageManager *)imageManager didFinishWithImage:(UIImage *)image;
/**
* Called when an error occurred.
*
* @param imageManager The image manager
* @param error The error
* @param url The image URL used to retrive the image
* @param info The user info dictionnary
*/
- (void)webImageManager:(SDWebImageManager *)imageManager didFailWithError:(NSError *)error forURL:(NSURL *)url userInfo:(NSDictionary *)info;
- (void)webImageManager:(SDWebImageManager *)imageManager didFailWithError:(NSError *)error forURL:(NSURL *)url;
- (void)webImageManager:(SDWebImageManager *)imageManager didFailWithError:(NSError *)error;
@end

View File

@ -0,0 +1,15 @@
//
// SDWebImageOperation.h
// SDWebImage
//
// Created by Olivier Poitrey on 04/11/12.
// Copyright (c) 2012 Dailymotion. All rights reserved.
//
#import <Foundation/Foundation.h>
@protocol SDWebImageOperation <NSObject>
- (void)cancel;
@end

View File

@ -7,20 +7,12 @@
*/
#import <Foundation/Foundation.h>
#import "SDWebImageManagerDelegate.h"
#import "SDWebImageManager.h"
/**
* Prefetch some URLs in the cache for future use. Images are downloaded in low priority.
*/
@interface SDWebImagePrefetcher : NSObject <SDWebImageManagerDelegate>
{
NSArray *_prefetchURLs;
NSUInteger _skippedCount;
NSUInteger _finishedCount;
NSUInteger _requestedCount;
NSTimeInterval _startedTime;
}
@interface SDWebImagePrefetcher : NSObject
/**
* Maximum number of URLs to prefetch at the same time. Defaults to 3.

View File

@ -10,105 +10,96 @@
#import "SDWebImageManager.h"
@interface SDWebImagePrefetcher ()
@property (nonatomic, retain) NSArray *prefetchURLs;
@property (strong, nonatomic) SDWebImageManager *manager;
@property (strong, nonatomic) NSArray *prefetchURLs;
@property (assign, nonatomic) NSUInteger requestedCount;
@property (assign, nonatomic) NSUInteger skippedCount;
@property (assign, nonatomic) NSUInteger finishedCount;
@property (assign, nonatomic) NSTimeInterval startedTime;
@end
@implementation SDWebImagePrefetcher
static SDWebImagePrefetcher *instance;
@synthesize prefetchURLs;
@synthesize maxConcurrentDownloads;
@synthesize options;
+ (SDWebImagePrefetcher *)sharedImagePrefetcher
{
if (instance == nil)
{
instance = [[SDWebImagePrefetcher alloc] init];
instance.maxConcurrentDownloads = 3;
instance.options = (SDWebImageLowPriority);
}
static dispatch_once_t once;
static id instance;
dispatch_once(&once, ^{instance = self.new;});
return instance;
}
- (void)startPrefetchingAtIndex:(NSUInteger)index withManager:(SDWebImageManager *)imageManager
- (id)init
{
if (index >= [self.prefetchURLs count]) return;
_requestedCount++;
[imageManager downloadWithURL:[self.prefetchURLs objectAtIndex:index] delegate:self options:self.options];
if ((self = [super init]))
{
_manager = SDWebImageManager.new;
_manager.imageDownloader.maxConcurrentDownloads = 3;
_options = SDWebImageLowPriority;
}
return self;
}
- (void)startPrefetchingAtIndex:(NSUInteger)index
{
if (index >= self.prefetchURLs.count) return;
self.requestedCount++;
[self.manager downloadWithURL:self.prefetchURLs[index] options:self.options progress:nil completed:^(UIImage *image, NSError *error, BOOL fromCache)
{
self.finishedCount++;
if (image)
{
NSLog(@"Prefetched %d out of %d", self.finishedCount, self.prefetchURLs.count);
}
else
{
NSLog(@"Prefetched %d out of %d (Failed)", self.finishedCount, [self.prefetchURLs count]);
// Add last failed
self.skippedCount++;
}
if (self.prefetchURLs.count > self.requestedCount)
{
[self startPrefetchingAtIndex:self.requestedCount];
}
else if (self.finishedCount == self.requestedCount)
{
[self reportStatus];
}
}];
}
- (void)reportStatus
{
NSUInteger total = [self.prefetchURLs count];
NSLog(@"Finished prefetching (%d successful, %d skipped, timeElasped %.2f)", total - _skippedCount, _skippedCount, CFAbsoluteTimeGetCurrent() - _startedTime);
NSLog(@"Finished prefetching (%d successful, %d skipped, timeElasped %.2f)", total - self.skippedCount, self.skippedCount, CFAbsoluteTimeGetCurrent() - self.startedTime);
}
- (void)prefetchURLs:(NSArray *)urls
{
[self cancelPrefetching]; // Prevent duplicate prefetch request
_startedTime = CFAbsoluteTimeGetCurrent();
self.startedTime = CFAbsoluteTimeGetCurrent();
self.prefetchURLs = urls;
// Starts prefetching from the very first image on the list with the max allowed concurrency
NSUInteger listCount = [self.prefetchURLs count];
SDWebImageManager *manager = [SDWebImageManager sharedManager];
for (NSUInteger i = 0; i < self.maxConcurrentDownloads && _requestedCount < listCount; i++)
NSUInteger listCount = self.prefetchURLs.count;
for (NSUInteger i = 0; i < self.maxConcurrentDownloads && self.requestedCount < listCount; i++)
{
[self startPrefetchingAtIndex:i withManager:manager];
[self startPrefetchingAtIndex:i];
}
}
- (void)cancelPrefetching
{
self.prefetchURLs = nil;
_skippedCount = 0;
_requestedCount = 0;
_finishedCount = 0;
[[SDWebImageManager sharedManager] cancelForDelegate:self];
}
#pragma mark SDWebImagePrefetcher (SDWebImageManagerDelegate)
- (void)webImageManager:(SDWebImageManager *)imageManager didFinishWithImage:(UIImage *)image
{
_finishedCount++;
NSLog(@"Prefetched %d out of %d", _finishedCount, [self.prefetchURLs count]);
if ([self.prefetchURLs count] > _requestedCount)
{
[self startPrefetchingAtIndex:_requestedCount withManager:imageManager];
}
else if (_finishedCount == _requestedCount)
{
[self reportStatus];
}
}
- (void)webImageManager:(SDWebImageManager *)imageManager didFailWithError:(NSError *)error
{
_finishedCount++;
NSLog(@"Prefetched %d out of %d (Failed)", _finishedCount, [self.prefetchURLs count]);
// Add last failed
_skippedCount++;
if ([self.prefetchURLs count] > _requestedCount)
{
[self startPrefetchingAtIndex:_requestedCount withManager:imageManager];
}
else if (_finishedCount == _requestedCount)
{
[self reportStatus];
}
}
- (void)dealloc
{
self.prefetchURLs = nil;
SDWISuperDealoc;
self.skippedCount = 0;
self.requestedCount = 0;
self.finishedCount = 0;
[self.manager cancelAll];
}
@end

View File

@ -7,13 +7,12 @@
*/
#import "SDWebImageCompat.h"
#import "SDWebImageManagerDelegate.h"
#import "SDWebImageManager.h"
/**
* Integrates SDWebImage async downloading and caching of remote images with UIButtonView.
*/
@interface UIButton (WebCache) <SDWebImageManagerDelegate>
@interface UIButton (WebCache)
/**
* Set the imageView `image` with an `url`.
@ -21,8 +20,9 @@
* The downloand is asynchronous and cached.
*
* @param url The url for the image.
* @param state The state that uses the specified title. The values are described in UIControlState.
*/
- (void)setImageWithURL:(NSURL *)url;
- (void)setImageWithURL:(NSURL *)url forState:(UIControlState)state;
/**
* Set the imageView `image` with an `url` and a placeholder.
@ -30,10 +30,11 @@
* The downloand is asynchronous and cached.
*
* @param url The url for the image.
* @param state The state that uses the specified title. The values are described in UIControlState.
* @param placeholder The image to be set initially, until the image request finishes.
* @see setImageWithURL:placeholderImage:options:
*/
- (void)setImageWithURL:(NSURL *)url placeholderImage:(UIImage *)placeholder;
- (void)setImageWithURL:(NSURL *)url forState:(UIControlState)state placeholderImage:(UIImage *)placeholder;
/**
* Set the imageView `image` with an `url`, placeholder and custom options.
@ -41,22 +42,25 @@
* The downloand is asynchronous and cached.
*
* @param url The url for the image.
* @param state The state that uses the specified title. The values are described in UIControlState.
* @param placeholder The image to be set initially, until the image request finishes.
* @param options The options to use when downloading the image. @see SDWebImageOptions for the possible values.
*/
- (void)setImageWithURL:(NSURL *)url placeholderImage:(UIImage *)placeholder options:(SDWebImageOptions)options;
- (void)setImageWithURL:(NSURL *)url forState:(UIControlState)state placeholderImage:(UIImage *)placeholder options:(SDWebImageOptions)options;
#if NS_BLOCKS_AVAILABLE
/**
* Set the imageView `image` with an `url`.
*
* The downloand is asynchronous and cached.
*
* @param url The url for the image.
* @param success A block to be executed when the image request succeed This block has no return value and takes the retrieved image as argument.
* @param failure A block object to be executed when the image request failed. This block has no return value and takes the error object describing the network or parsing error that occurred (may be nil).
* @param state The state that uses the specified title. The values are described in UIControlState.
* @param completedBlock A block called when operation has been completed. This block as no return value
* and takes the requested UIImage as first parameter. In case of error the image parameter
* is nil and the second parameter may contain an NSError. The third parameter is a Boolean
* indicating if the image was retrived from the local cache of from the network.
*/
- (void)setImageWithURL:(NSURL *)url success:(SDWebImageSuccessBlock)success failure:(SDWebImageFailureBlock)failure;
- (void)setImageWithURL:(NSURL *)url forState:(UIControlState)state completed:(SDWebImageCompletedBlock)completedBlock;
/**
* Set the imageView `image` with an `url`, placeholder.
@ -64,11 +68,14 @@
* The downloand is asynchronous and cached.
*
* @param url The url for the image.
* @param state The state that uses the specified title. The values are described in UIControlState.
* @param placeholder The image to be set initially, until the image request finishes.
* @param success A block to be executed when the image request succeed This block has no return value and takes the retrieved image as argument.
* @param failure A block object to be executed when the image request failed. This block has no return value and takes the error object describing the network or parsing error that occurred (may be nil).
* @param completedBlock A block called when operation has been completed. This block as no return value
* and takes the requested UIImage as first parameter. In case of error the image parameter
* is nil and the second parameter may contain an NSError. The third parameter is a Boolean
* indicating if the image was retrived from the local cache of from the network.
*/
- (void)setImageWithURL:(NSURL *)url placeholderImage:(UIImage *)placeholder success:(SDWebImageSuccessBlock)success failure:(SDWebImageFailureBlock)failure;
- (void)setImageWithURL:(NSURL *)url forState:(UIControlState)state placeholderImage:(UIImage *)placeholder completed:(SDWebImageCompletedBlock)completedBlock;
/**
* Set the imageView `image` with an `url`, placeholder and custom options.
@ -76,13 +83,15 @@
* The downloand is asynchronous and cached.
*
* @param url The url for the image.
* @param state The state that uses the specified title. The values are described in UIControlState.
* @param placeholder The image to be set initially, until the image request finishes.
* @param options The options to use when downloading the image. @see SDWebImageOptions for the possible values.
* @param success A block to be executed when the image request succeed This block has no return value and takes the retrieved image as argument.
* @param failure A block object to be executed when the image request failed. This block has no return value and takes the error object describing the network or parsing error that occurred (may be nil).
* @param completedBlock A block called when operation has been completed. This block as no return value
* and takes the requested UIImage as first parameter. In case of error the image parameter
* is nil and the second parameter may contain an NSError. The third parameter is a Boolean
* indicating if the image was retrived from the local cache of from the network.
*/
- (void)setImageWithURL:(NSURL *)url placeholderImage:(UIImage *)placeholder options:(SDWebImageOptions)options success:(SDWebImageSuccessBlock)success failure:(SDWebImageFailureBlock)failure;
#endif
- (void)setImageWithURL:(NSURL *)url forState:(UIControlState)state placeholderImage:(UIImage *)placeholder options:(SDWebImageOptions)options completed:(SDWebImageCompletedBlock)completedBlock;
/**
* Set the backgroundImageView `image` with an `url`.
@ -90,8 +99,9 @@
* The downloand is asynchronous and cached.
*
* @param url The url for the image.
* @param state The state that uses the specified title. The values are described in UIControlState.
*/
- (void)setBackgroundImageWithURL:(NSURL *)url;
- (void)setBackgroundImageWithURL:(NSURL *)url forState:(UIControlState)state;
/**
* Set the backgroundImageView `image` with an `url` and a placeholder.
@ -99,10 +109,11 @@
* The downloand is asynchronous and cached.
*
* @param url The url for the image.
* @param state The state that uses the specified title. The values are described in UIControlState.
* @param placeholder The image to be set initially, until the image request finishes.
* @see setImageWithURL:placeholderImage:options:
*/
- (void)setBackgroundImageWithURL:(NSURL *)url placeholderImage:(UIImage *)placeholder;
- (void)setBackgroundImageWithURL:(NSURL *)url forState:(UIControlState)state placeholderImage:(UIImage *)placeholder;
/**
* Set the backgroundImageView `image` with an `url`, placeholder and custom options.
@ -110,22 +121,23 @@
* The downloand is asynchronous and cached.
*
* @param url The url for the image.
* @param state The state that uses the specified title. The values are described in UIControlState.
* @param placeholder The image to be set initially, until the image request finishes.
* @param options The options to use when downloading the image. @see SDWebImageOptions for the possible values.
*/
- (void)setBackgroundImageWithURL:(NSURL *)url placeholderImage:(UIImage *)placeholder options:(SDWebImageOptions)options;
- (void)setBackgroundImageWithURL:(NSURL *)url forState:(UIControlState)state placeholderImage:(UIImage *)placeholder options:(SDWebImageOptions)options;
#if NS_BLOCKS_AVAILABLE
/**
* Set the backgroundImageView `image` with an `url`.
*
* The downloand is asynchronous and cached.
*
* @param url The url for the image.
* @param state The state that uses the specified title. The values are described in UIControlState.
* @param success A block to be executed when the image request succeed This block has no return value and takes the retrieved image as argument.
* @param failure A block object to be executed when the image request failed. This block has no return value and takes the error object describing the network or parsing error that occurred (may be nil).
*/
- (void)setBackgroundImageWithURL:(NSURL *)url success:(SDWebImageSuccessBlock)success failure:(SDWebImageFailureBlock)failure;
- (void)setBackgroundImageWithURL:(NSURL *)url forState:(UIControlState)state completed:(SDWebImageCompletedBlock)completedBlock;
/**
* Set the backgroundImageView `image` with an `url`, placeholder.
@ -133,11 +145,12 @@
* The downloand is asynchronous and cached.
*
* @param url The url for the image.
* @param state The state that uses the specified title. The values are described in UIControlState.
* @param placeholder The image to be set initially, until the image request finishes.
* @param success A block to be executed when the image request succeed This block has no return value and takes the retrieved image as argument.
* @param failure A block object to be executed when the image request failed. This block has no return value and takes the error object describing the network or parsing error that occurred (may be nil).
*/
- (void)setBackgroundImageWithURL:(NSURL *)url placeholderImage:(UIImage *)placeholder success:(SDWebImageSuccessBlock)success failure:(SDWebImageFailureBlock)failure;
- (void)setBackgroundImageWithURL:(NSURL *)url forState:(UIControlState)state placeholderImage:(UIImage *)placeholder completed:(SDWebImageCompletedBlock)completedBlock;
/**
* Set the backgroundImageView `image` with an `url`, placeholder and custom options.
@ -150,9 +163,7 @@
* @param success A block to be executed when the image request succeed This block has no return value and takes the retrieved image as argument.
* @param failure A block object to be executed when the image request failed. This block has no return value and takes the error object describing the network or parsing error that occurred (may be nil).
*/
- (void)setBackgroundImageWithURL:(NSURL *)url placeholderImage:(UIImage *)placeholder options:(SDWebImageOptions)options success:(SDWebImageSuccessBlock)success failure:(SDWebImageFailureBlock)failure;
#endif
- (void)setBackgroundImageWithURL:(NSURL *)url forState:(UIControlState)state placeholderImage:(UIImage *)placeholder options:(SDWebImageOptions)options completed:(SDWebImageCompletedBlock)completedBlock;
/**
* Cancel the current download

View File

@ -7,161 +7,104 @@
*/
#import "UIButton+WebCache.h"
#import "SDWebImageManager.h"
#import "objc/runtime.h"
static char operationKey;
@implementation UIButton (WebCache)
- (void)setImageWithURL:(NSURL *)url
- (void)setImageWithURL:(NSURL *)url forState:(UIControlState)state
{
[self setImageWithURL:url placeholderImage:nil];
[self setImageWithURL:url forState:state placeholderImage:nil options:0 completed:nil];
}
- (void)setImageWithURL:(NSURL *)url placeholderImage:(UIImage *)placeholder
- (void)setImageWithURL:(NSURL *)url forState:(UIControlState)state placeholderImage:(UIImage *)placeholder
{
[self setImageWithURL:url placeholderImage:placeholder options:0];
[self setImageWithURL:url forState:state placeholderImage:placeholder options:0 completed:nil];
}
- (void)setImageWithURL:(NSURL *)url placeholderImage:(UIImage *)placeholder options:(SDWebImageOptions)options
- (void)setImageWithURL:(NSURL *)url forState:(UIControlState)state placeholderImage:(UIImage *)placeholder options:(SDWebImageOptions)options
{
SDWebImageManager *manager = [SDWebImageManager sharedManager];
[self setImageWithURL:url forState:state placeholderImage:placeholder options:options completed:nil];
}
// Remove in progress downloader from queue
[manager cancelForDelegate:self];
- (void)setImageWithURL:(NSURL *)url forState:(UIControlState)state completed:(SDWebImageCompletedBlock)completedBlock
{
[self setImageWithURL:url forState:state placeholderImage:nil options:0 completed:completedBlock];
}
- (void)setImageWithURL:(NSURL *)url forState:(UIControlState)state placeholderImage:(UIImage *)placeholder completed:(SDWebImageCompletedBlock)completedBlock
{
[self setImageWithURL:url forState:state placeholderImage:placeholder options:0 completed:completedBlock];
}
[self setImage:placeholder forState:UIControlStateNormal];
[self setImage:placeholder forState:UIControlStateSelected];
[self setImage:placeholder forState:UIControlStateHighlighted];
- (void)setImageWithURL:(NSURL *)url forState:(UIControlState)state placeholderImage:(UIImage *)placeholder options:(SDWebImageOptions)options completed:(SDWebImageCompletedBlock)completedBlock
{
[self cancelCurrentImageLoad];
[self setImage:placeholder forState:state];
if (url)
{
[manager downloadWithURL:url delegate:self options:options];
id<SDWebImageOperation> operation = [SDWebImageManager.sharedManager downloadWithURL:url options:options progress:nil completed:^(UIImage *image, NSError *error, BOOL fromCache)
{
if (image) [self setImage:image forState:state];
if (completedBlock) completedBlock(image, error, fromCache);
}];
objc_setAssociatedObject(self, &operationKey, operation, OBJC_ASSOCIATION_RETAIN_NONATOMIC);
}
}
#if NS_BLOCKS_AVAILABLE
- (void)setImageWithURL:(NSURL *)url success:(SDWebImageSuccessBlock)success failure:(SDWebImageFailureBlock)failure;
- (void)setBackgroundImageWithURL:(NSURL *)url forState:(UIControlState)state
{
[self setImageWithURL:url placeholderImage:nil success:success failure:failure];
[self setBackgroundImageWithURL:url forState:state placeholderImage:nil options:0 completed:nil];
}
- (void)setImageWithURL:(NSURL *)url placeholderImage:(UIImage *)placeholder success:(SDWebImageSuccessBlock)success failure:(SDWebImageFailureBlock)failure;
- (void)setBackgroundImageWithURL:(NSURL *)url forState:(UIControlState)state placeholderImage:(UIImage *)placeholder
{
[self setImageWithURL:url placeholderImage:placeholder options:0 success:success failure:failure];
[self setBackgroundImageWithURL:url forState:state placeholderImage:placeholder options:0 completed:nil];
}
- (void)setImageWithURL:(NSURL *)url placeholderImage:(UIImage *)placeholder options:(SDWebImageOptions)options success:(SDWebImageSuccessBlock)success failure:(SDWebImageFailureBlock)failure;
- (void)setBackgroundImageWithURL:(NSURL *)url forState:(UIControlState)state placeholderImage:(UIImage *)placeholder options:(SDWebImageOptions)options
{
SDWebImageManager *manager = [SDWebImageManager sharedManager];
[self setBackgroundImageWithURL:url forState:state placeholderImage:placeholder options:options completed:nil];
}
// Remove in progress downloader from queue
[manager cancelForDelegate:self];
- (void)setBackgroundImageWithURL:(NSURL *)url forState:(UIControlState)state completed:(SDWebImageCompletedBlock)completedBlock
{
[self setBackgroundImageWithURL:url forState:state placeholderImage:nil options:0 completed:completedBlock];
}
[self setImage:placeholder forState:UIControlStateNormal];
[self setImage:placeholder forState:UIControlStateSelected];
[self setImage:placeholder forState:UIControlStateHighlighted];
- (void)setBackgroundImageWithURL:(NSURL *)url forState:(UIControlState)state placeholderImage:(UIImage *)placeholder completed:(SDWebImageCompletedBlock)completedBlock
{
[self setBackgroundImageWithURL:url forState:state placeholderImage:placeholder options:0 completed:completedBlock];
}
- (void)setBackgroundImageWithURL:(NSURL *)url forState:(UIControlState)state placeholderImage:(UIImage *)placeholder options:(SDWebImageOptions)options completed:(SDWebImageCompletedBlock)completedBlock
{
[self cancelCurrentImageLoad];
[self setBackgroundImage:placeholder forState:state];
if (url)
{
[manager downloadWithURL:url delegate:self options:options success:success failure:failure];
id<SDWebImageOperation> operation = [SDWebImageManager.sharedManager downloadWithURL:url options:options progress:nil completed:^(UIImage *image, NSError *error, BOOL fromCache)
{
if (image) [self setBackgroundImage:image forState:state];
if (completedBlock) completedBlock(image, error, fromCache);
}];
objc_setAssociatedObject(self, &operationKey, operation, OBJC_ASSOCIATION_RETAIN_NONATOMIC);
}
}
#endif
- (void)setBackgroundImageWithURL:(NSURL *)url
{
[self setBackgroundImageWithURL:url placeholderImage:nil];
}
- (void)setBackgroundImageWithURL:(NSURL *)url placeholderImage:(UIImage *)placeholder
{
[self setBackgroundImageWithURL:url placeholderImage:placeholder options:0];
}
- (void)setBackgroundImageWithURL:(NSURL *)url placeholderImage:(UIImage *)placeholder options:(SDWebImageOptions)options
{
SDWebImageManager *manager = [SDWebImageManager sharedManager];
// Remove in progress downloader from queue
[manager cancelForDelegate:self];
[self setBackgroundImage:placeholder forState:UIControlStateNormal];
[self setBackgroundImage:placeholder forState:UIControlStateSelected];
[self setBackgroundImage:placeholder forState:UIControlStateHighlighted];
if (url)
{
NSDictionary *info = [NSDictionary dictionaryWithObject:@"background" forKey:@"type"];
[manager downloadWithURL:url delegate:self options:options userInfo:info];
}
}
#if NS_BLOCKS_AVAILABLE
- (void)setBackgroundImageWithURL:(NSURL *)url success:(SDWebImageSuccessBlock)success failure:(SDWebImageFailureBlock)failure;
{
[self setBackgroundImageWithURL:url placeholderImage:nil success:success failure:failure];
}
- (void)setBackgroundImageWithURL:(NSURL *)url placeholderImage:(UIImage *)placeholder success:(SDWebImageSuccessBlock)success failure:(SDWebImageFailureBlock)failure;
{
[self setBackgroundImageWithURL:url placeholderImage:placeholder options:0 success:success failure:failure];
}
- (void)setBackgroundImageWithURL:(NSURL *)url placeholderImage:(UIImage *)placeholder options:(SDWebImageOptions)options success:(SDWebImageSuccessBlock)success failure:(SDWebImageFailureBlock)failure;
{
SDWebImageManager *manager = [SDWebImageManager sharedManager];
// Remove in progress downloader from queue
[manager cancelForDelegate:self];
[self setBackgroundImage:placeholder forState:UIControlStateNormal];
[self setBackgroundImage:placeholder forState:UIControlStateSelected];
[self setBackgroundImage:placeholder forState:UIControlStateHighlighted];
if (url)
{
NSDictionary *info = [NSDictionary dictionaryWithObject:@"background" forKey:@"type"];
[manager downloadWithURL:url delegate:self options:options userInfo:info success:success failure:failure];
}
}
#endif
- (void)cancelCurrentImageLoad
{
[[SDWebImageManager sharedManager] cancelForDelegate:self];
}
- (void)webImageManager:(SDWebImageManager *)imageManager didProgressWithPartialImage:(UIImage *)image forURL:(NSURL *)url userInfo:(NSDictionary *)info
{
if ([[info valueForKey:@"type"] isEqualToString:@"background"])
// Cancel in progress downloader from queue
id<SDWebImageOperation> operation = objc_getAssociatedObject(self, &operationKey);
if (operation)
{
[self setBackgroundImage:image forState:UIControlStateNormal];
[self setBackgroundImage:image forState:UIControlStateSelected];
[self setBackgroundImage:image forState:UIControlStateHighlighted];
}
else
{
[self setImage:image forState:UIControlStateNormal];
[self setImage:image forState:UIControlStateSelected];
[self setImage:image forState:UIControlStateHighlighted];
}
}
- (void)webImageManager:(SDWebImageManager *)imageManager didFinishWithImage:(UIImage *)image forURL:(NSURL *)url userInfo:(NSDictionary *)info
{
if ([[info valueForKey:@"type"] isEqualToString:@"background"])
{
[self setBackgroundImage:image forState:UIControlStateNormal];
[self setBackgroundImage:image forState:UIControlStateSelected];
[self setBackgroundImage:image forState:UIControlStateHighlighted];
}
else
{
[self setImage:image forState:UIControlStateNormal];
[self setImage:image forState:UIControlStateSelected];
[self setImage:image forState:UIControlStateHighlighted];
[operation cancel];
objc_setAssociatedObject(self, &operationKey, nil, OBJC_ASSOCIATION_RETAIN_NONATOMIC);
}
}

View File

@ -7,7 +7,6 @@
*/
#import "SDWebImageCompat.h"
#import "SDWebImageManagerDelegate.h"
#import "SDWebImageManager.h"
/**
@ -42,7 +41,7 @@
* }
*
*/
@interface UIImageView (WebCache) <SDWebImageManagerDelegate>
@interface UIImageView (WebCache)
/**
* Set the imageView `image` with an `url`.
@ -75,17 +74,18 @@
*/
- (void)setImageWithURL:(NSURL *)url placeholderImage:(UIImage *)placeholder options:(SDWebImageOptions)options;
#if NS_BLOCKS_AVAILABLE
/**
* Set the imageView `image` with an `url`.
*
* The downloand is asynchronous and cached.
*
* @param url The url for the image.
* @param success A block to be executed when the image request succeed This block has no return value and takes the retrieved image as argument.
* @param failure A block object to be executed when the image request failed. This block has no return value and takes the error object describing the network or parsing error that occurred (may be nil).
* @param completedBlock A block called when operation has been completed. This block as no return value
* and takes the requested UIImage as first parameter. In case of error the image parameter
* is nil and the second parameter may contain an NSError. The third parameter is a Boolean
* indicating if the image was retrived from the local cache of from the network.
*/
- (void)setImageWithURL:(NSURL *)url success:(SDWebImageSuccessBlock)success failure:(SDWebImageFailureBlock)failure;
- (void)setImageWithURL:(NSURL *)url completed:(SDWebImageCompletedBlock)completedBlock;
/**
* Set the imageView `image` with an `url`, placeholder.
@ -94,10 +94,12 @@
*
* @param url The url for the image.
* @param placeholder The image to be set initially, until the image request finishes.
* @param success A block to be executed when the image request succeed This block has no return value and takes the retrieved image as argument.
* @param failure A block object to be executed when the image request failed. This block has no return value and takes the error object describing the network or parsing error that occurred (may be nil).
* @param completedBlock A block called when operation has been completed. This block as no return value
* and takes the requested UIImage as first parameter. In case of error the image parameter
* is nil and the second parameter may contain an NSError. The third parameter is a Boolean
* indicating if the image was retrived from the local cache of from the network.
*/
- (void)setImageWithURL:(NSURL *)url placeholderImage:(UIImage *)placeholder success:(SDWebImageSuccessBlock)success failure:(SDWebImageFailureBlock)failure;
- (void)setImageWithURL:(NSURL *)url placeholderImage:(UIImage *)placeholder completed:(SDWebImageCompletedBlock)completedBlock;
/**
* Set the imageView `image` with an `url`, placeholder and custom options.
@ -107,11 +109,12 @@
* @param url The url for the image.
* @param placeholder The image to be set initially, until the image request finishes.
* @param options The options to use when downloading the image. @see SDWebImageOptions for the possible values.
* @param success A block to be executed when the image request succeed This block has no return value and takes the retrieved image as argument.
* @param failure A block object to be executed when the image request failed. This block has no return value and takes the error object describing the network or parsing error that occurred (may be nil).
* @param completedBlock A block called when operation has been completed. This block as no return value
* and takes the requested UIImage as first parameter. In case of error the image parameter
* is nil and the second parameter may contain an NSError. The third parameter is a Boolean
* indicating if the image was retrived from the local cache of from the network.
*/
- (void)setImageWithURL:(NSURL *)url placeholderImage:(UIImage *)placeholder options:(SDWebImageOptions)options success:(SDWebImageSuccessBlock)success failure:(SDWebImageFailureBlock)failure;
#endif
- (void)setImageWithURL:(NSURL *)url placeholderImage:(UIImage *)placeholder options:(SDWebImageOptions)options completed:(SDWebImageCompletedBlock)completedBlock;
/**
* Cancel the current download

View File

@ -7,76 +7,70 @@
*/
#import "UIImageView+WebCache.h"
#import "objc/runtime.h"
static char operationKey;
@implementation UIImageView (WebCache)
- (void)setImageWithURL:(NSURL *)url
{
[self setImageWithURL:url placeholderImage:nil];
[self setImageWithURL:url placeholderImage:nil options:0 completed:nil];
}
- (void)setImageWithURL:(NSURL *)url placeholderImage:(UIImage *)placeholder
{
[self setImageWithURL:url placeholderImage:placeholder options:0];
[self setImageWithURL:url placeholderImage:placeholder options:0 completed:nil];
}
- (void)setImageWithURL:(NSURL *)url placeholderImage:(UIImage *)placeholder options:(SDWebImageOptions)options
{
SDWebImageManager *manager = [SDWebImageManager sharedManager];
[self setImageWithURL:url placeholderImage:placeholder options:options completed:nil];
}
// Remove in progress downloader from queue
[manager cancelForDelegate:self];
- (void)setImageWithURL:(NSURL *)url completed:(SDWebImageCompletedBlock)completedBlock
{
[self setImageWithURL:url placeholderImage:nil options:0 completed:completedBlock];
}
- (void)setImageWithURL:(NSURL *)url placeholderImage:(UIImage *)placeholder completed:(SDWebImageCompletedBlock)completedBlock
{
[self setImageWithURL:url placeholderImage:placeholder options:0 completed:completedBlock];
}
- (void)setImageWithURL:(NSURL *)url placeholderImage:(UIImage *)placeholder options:(SDWebImageOptions)options completed:(SDWebImageCompletedBlock)completedBlock
{
[self cancelCurrentImageLoad];
self.image = placeholder;
if (url)
{
[manager downloadWithURL:url delegate:self options:options];
id<SDWebImageOperation> operation = [SDWebImageManager.sharedManager downloadWithURL:url options:options progress:nil completed:^(UIImage *image, NSError *error, BOOL fromCache)
{
if (image)
{
self.image = image;
[self setNeedsLayout];
}
if (completedBlock)
{
completedBlock(image, error, fromCache);
}
}];
objc_setAssociatedObject(self, &operationKey, operation, OBJC_ASSOCIATION_RETAIN_NONATOMIC);
}
}
#if NS_BLOCKS_AVAILABLE
- (void)setImageWithURL:(NSURL *)url success:(SDWebImageSuccessBlock)success failure:(SDWebImageFailureBlock)failure;
{
[self setImageWithURL:url placeholderImage:nil success:success failure:failure];
}
- (void)setImageWithURL:(NSURL *)url placeholderImage:(UIImage *)placeholder success:(SDWebImageSuccessBlock)success failure:(SDWebImageFailureBlock)failure;
{
[self setImageWithURL:url placeholderImage:placeholder options:0 success:success failure:failure];
}
- (void)setImageWithURL:(NSURL *)url placeholderImage:(UIImage *)placeholder options:(SDWebImageOptions)options success:(SDWebImageSuccessBlock)success failure:(SDWebImageFailureBlock)failure;
{
SDWebImageManager *manager = [SDWebImageManager sharedManager];
// Remove in progress downloader from queue
[manager cancelForDelegate:self];
self.image = placeholder;
if (url)
{
[manager downloadWithURL:url delegate:self options:options success:success failure:failure];
}
}
#endif
- (void)cancelCurrentImageLoad
{
[[SDWebImageManager sharedManager] cancelForDelegate:self];
}
- (void)webImageManager:(SDWebImageManager *)imageManager didProgressWithPartialImage:(UIImage *)image forURL:(NSURL *)url
{
self.image = image;
[self setNeedsLayout];
}
- (void)webImageManager:(SDWebImageManager *)imageManager didFinishWithImage:(UIImage *)image
{
self.image = image;
[self setNeedsLayout];
// Cancel in progress downloader from queue
id<SDWebImageOperation> operation = objc_getAssociatedObject(self, &operationKey);
if (operation)
{
[operation cancel];
objc_setAssociatedObject(self, &operationKey, nil, OBJC_ASSOCIATION_RETAIN_NONATOMIC);
}
}
@end