Skip to content

Commit

Permalink
Update to latest SDWebImage.
Browse files Browse the repository at this point in the history
  • Loading branch information
henrik authored and kirbyt committed Jun 26, 2010
1 parent b0cef85 commit 3e84e0f
Show file tree
Hide file tree
Showing 11 changed files with 211 additions and 148 deletions.
9 changes: 7 additions & 2 deletions src/Sample/Classes/SDWebImageSample/KTPhotoView+SDWebImage.m
Expand Up @@ -20,7 +20,10 @@ - (void)setImageWithURL:(NSURL *)url placeholderImage:(UIImage *)placeholder {
// Remove in progress downloader from queue
[manager cancelForDelegate:self];

UIImage *cachedImage = [manager imageWithURL:url];
UIImage *cachedImage = nil;
if (url) {
cachedImage = [manager imageWithURL:url];
}

if (cachedImage) {
[self setImage:cachedImage];
Expand All @@ -30,7 +33,9 @@ - (void)setImageWithURL:(NSURL *)url placeholderImage:(UIImage *)placeholder {
[self setImage:placeholder];
}

[manager downloadWithURL:url delegate:self];
if (url) {
[manager downloadWithURL:url delegate:self];
}
}
}

Expand Down
9 changes: 7 additions & 2 deletions src/Sample/Classes/SDWebImageSample/KTThumbView+SDWebImage.m
Expand Up @@ -20,7 +20,10 @@ - (void)setImageWithURL:(NSURL *)url placeholderImage:(UIImage *)placeholder {
// Remove in progress downloader from queue
[manager cancelForDelegate:self];

UIImage *cachedImage = [manager imageWithURL:url];
UIImage *cachedImage = nil;
if (url) {
cachedImage = [manager imageWithURL:url];
}

if (cachedImage) {
[self setThumbImage:cachedImage];
Expand All @@ -30,7 +33,9 @@ - (void)setImageWithURL:(NSURL *)url placeholderImage:(UIImage *)placeholder {
[self setThumbImage:placeholder];
}

[manager downloadWithURL:url delegate:self];
if (url) {
[manager downloadWithURL:url delegate:self];
}
}
}

Expand Down
51 changes: 24 additions & 27 deletions src/Sample/Classes/SDWebImageSample/SDWebImage/README.md
Expand Up @@ -6,7 +6,7 @@ This library provides a category for UIImageVIew with support for remote images
It provides:

- An UIImageView category adding web image and cache management to the Cocoa Touch framework
- An asynchronous image downloader using threads (NSOperation)
- An asynchronous image downloader
- An asynchronous memory + disk image caching with automatic cache expiration handling
- A garantie that the same URL won't be downloaded several times
- A garantie that bogus URLs won't be retried again and again
Expand Down Expand Up @@ -39,23 +39,21 @@ time faster than my own servers... WTF??

