我试图在iPhone 6(iOS 9.3.2)上运行我的应用程序并使用XCode 7.3,但问题是每次从一个标签移动到另一个标签时它都会崩溃。 Exception指向此类,标记为" error"。
的方法有人可以帮我解决这个问题吗?
我附上代码供您参考。
感谢。
//
// AsyncImageView.m //
#import "AsyncImageView.h"
#import <objc/message.h>
#define MAX_IMAGE_SIZE 1.5 * 1024 * 1024
#define RESIZE_IMG 0
NSString *const AsyncImageLoadDidFinish = @"AsyncImageLoadDidFinish";
NSString *const AsyncImageLoadDidFail = @"AsyncImageLoadDidFail";
NSString *const AsyncImageTargetReleased = @"AsyncImageTargetReleased";
NSString *const AsyncImageImageKey = @"image";
NSString *const AsyncImageURLKey = @"URL";
NSString *const AsyncImageCacheKey = @"cache";
NSString *const AsyncImageErrorKey = @"error";
@interface AsyncImageConnection : NSObject
@property (nonatomic, strong) NSURLConnection *connection;
@property (nonatomic, strong) NSMutableData *data;
@property (nonatomic, strong) NSURL *URL;
@property (nonatomic, strong) NSCache *cache;
@property (nonatomic, strong) id target;
@property (nonatomic, assign) SEL success;
@property (nonatomic, assign) SEL failure;
@property (nonatomic, readonly, getter = isLoading) BOOL loading;
@property (nonatomic, readonly) BOOL cancelled;
- (AsyncImageConnection *)initWithURL:(NSURL *)URL
cache:(NSCache *)cache
target:(id)target
success:(SEL)success
failure:(SEL)failure;
- (void)start;
- (void)cancel;
- (BOOL)isInCache;
@end
@implementation AsyncImageConnection
@synthesize connection = _connection;
@synthesize data = _data;
@synthesize URL = _URL;
@synthesize cache = _cache;
@synthesize target = _target;
@synthesize success = _success;
@synthesize failure = _failure;
@synthesize loading = _loading;
@synthesize cancelled = _cancelled;
- (AsyncImageConnection *)initWithURL:(NSURL *)URL
cache:(NSCache *)cache
target:(id)target
success:(SEL)success
failure:(SEL)failure
{
if ((self = [self init]))
{
self.URL = URL;
self.cache = cache;
self.target = target;
self.success = success;
self.failure = failure;
}
return self;
}
- (UIImage *)cachedImage
{
if ([_URL isFileURL])
{
NSString *path = [[_URL absoluteURL] path];
NSString *resourcePath = [[NSBundle mainBundle] resourcePath];
if ([path hasPrefix:resourcePath])
{
return [UIImage imageNamed:[path substringFromIndex:[resourcePath length]]];
}
}
return [_cache objectForKey:_URL];
}
- (BOOL)isInCache
{
return [self cachedImage] != nil;
}
- (void)loadFailedWithError:(NSError *)error
{
_loading = NO;
_cancelled = NO;
[[NSNotificationCenter defaultCenter] postNotificationName:AsyncImageLoadDidFail
object:_target
userInfo:[NSDictionary dictionaryWithObjectsAndKeys:
_URL, AsyncImageURLKey,
error, AsyncImageErrorKey,
nil]];
}
- (void)cacheImage:(UIImage *)image
{
if (!_cancelled)
{
if (image && _URL)
{
BOOL storeInCache = YES;
if ([_URL isFileURL])
{
if ([[[_URL absoluteURL] path] hasPrefix:[[NSBundle mainBundle] resourcePath]])
{
//do not store in cache
storeInCache = NO;
}
}
if (storeInCache)
{
#if RESIZE_IMG
// resize the image before storing
NSData *data = UIImageJPEGRepresentation(image, 1.0);
if (data.length > MAX_IMAGE_SIZE)
{
image = [HFUtils imageWithImage:image scaledBy:MAX_IMAGE_SIZE / (float)data.length];
}
#endif
[_cache setObject:image forKey:_URL];
}
}
NSMutableDictionary *userInfo = [NSMutableDictionary dictionaryWithObjectsAndKeys:
image, AsyncImageImageKey,
_URL, AsyncImageURLKey,
nil];
if (_cache)
{
[userInfo setObject:_cache forKey:AsyncImageCacheKey];
}
_loading = NO;
[[NSNotificationCenter defaultCenter] postNotificationName:AsyncImageLoadDidFinish
object:_target
userInfo:[[userInfo copy] autorelease]];
}
else
{
_loading = NO;
_cancelled = NO;
}
}
- (void)processDataInBackground:(NSData *)data
{
@synchronized ([self class])
{
if (!_cancelled)
{
UIImage *image = [[UIImage alloc] initWithData:data];
if (image)
{
//add to cache (may be cached already but it doesn't matter)
[self performSelectorOnMainThread:@selector(cacheImage:)
withObject:image
waitUntilDone:YES];
[image release];
}
else
{
@autoreleasepool
{
NSError *error = [NSError errorWithDomain:@"AsyncImageLoader" code:0 userInfo:[NSDictionary dictionaryWithObject:@"Invalid image data" forKey:NSLocalizedDescriptionKey]];
[self performSelectorOnMainThread:@selector(loadFailedWithError:) withObject:error waitUntilDone:YES];
}
}
}
else
{
//clean up
[self performSelectorOnMainThread:@selector(cacheImage:)
withObject:nil
waitUntilDone:YES];
}
}
}
- (void)connection:(NSURLConnection *)connection didReceiveResponse:(NSURLResponse *)response
{
self.data = [NSMutableData data];
}
- (void)connection:(NSURLConnection *)connection didReceiveData:(NSData *)data
{
//add data
[_data appendData:data];
}
- (void)connectionDidFinishLoading:(NSURLConnection *)connection
{
[self performSelectorInBackground:@selector(processDataInBackground:) withObject:_data];
self.connection = nil;
self.data = nil;
}
- (void)connection:(NSURLConnection *)connection didFailWithError:(NSError *)error
{
self.connection = nil;
self.data = nil;
[self loadFailedWithError:error];
}
- (void)start
{
if (_loading && !_cancelled)
{
return;
}
//begin loading
_loading = YES;
_cancelled = NO;
//check for nil URL
if (_URL == nil)
{
[self cacheImage:nil];
return;
}
//check for cached image
UIImage *image = [self cachedImage];
if (image)
{
//add to cache (cached already but it doesn't matter)
[self performSelectorOnMainThread:@selector(cacheImage:)
withObject:image
waitUntilDone:NO];
return;
}
//begin load
NSURLRequest *request = [NSURLRequest requestWithURL:_URL
cachePolicy:NSURLCacheStorageNotAllowed
timeoutInterval:[AsyncImageLoader sharedLoader].loadingTimeout];
_connection = [[NSURLConnection alloc] initWithRequest:request delegate:self startImmediately:NO];
[_connection scheduleInRunLoop:[NSRunLoop mainRunLoop] forMode:NSRunLoopCommonModes];
[_connection start];
}
- (void)cancel
{
_cancelled = YES;
[_connection cancel];
self.connection = nil;
self.data = nil;
}
- (void)dealloc
{
[_connection release];
[_data release];
[_URL release];
[_target release];
[super ah_dealloc];
}
@end
@interface AsyncImageLoader ()
@property (nonatomic, strong) NSMutableArray *connections;
@end
@implementation AsyncImageLoader
@synthesize cache = _cache;
@synthesize connections = _connections;
@synthesize concurrentLoads = _concurrentLoads;
@synthesize loadingTimeout = _loadingTimeout;
+ (AsyncImageLoader *)sharedLoader
{
static AsyncImageLoader *sharedInstance = nil;
if (sharedInstance == nil)
{
sharedInstance = [[self alloc] init];
}
return sharedInstance;
}
+ (NSCache *)defaultCache
{
static NSCache *sharedInstance = nil;
if (sharedInstance == nil)
{
sharedInstance = [[NSCache alloc] init];
}
return sharedInstance;
}
- (AsyncImageLoader *)init
{
if ((self = [super init]))
{
self.cache = [[self class] defaultCache];
_concurrentLoads = 2;
_loadingTimeout = 60.0;
_connections = [[NSMutableArray alloc] init];
[[NSNotificationCenter defaultCenter] addObserver:self
selector:@selector(imageLoaded:)
name:AsyncImageLoadDidFinish
object:nil];
[[NSNotificationCenter defaultCenter] addObserver:self
selector:@selector(imageFailed:)
name:AsyncImageLoadDidFail
object:nil];
[[NSNotificationCenter defaultCenter] addObserver:self
selector:@selector(targetReleased:)
name:AsyncImageTargetReleased
object:nil];
}
return self;
}
- (void)updateQueue
{
//start connections
NSUInteger count = 0;
for (AsyncImageConnection *connection in _connections)
{
if (![connection isLoading])
{
if ([connection isInCache])
{
[connection start];
}
else if (count < _concurrentLoads)
{
count ++;
[connection start];
}
}
}
}
- (void)imageLoaded:(NSNotification *)notification
{
//complete connections for URL
NSURL *URL = [notification.userInfo objectForKey:AsyncImageURLKey];
for (int i = (int)[_connections count] - 1; i >= 0; i--)
{
AsyncImageConnection *connection = [_connections objectAtIndex:(NSUInteger)i];
if (connection.URL == URL || [connection.URL isEqual:URL])
{
//cancel earlier connections for same target/action
for (int j = i - 1; j >= 0; j--)
{
AsyncImageConnection *earlier = [_connections objectAtIndex:(NSUInteger)j];
if (earlier.target == connection.target &&
earlier.success == connection.success)
{
[earlier cancel];
[_connections removeObjectAtIndex:(NSUInteger)j];
i--;
}
}
//cancel connection (in case it's a duplicate)
[connection cancel];
//perform action
UIImage *image = [notification.userInfo objectForKey:AsyncImageImageKey];
#if RESIZE_IMG
// resize image before sending it over
NSData *data = UIImageJPEGRepresentation(image, 1.0);
if (data.length > MAX_IMAGE_SIZE)
{
image = [HFUtils imageWithImage:image scaledBy:MAX_IMAGE_SIZE / (float)data.length];
}
#endif
objc_msgSend(connection.target, connection.success, image, connection.URL);
//remove from queue
[_connections removeObjectAtIndex:(NSUInteger)i];
}
}
//update the queue
[self updateQueue];
}
- (void)imageFailed:(NSNotification *)notification
{
//remove connections for URL
NSURL *URL = [notification.userInfo objectForKey:AsyncImageURLKey];
for (int i = (int)[_connections count] - 1; i >= 0; i--)
{
AsyncImageConnection *connection = [_connections objectAtIndex:(NSUInteger)i];
if ([connection.URL isEqual:URL])
{
//cancel connection (in case it's a duplicate)
[connection cancel];
//perform failure action
if (connection.failure)
{
NSError *error = [notification.userInfo objectForKey:AsyncImageErrorKey];
objc_msgSend(connection.target, connection.failure, error, URL);
}
//remove from queue
[_connections removeObjectAtIndex:(NSUInteger)i];
}
}
//update the queue
[self updateQueue];
}
- (void)targetReleased:(NSNotification *)notification
{
//remove connections for URL
id target = [notification object];
for (int i = (int)[_connections count] - 1; i >= 0; i--)
{
AsyncImageConnection *connection = [_connections objectAtIndex:(NSUInteger)i];
if (connection.target == target)
{
//cancel connection
[connection cancel];
[_connections removeObjectAtIndex:(NSUInteger)i];
}
}
//update the queue
[self updateQueue];
}
- (void)loadImageWithURL:(NSURL *)URL target:(id)target success:(SEL)success failure:(SEL)failure
{
if (URL == nil)
return;
//check cache
UIImage *image = [_cache objectForKey:URL];
if (image)
{
[self cancelLoadingImagesForTarget:self action:success];
if (success) [target performSelectorOnMainThread:success withObject:image waitUntilDone:NO];
return;
}
//create new connection
AsyncImageConnection *connection = [[AsyncImageConnection alloc] initWithURL:URL
cache:_cache
target:target
success:success
failure:failure];
BOOL added = NO;
for (NSUInteger i = 0; i < [_connections count]; i++)
{
AsyncImageConnection *existingConnection = [_connections objectAtIndex:i];
if (!existingConnection.loading)
{
[_connections insertObject:connection atIndex:i];
added = YES;
break;
}
}
if (!added)
{
[_connections addObject:connection];
}
[connection release];
[self updateQueue];
}
- (void)loadImageWithURL:(NSURL *)URL target:(id)target action:(SEL)action
{
[self loadImageWithURL:URL target:target success:action failure:NULL];
}
- (void)loadImageWithURL:(NSURL *)URL
{
[self loadImageWithURL:URL target:nil success:NULL failure:NULL];
}
- (void)cancelLoadingURL:(NSURL *)URL target:(id)target action:(SEL)action
{
for (int i = (int)[_connections count] - 1; i >= 0; i--)
{
AsyncImageConnection *connection = [_connections objectAtIndex:(NSUInteger)i];
if ([connection.URL isEqual:URL] && connection.target == target && connection.success == action)
{
[connection cancel];
[_connections removeObjectAtIndex:(NSUInteger)i];
}
}
}
- (void)cancelLoadingURL:(NSURL *)URL target:(id)target
{
for (int i = (int)[_connections count] - 1; i >= 0; i--)
{
AsyncImageConnection *connection = [_connections objectAtIndex:(NSUInteger)i];
if ([connection.URL isEqual:URL] && connection.target == target)
{
[connection cancel];
[_connections removeObjectAtIndex:(NSUInteger)i];
}
}
}
- (void)cancelLoadingURL:(NSURL *)URL
{
for (int i = (int)[_connections count] - 1; i >= 0; i--)
{
AsyncImageConnection *connection = [_connections objectAtIndex:(NSUInteger)i];
if ([connection.URL isEqual:URL])
{
[connection cancel];
[_connections removeObjectAtIndex:(NSUInteger)i];
}
}
}
- (void)cancelLoadingImagesForTarget:(id)target action:(SEL)action
{
for (int i = (int)[_connections count] - 1; i >= 0; i--)
{
AsyncImageConnection *connection = [_connections objectAtIndex:(NSUInteger)i];
if (connection.target == target && connection.success == action)
{
[connection cancel];
}
}
}
- (void)cancelLoadingImagesForTarget:(id)target
{
for (int i = (int)[_connections count] - 1; i >= 0; i--)
{
AsyncImageConnection *connection = [_connections objectAtIndex:(NSUInteger)i];
if (connection.target == target)
{
[connection cancel];
}
}
}
- (NSURL *)URLForTarget:(id)target action:(SEL)action
{
//return the most recent image URL assigned to the target for the given action
//this is not neccesarily the next image that will be assigned
for (int i = (int)[_connections count] - 1; i >= 0; i--)
{
AsyncImageConnection *connection = [_connections objectAtIndex:(NSUInteger)i];
if (connection.target == target && connection.success == action)
{
return [[connection.URL ah_retain] autorelease];
}
}
return nil;
}
- (NSURL *)URLForTarget:(id)target
{
//return the most recent image URL assigned to the target
//this is not neccesarily the next image that will be assigned
for (int i = (int)[_connections count] - 1; i >= 0; i--)
{
AsyncImageConnection *connection = [_connections objectAtIndex:(NSUInteger)i];
if (connection.target == target)
{
return [[connection.URL ah_retain] autorelease];
}
}
return nil;
}
- (void)dealloc
{
[[NSNotificationCenter defaultCenter] removeObserver:self];
[_cache release];
[_connections release];
[super ah_dealloc];
}
@end
@implementation UIImageView(AsyncImageView)
- (void)setImageURL:(NSURL *)imageURL
{
[[AsyncImageLoader sharedLoader] loadImageWithURL:imageURL target:self action:@selector(setImage:)];
}
- (NSURL *)imageURL
{
return [[AsyncImageLoader sharedLoader] URLForTarget:self action:@selector(setImage:)];
}
@end
@interface AsyncImageView ()
@property (nonatomic, strong) UIActivityIndicatorView *activityView;
@end
@implementation AsyncImageView
@synthesize showActivityIndicator = _showActivityIndicator;
@synthesize activityIndicatorStyle = _activityIndicatorStyle;
@synthesize crossfadeImages = _crossfadeImages;
@synthesize crossfadeDuration = _crossfadeDuration;
@synthesize activityView = _activityView;
- (void)setUp
{
_showActivityIndicator = YES;
_activityIndicatorStyle = UIActivityIndicatorViewStyleGray;
_crossfadeImages = YES;
_crossfadeDuration = 0.4;
}
- (id)initWithFrame:(CGRect)frame
{
if ((self = [super initWithFrame:frame]))
{
[self setUp];
}
return self;
}
- (id)initWithCoder:(NSCoder *)aDecoder
{
if ((self = [super initWithCoder:aDecoder]))
{
[self setUp];
}
return self;
}
- (void)setImageURL:(NSURL *)imageURL
{
super.imageURL = imageURL;
if (_showActivityIndicator && !self.image && imageURL)
{
if (_activityView == nil)
{
_activityView = [[UIActivityIndicatorView alloc] initWithActivityIndicatorStyle:_activityIndicatorStyle];
_activityView.hidesWhenStopped = YES;
_activityView.center = CGPointMake(self.bounds.size.width / 2.0f, self.bounds.size.height / 2.0f);
_activityView.autoresizingMask = UIViewAutoresizingFlexibleLeftMargin | UIViewAutoresizingFlexibleTopMargin | UIViewAutoresizingFlexibleRightMargin | UIViewAutoresizingFlexibleBottomMargin;
[self addSubview:_activityView];
}
[_activityView startAnimating];
}
}
- (void)setActivityIndicatorStyle:(UIActivityIndicatorViewStyle)style
{
_activityIndicatorStyle = style;
[_activityView removeFromSuperview];
self.activityView = nil;
}
- (void)setImage:(UIImage *)image //error
{
if (_crossfadeImages)
{
//implement crossfade transition without needing to import QuartzCore
id animation = objc_msgSend(NSClassFromString(@"CATransition"), @selector(animation));
objc_msgSend(animation, @selector(setType:), @"kCATransitionFade");
objc_msgSend(animation, @selector(setDuration:), _crossfadeDuration);
objc_msgSend(self.layer, @selector(addAnimation:forKey:), animation, nil);
}
super.image = image;
[_activityView stopAnimating];
}
- (void)dealloc
{
[[AsyncImageLoader sharedLoader] cancelLoadingURL:self.imageURL target:self];
[_activityView release];
[super ah_dealloc];
}
@end