In fact, my servers were well but a lot of latency was added to the requests, certainly because my
application wasn't responsive enough to handle the requests at full speed. At this moment, I
understood something important, asynchronous NSURLConnections are tied to the main runloop (I
guess). It's certainly based on the poll multiplexer system call, which allows a single thread to
handle quite a huge number of simultaneous connections. It works well while nothing blocks in the
loop, but in this loop, there is also the events handling. A simple test to recognize an application
using NSURLConnection to load there remote images is to scroll the UITableView with your finger to
disclose an unloaded image, and to keep your finger pressed on the screen. If the image doesn't load
until you release you finger, the application is certainly using NSURLConnection (try with the
Facebook app for instance). I'm not completely clear about the reason of this blocking, I thought
the iPhone was running a dedicated run-loop for connections, but the fact is, NSURLConnection is
affected by the application events and/or UI handling (or something else I'm not aware of).

Thus I explored another path and found this marvelous NSOperation class to handle concurrency with
love. I ran some quick tests with this tool and I instantly got enhanced responsiveness of the image
loading in my UITableView by... a lot. Thus I rewrote the [Fraggle][]'s implementation using the
same concept of drop-in remplacement for UIImageView but with this new technic. I thought the result
could benefits to a lot of other applications, thus we decided, with [Fraggle][], to open-sourced
it, et voila!
understood something important, asynchronous NSURLConnections are tied to the main runloop in the
NSEventTrackingRunLoopMode. As explained in the documentation, this runloop mode is affected by
UI events:

> Cocoa uses this mode to restrict incoming events during mouse-dragging loops and other sorts of
> user interface tracking loops.
A simple test to recognize an application using NSURLConnection in its default mode to load there
remote images is to scroll the UITableView with your finger to disclose an unloaded image, and to
keep your finger pressed on the screen. If the image doesn't load until you release you finger,
you've got one (try with the Facebook app for instance). It took me quite some time to understand
the reason for this lagging issue. Actually I first used NSOperation to workaround this issue.

This technic combined with an image cache instantly gave a lot of responsiveness to my app.
I thought this lib could benefits to a lot of other Cocoa Touch application so I open-sourced it.

How To Use It
-------------
Expand All @@ -64,7 +62,7 @@ How To Use It

Just #import the UIImageView+WebCache.h header, and call the setImageWithURL:placeholderImage:
method from the tableView:cellForRowAtIndexPath: UITableViewDataSource method. Everything will be
handled for you, from parallel downloads to caching management.
handled for you, from async downloads to caching management.

#import "UIImageView+WebCache.h"

Expand Down Expand Up @@ -113,23 +111,22 @@ Here is a simple example of how to use SDWebImageManager:
}

Your class will have to implement the SDWebImageManagerDelegate protocol, and to implement the
imageHelper:didFinishWithImage: method from this protocol:
webImageManager:didFinishWithImage: method from this protocol:

- (void)imageHelper:(SDWebImageManager *)imageHelper didFinishWithImage:(UIImage *)image
- (void)webImageManager:(SDWebImageManager *)imageManager didFinishWithImage:(UIImage *)image
{
// Do something with the downloaded image
}

### Using Asynchronous Image Downloader Independently

It is possible to use the NSOperation based image downloader independently. Just create an instance
of SDWebImageDownloader using its convenience constructor downloaderWithURL:target:action:.
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:.

downloader = [SDWebImageDownloader downloaderWithURL:url delegate:self];

The download will by queued immediately and the imageDownloader:didFinishWithImage: method from the
SDWebImageDownloaderDelegate protocol will be called as soon as the download of image is completed
(prepare not to be called from the main thread).
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.

### Using Asynchronous Image Caching Independently

Expand Down Expand Up @@ -168,4 +165,4 @@ Future Enhancements
[Fraggle]: http://fraggle.squarespace.com
[Urban Rivals]: http://fraggle.squarespace.com/blog/2009/9/15/almost-done-here-is-urban-rivals-iphone-trailer.html
[Three20]: http://groups.google.com/group/three20
[Joe Hewitt]: http://www.joehewitt.com
[Joe Hewitt]: http://www.joehewitt.com
72 changes: 36 additions & 36 deletions src/Sample/Classes/SDWebImageSample/SDWebImage/SDImageCache.m
Expand Up @@ -4,7 +4,7 @@
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
*/

#import "SDImageCache.h"
#import <CommonCrypto/CommonDigest.h>
Expand All @@ -15,18 +15,6 @@

@implementation SDImageCache

#pragma mark SDImageCache (notification handlers)

- (void)didReceiveMemoryWarning:(void *)object
{
[self clearMemory];
}

- (void)willTerminate
{
[self cleanDisk];
}

#pragma mark NSObject

- (id)init
Expand All @@ -39,10 +27,13 @@ - (id)init
// Init the disk cache
NSArray *paths = NSSearchPathForDirectoriesInDomains(NSCachesDirectory, NSUserDomainMask, YES);
diskCachePath = [[[paths objectAtIndex:0] stringByAppendingPathComponent:@"ImageCache"] retain];

if (![[NSFileManager defaultManager] fileExistsAtPath:diskCachePath])
{
[[NSFileManager defaultManager] createDirectoryAtPath:diskCachePath attributes:nil];
[[NSFileManager defaultManager] createDirectoryAtPath:diskCachePath
withIntermediateDirectories:YES
attributes:nil
error:NULL];
}

// Init the operation queue
Expand All @@ -51,33 +42,39 @@ - (id)init

// Subscribe to app events
[[NSNotificationCenter defaultCenter] addObserver:self
selector:@selector(didReceiveMemoryWarning:)
name:UIApplicationDidReceiveMemoryWarningNotification
selector:@selector(clearMemory)
name:UIApplicationDidReceiveMemoryWarningNotification
object:nil];

[[NSNotificationCenter defaultCenter] addObserver:self
selector:@selector(willTerminate)
name:UIApplicationWillTerminateNotification
object:nil];
selector:@selector(cleanDisk)
name:UIApplicationWillTerminateNotification
object:nil];

#ifdef __IPHONE_4_0
UIDevice *device = [UIDevice currentDevice];
if ([device respondsToSelector:@selector(isMultitaskingSupported)] && device.multitaskingSupported)
{
// When in background, clean memory in order to have less chance to be killed
[[NSNotificationCenter defaultCenter] addObserver:self
selector:@selector(clearMemory)
name:UIApplicationDidEnterBackgroundNotification
object:nil];
}
#endif
}

return self;
}

- (void)dealloc
{
[memCache release];
[diskCachePath release];
[cacheInQueue release];

[[NSNotificationCenter defaultCenter] removeObserver:self
name:UIApplicationDidReceiveMemoryWarningNotification
object:nil];

[[NSNotificationCenter defaultCenter] removeObserver:self
name:UIApplicationWillTerminateNotification
object:nil];

[memCache release], memCache = nil;
[diskCachePath release], diskCachePath = nil;
[cacheInQueue release], cacheInQueue = nil;

[[NSNotificationCenter defaultCenter] removeObserver:self];

[super dealloc];
}

Expand All @@ -89,7 +86,7 @@ + (SDImageCache *)sharedImageCache
{
instance = [[SDImageCache alloc] init];
}

return instance;
}

Expand Down Expand Up @@ -134,7 +131,7 @@ - (void)storeImage:(UIImage *)image forKey:(NSString *)key toDisk:(BOOL)toDisk
[memCache setObject:image forKey:key];

if (toDisk)
{
{
[cacheInQueue addOperation:[[[NSInvocationOperation alloc] initWithTarget:self selector:@selector(storeKeyToDisk:) object:key] autorelease]];
}
}
Expand Down Expand Up @@ -187,7 +184,10 @@ - (void)clearDisk
{
[cacheInQueue cancelAllOperations];
[[NSFileManager defaultManager] removeItemAtPath:diskCachePath error:nil];
[[NSFileManager defaultManager] createDirectoryAtPath:diskCachePath attributes:nil];
[[NSFileManager defaultManager] createDirectoryAtPath:diskCachePath
withIntermediateDirectories:YES
attributes:nil
error:NULL];
}

- (void)cleanDisk
Expand Down
Expand Up @@ -9,16 +9,23 @@
#import <Foundation/Foundation.h>
#import "SDWebImageDownloaderDelegate.h"

@interface SDWebImageDownloader : NSOperation
@interface SDWebImageDownloader : NSObject
{
@private
NSURL *url;
id<SDWebImageDownloaderDelegate> delegate;
NSURLConnection *connection;
NSMutableData *imageData;
}

@property (retain) NSURL *url;
@property (assign) id<SDWebImageDownloaderDelegate> delegate;
@property (nonatomic, retain) NSURL *url;
@property (nonatomic, assign) id<SDWebImageDownloaderDelegate> delegate;

+ (id)downloaderWithURL:(NSURL *)url delegate:(id<SDWebImageDownloaderDelegate>)delegate;
+ (void)setMaxConcurrentDownloads:(NSUInteger)max;
- (void)start;
- (void)cancel;

// This method is now no-op and is deprecated
+ (void)setMaxConcurrentDownloads:(NSUInteger)max __attribute__((deprecated));

@end

0 comments on commit 3e84e0f

Please sign in to comment.