Improvements to reachability, the request queue, logging throughout the network layer, decoupling of components via notifications, reliability improvements for unit tests in the cache layer. Improved thread-local cleanup routines inside the managed object store. Changing base URL's is now more reliable. fixes #171, #158, #113, #104, #102

This commit is contained in:
Blake Watters
2011-06-29 13:28:14 -04:00
parent 537a9d2fd1
commit 2a8ece97c1
22 changed files with 330 additions and 182 deletions

View File

@@ -231,6 +231,8 @@ NSString* RKPathAppendQueryParams(NSString* resourcePath, NSDictionary* queryPar
@property (nonatomic, assign) RKRequestCachePolicy cachePolicy;
@property (nonatomic, readonly) NSString* cachePath;
/////////////////////////////////////////////////////////////////////////
/// @name Shared Client Instance
/////////////////////////////////////////////////////////////////////////

View File

@@ -14,6 +14,10 @@
#import "RKAlert.h"
#import "RKLog.h"
// Set Logging Component
#undef RKLogComponent
#define RKLogComponent lcl_cRestKitNetwork
///////////////////////////////////////////////////////////////////////////////////////////////////
// Global
@@ -127,13 +131,7 @@ NSString* RKPathAppendQueryParams(NSString* resourcePath, NSDictionary* queryPar
- (id)initWithBaseURL:(NSString*)baseURL {
self = [self init];
if (self) {
NSString* cacheDirForClient = [NSString stringWithFormat:@"RKClientRequestCache-%@",
[[NSURL URLWithString:baseURL] host]];
NSString* cachePath = [[NSSearchPathForDirectoriesInDomains(NSCachesDirectory, NSUserDomainMask, YES) objectAtIndex:0]
stringByAppendingPathComponent:cacheDirForClient];
_cache = [[RKRequestCache alloc] initWithCachePath:cachePath
storagePolicy:RKRequestCacheStoragePolicyPermanently];
if (self) {
self.cachePolicy = RKRequestCachePolicyDefault;
self.baseURL = baseURL;
@@ -156,18 +154,25 @@ NSString* RKPathAppendQueryParams(NSString* resourcePath, NSDictionary* queryPar
self.serviceUnavailableAlertMessage = nil;
self.cache = nil;
[_HTTPHeaders release];
[_baseURLReachabilityObserver release];
[super dealloc];
}
- (NSString*)cachePath {
NSString* cacheDirForClient = [NSString stringWithFormat:@"RKClientRequestCache-%@",
[[NSURL URLWithString:self.baseURL] host]];
NSString* cachePath = [[NSSearchPathForDirectoriesInDomains(NSCachesDirectory, NSUserDomainMask, YES) objectAtIndex:0]
stringByAppendingPathComponent:cacheDirForClient];
return cachePath;
}
- (BOOL)isNetworkAvailable {
BOOL isNetworkAvailable = NO;
if (self.baseURLReachabilityObserver) {
isNetworkAvailable = [self.baseURLReachabilityObserver isNetworkReachable];
} else {
RKReachabilityObserver* googleObserver = [RKReachabilityObserver reachabilityObserverWithHostName:@"google.com"];
isNetworkAvailable = [googleObserver isNetworkReachable];
}
return isNetworkAvailable;
}
@@ -204,14 +209,29 @@ NSString* RKPathAppendQueryParams(NSString* resourcePath, NSDictionary* queryPar
[_baseURL release];
_baseURL = nil;
_baseURL = [baseURL retain];
[_baseURLReachabilityObserver release];
_baseURLReachabilityObserver = nil;
// Don't crash if baseURL is nil'd out (i.e. dealloc)
if (baseURL) {
// Configure a cache for the new base URL
[_cache release];
_cache = [[RKRequestCache alloc] initWithCachePath:[self cachePath]
storagePolicy:RKRequestCacheStoragePolicyPermanently];
// Configure a new reachability observer
NSURL* URL = [NSURL URLWithString:baseURL];
_baseURLReachabilityObserver = [[RKReachabilityObserver reachabilityObserverWithHostName:[URL host]] retain];
_baseURLReachabilityObserver = [[RKReachabilityObserver alloc] initWithHostname:[URL host]];
// Suspend the queue until reachability to our new hostname is established
[RKRequestQueue sharedQueue].suspended = YES;
[[NSNotificationCenter defaultCenter] addObserver:self
selector:@selector(reachabilityWasDetermined:)
name:RKReachabilityStateWasDeterminedNotification
object:_baseURLReachabilityObserver];
RKLogDebug(@"Base URL changed for client %@, suspending main queue until reachability to host '%@' can be determined",
self, [URL host]);
}
}
@@ -265,4 +285,13 @@ NSString* RKPathAppendQueryParams(NSString* resourcePath, NSDictionary* queryPar
}
}
- (void)reachabilityWasDetermined:(NSNotification*)notification {
RKReachabilityObserver* observer = (RKReachabilityObserver*) [notification object];
NSAssert(observer == _baseURLReachabilityObserver, @"Received unexpected reachability notification from inappropriate reachability observer");
RKLogDebug(@"Reachability to host '%@' determined for client %@, unsuspending main queue", observer.hostName, self);
[RKRequestQueue sharedQueue].suspended = NO;
[[NSNotificationCenter defaultCenter] removeObserver:self name:RKReachabilityStateWasDeterminedNotification object:observer];
}
@end

View File

@@ -13,6 +13,7 @@
* Posted when the network state has changed
*/
extern NSString* const RKReachabilityStateChangedNotification;
extern NSString* const RKReachabilityStateWasDeterminedNotification;
typedef enum {
RKReachabilityIndeterminate,
@@ -29,9 +30,16 @@ typedef enum {
* code sample: http://developer.apple.com/library/ios/#samplecode/Reachability/Listings/Classes_Reachability_m.html
*/
@interface RKReachabilityObserver : NSObject {
SCNetworkReachabilityRef _reachabilityRef;
NSString* _hostName;
SCNetworkReachabilityRef _reachabilityRef;
BOOL _reachabilityEstablished;
}
/**
The hostname we are observing reachability to
*/
@property (nonatomic, readonly) NSString* hostName;
/**
* Create a new reachability observer against a given hostname. The observer
* will monitor the ability to reach the specified hostname and emit notifications
@@ -39,7 +47,7 @@ typedef enum {
*
* Note that the observer will be scheduled in the current run loop.
*/
+ (RKReachabilityObserver*)reachabilityObserverWithHostName:(NSString*)hostName;
- (id)initWithHostname:(NSString*)hostName;
/**
* Returns the current network status

View File

@@ -13,15 +13,26 @@
#import "RKReachabilityObserver.h"
#include <netdb.h>
#include <arpa/inet.h>
#import "RKLog.h"
#import "../Support/RKLog.h"
// Set Logging Component
#undef RKLogComponent
#define RKLogComponent lcl_cRestKitNetworkReachability
@interface RKReachabilityObserver (Private)
@property (nonatomic, assign) BOOL reachabilityEstablished;
// Internal initializer
- (id)initWithReachabilityRef:(SCNetworkReachabilityRef)reachabilityRef;
- (void)scheduleObserver;
- (void)unscheduleObserver;
@end
// Constants
NSString* const RKReachabilityStateChangedNotification = @"RKReachabilityStateChangedNotification";
static bool hasNetworkAvailabilityBeenDetermined = NO;
NSString* const RKReachabilityStateWasDeterminedNotification = @"RKReachabilityStateWasDeterminedNotification";
static void ReachabilityCallback(SCNetworkReachabilityRef target, SCNetworkReachabilityFlags flags, void* info) {
#pragma unused (target, flags)
@@ -31,7 +42,11 @@ static void ReachabilityCallback(SCNetworkReachabilityRef target, SCNetworkReach
RKReachabilityObserver* observer = (RKReachabilityObserver*) info;
hasNetworkAvailabilityBeenDetermined = YES;
if (!observer.reachabilityEstablished) {
RKLogInfo(@"Network availability has been determined for reachability observer %@", observer);
observer.reachabilityEstablished = YES;
[[NSNotificationCenter defaultCenter] postNotificationName:RKReachabilityStateWasDeterminedNotification object:observer];
}
// Post a notification to notify the client that the network reachability changed.
[[NSNotificationCenter defaultCenter] postNotificationName:RKReachabilityStateChangedNotification object:observer];
@@ -41,66 +56,62 @@ static void ReachabilityCallback(SCNetworkReachabilityRef target, SCNetworkReach
#pragma mark -
@interface RKReachabilityObserver (Private)
// Internal initializer
- (id)initWithReachabilityRef:(SCNetworkReachabilityRef)reachabilityRef;
- (void)scheduleObserver;
- (void)unscheduleObserver;
@end
@implementation RKReachabilityObserver
+ (RKReachabilityObserver*)reachabilityObserverWithHostName:(NSString*)hostName {
RKReachabilityObserver* observer = nil;
SCNetworkReachabilityRef reachabilityRef;
// Try to determine if we have an IP address or a hostname
struct sockaddr_in sa;
char* hostNameOrIPAddress = (char*) [hostName UTF8String];
int result = inet_pton(AF_INET, hostNameOrIPAddress, &(sa.sin_addr));
if (result != 0) {
// IP Address
struct sockaddr_in remote_saddr;
bzero(&remote_saddr, sizeof(struct sockaddr_in));
remote_saddr.sin_len = sizeof(struct sockaddr_in);
remote_saddr.sin_family = AF_INET;
inet_aton(hostNameOrIPAddress, &(remote_saddr.sin_addr));
reachabilityRef = SCNetworkReachabilityCreateWithAddress(CFAllocatorGetDefault(), (struct sockaddr*)&remote_saddr);
// We can immediately determine reachability to an IP address
hasNetworkAvailabilityBeenDetermined = YES;
} else {
// Hostname
reachabilityRef = SCNetworkReachabilityCreateWithName(CFAllocatorGetDefault(), hostNameOrIPAddress);
}
if (nil != reachabilityRef) {
observer = [[[self alloc] initWithReachabilityRef:reachabilityRef] autorelease];
}
return observer;
}
@synthesize hostName = _hostName;
- (id)initWithReachabilityRef:(SCNetworkReachabilityRef)reachabilityRef {
self = [self init];
if (self) {
_reachabilityRef = reachabilityRef;
[self scheduleObserver];
}
return self;
- (id)initWithHostname:(NSString*)hostName {
self = [self init];
if (self) {
_hostName = [hostName retain];
// Try to determine if we have an IP address or a hostname
struct sockaddr_in sa;
char* hostNameOrIPAddress = (char*) [hostName UTF8String];
int result = inet_pton(AF_INET, hostNameOrIPAddress, &(sa.sin_addr));
if (result != 0) {
// IP Address
struct sockaddr_in remote_saddr;
bzero(&remote_saddr, sizeof(struct sockaddr_in));
remote_saddr.sin_len = sizeof(struct sockaddr_in);
remote_saddr.sin_family = AF_INET;
inet_aton(hostNameOrIPAddress, &(remote_saddr.sin_addr));
_reachabilityRef = SCNetworkReachabilityCreateWithAddress(CFAllocatorGetDefault(), (struct sockaddr*)&remote_saddr);
// We can immediately determine reachability to an IP address
_reachabilityEstablished = YES;
RKLogInfo(@"Reachability observer initialized with IP address %@.", hostName);
RKLogDebug(@"Reachability observer initialized with IP address, automatically marking reachability as determined.");
} else {
// Hostname
_reachabilityRef = SCNetworkReachabilityCreateWithName(CFAllocatorGetDefault(), hostNameOrIPAddress);
RKLogInfo(@"Reachability observer initialized with hostname %@", hostName);
}
if (_reachabilityRef) {
[self scheduleObserver];
} else {
RKLogWarning(@"Unable to initialize reachability reference");
}
}
return self;
}
- (void)dealloc {
[[NSNotificationCenter defaultCenter] removeObserver:self];
[self unscheduleObserver];
if (_reachabilityRef) {
CFRelease(_reachabilityRef);
}
[super dealloc];
RKLogTrace(@"Deallocating reachability observer %@", self);
[[NSNotificationCenter defaultCenter] removeObserver:self];
[self unscheduleObserver];
if (_reachabilityRef) {
CFRelease(_reachabilityRef);
}
[super dealloc];
}
- (RKReachabilityNetworkStatus)networkStatus {
@@ -108,21 +119,36 @@ static void ReachabilityCallback(SCNetworkReachabilityRef target, SCNetworkReach
RKReachabilityNetworkStatus status = RKReachabilityNotReachable;
SCNetworkReachabilityFlags flags;
if (!hasNetworkAvailabilityBeenDetermined) {
if (!self.reachabilityEstablished) {
RKLogTrace(@"Reachability observer %@ has not yet established reachability. networkStatus = %@", self, @"RKReachabilityIndeterminate");
return RKReachabilityIndeterminate;
}
if (SCNetworkReachabilityGetFlags(_reachabilityRef, &flags)) {
if (SCNetworkReachabilityGetFlags(_reachabilityRef, &flags)) {
RKLogTrace(@"Reachability Flag Status: %c%c %c%c%c%c%c%c%c \n",
(flags & kSCNetworkReachabilityFlagsIsWWAN) ? 'W' : '-',
(flags & kSCNetworkReachabilityFlagsReachable) ? 'R' : '-',
(flags & kSCNetworkReachabilityFlagsTransientConnection) ? 't' : '-',
(flags & kSCNetworkReachabilityFlagsConnectionRequired) ? 'c' : '-',
(flags & kSCNetworkReachabilityFlagsConnectionOnTraffic) ? 'C' : '-',
(flags & kSCNetworkReachabilityFlagsInterventionRequired) ? 'i' : '-',
(flags & kSCNetworkReachabilityFlagsConnectionOnDemand) ? 'D' : '-',
(flags & kSCNetworkReachabilityFlagsIsLocalAddress) ? 'l' : '-',
(flags & kSCNetworkReachabilityFlagsIsDirect) ? 'd' : '-'
);
if ((flags & kSCNetworkReachabilityFlagsReachable) == 0) {
// if target host is not reachable
RKLogTrace(@"Reachability observer %@ determined networkStatus = %@", self, @"RKReachabilityNotReachable");
return RKReachabilityNotReachable;
}
if ((flags & kSCNetworkReachabilityFlagsConnectionRequired) == 0) {
// if target host is reachable and no connection is required
// then we'll assume (for now) that your on Wi-Fi
status = RKReachabilityReachableViaWiFi;
RKLogTrace(@"Reachability observer %@ determined networkStatus = %@", self, @"RKReachabilityReachableViaWiFi");
status = RKReachabilityReachableViaWiFi;
}
@@ -134,6 +160,7 @@ static void ReachabilityCallback(SCNetworkReachabilityRef target, SCNetworkReach
if ((flags & kSCNetworkReachabilityFlagsInterventionRequired) == 0) {
// ... and no [user] intervention is needed
status = RKReachabilityReachableViaWiFi;
RKLogTrace(@"Reachability observer %@ determined networkStatus = %@", self, @"RKReachabilityReachableViaWiFi");
}
}
@@ -142,6 +169,7 @@ static void ReachabilityCallback(SCNetworkReachabilityRef target, SCNetworkReach
// ... but WWAN connections are OK if the calling application
// is using the CFNetwork (CFSocketStream?) APIs.
status = RKReachabilityReachableViaWWAN;
RKLogTrace(@"Reachability observer %@ determined networkStatus = %@", self, @"RKReachabilityReachableViaWWAN");
}
#endif
}
@@ -150,16 +178,21 @@ static void ReachabilityCallback(SCNetworkReachabilityRef target, SCNetworkReach
}
- (BOOL)isNetworkReachable {
return (RKReachabilityNotReachable != [self networkStatus]);
BOOL reachable = (RKReachabilityNotReachable != [self networkStatus]);
RKLogDebug(@"Reachability observer %@ determined isNetworkReachable = %d", self, reachable);
return reachable;
}
- (BOOL)isConnectionRequired {
NSAssert(_reachabilityRef != NULL, @"connectionRequired called with NULL reachabilityRef");
SCNetworkReachabilityFlags flags;
BOOL required = NO;
if (SCNetworkReachabilityGetFlags(_reachabilityRef, &flags)) {
return (flags & kSCNetworkReachabilityFlagsConnectionRequired);
required = (flags & kSCNetworkReachabilityFlagsConnectionRequired);
}
return NO;
RKLogDebug(@"Reachability observer %@ determined isConnectionRequired = %d", self, required);
return required;
}
#pragma mark Observer scheduling
@@ -167,16 +200,28 @@ static void ReachabilityCallback(SCNetworkReachabilityRef target, SCNetworkReach
- (void)scheduleObserver {
SCNetworkReachabilityContext context = {0, self, NULL, NULL, NULL};
if (SCNetworkReachabilitySetCallback(_reachabilityRef, ReachabilityCallback, &context)) {
RKLogDebug(@"Scheduling reachability observer %@ in current run loop", self);
if (NO == SCNetworkReachabilityScheduleWithRunLoop(_reachabilityRef, CFRunLoopGetCurrent(), kCFRunLoopDefaultMode)) {
RKLogWarning(@"Warning -- Unable to schedule reachability observer in current run loop.");
}
}
}
- (void)unscheduleObserver {
if (nil != _reachabilityRef) {
- (void)unscheduleObserver {
if (_reachabilityRef) {
RKLogDebug(@"Unscheduling reachability observer %@ from current run loop", self);
SCNetworkReachabilityUnscheduleFromRunLoop(_reachabilityRef, CFRunLoopGetCurrent(), kCFRunLoopDefaultMode);
}
} else {
RKLogDebug(@"Failed to unschedule reachability observer %@: reachability reference is nil.", _reachabilityRef);
}
}
- (BOOL)reachabilityEstablished {
return _reachabilityEstablished;
}
- (void)setReachabilityEstablished:(BOOL)reachabilityEstablished {
_reachabilityEstablished = reachabilityEstablished;
}
@end

View File

@@ -281,6 +281,11 @@ typedef enum RKRequestBackgroundPolicy {
*/
- (BOOL)isLoaded;
/**
* Returnes YES when this request has not yet been sent
*/
- (BOOL)isUnsent;
/**
* Returns YES when the request was sent to the specified resource path
*/

View File

@@ -470,6 +470,10 @@
return _isLoaded;
}
- (BOOL)isUnsent {
return _isLoading == NO && _isLoaded == NO;
}
- (NSString*)resourcePath {
NSString* resourcePath = nil;
if ([self.URL isKindOfClass:[RKURL class]]) {

View File

@@ -37,11 +37,12 @@ static NSDateFormatter* __rfc1123DateFormatter;
}
- (id)initWithCachePath:(NSString*)cachePath storagePolicy:(RKRequestCacheStoragePolicy)storagePolicy {
if ((self = [super init])) {
self = [super init];
if (self) {
_cachePath = [cachePath copy];
_cacheLock = [[NSRecursiveLock alloc] init];
NSFileManager* fileManager = [[NSFileManager alloc] init];
NSFileManager* fileManager = [NSFileManager defaultManager];
NSArray* pathArray = [NSArray arrayWithObjects:
_cachePath,
[_cachePath stringByAppendingPathComponent:sessionCacheFolder],
@@ -62,8 +63,10 @@ static NSDateFormatter* __rfc1123DateFormatter;
} else {
RKLogDebug(@"Created cache storage at path '%@'", path);
}
} else if (!isDirectory) {
RKLogWarning(@"Failed to create cache directory: Directory already exists: %@", path);
} else {
if (!isDirectory) {
RKLogWarning(@"Skipped creation of cache directory as non-directory file exists at path: %@", path);
}
}
}
@@ -71,6 +74,7 @@ static NSDateFormatter* __rfc1123DateFormatter;
self.storagePolicy = storagePolicy;
}
return self;
}
@@ -110,7 +114,7 @@ static NSDateFormatter* __rfc1123DateFormatter;
[_cacheLock lock];
BOOL hasEntryForRequest = NO;
NSFileManager* fileManager = [[NSFileManager alloc] init];
NSFileManager* fileManager = [NSFileManager defaultManager];
NSString* cachePath = [self pathForRequest:request];
hasEntryForRequest = ([fileManager fileExistsAtPath:cachePath] &&
@@ -136,12 +140,12 @@ static NSDateFormatter* __rfc1123DateFormatter;
if (cachePath) {
NSData* body = response.body;
if (body) {
BOOL success = [body writeToFile:cachePath atomically:NO];
NSError* error = nil;
BOOL success = [body writeToFile:cachePath options:NSDataWritingAtomic error:&error];
if (success) {
RKLogTrace(@"Wrote cached response body to path '%@'", cachePath);
RKLogTrace(@"Wrote cached response body to path '%@'", cachePath);
} else {
RKLogError(@"Failed to write cached response body to path '%@'", cachePath);
RKLogError(@"Failed to write cached response body to path '%@': %@", cachePath, [error localizedDescription]);
}
}
@@ -247,7 +251,7 @@ static NSDateFormatter* __rfc1123DateFormatter;
NSString* cachePath = [self pathForRequest:request];
if (cachePath) {
NSFileManager* fileManager = [[NSFileManager alloc] init];
NSFileManager* fileManager = [NSFileManager defaultManager];
[fileManager removeItemAtPath:cachePath error:NULL];
[fileManager removeItemAtPath:[cachePath stringByAppendingPathExtension:headersExtension]
error:NULL];
@@ -270,7 +274,7 @@ static NSDateFormatter* __rfc1123DateFormatter;
}
RKLogInfo(@"Invalidating cache at path: %@", cachePath);
NSFileManager* fileManager = [[NSFileManager alloc] init];
NSFileManager* fileManager = [NSFileManager defaultManager];
BOOL isDirectory = NO;
BOOL fileExists = [fileManager fileExistsAtPath:cachePath isDirectory:&isDirectory];

View File

@@ -13,7 +13,6 @@
#import "RKRequestQueue.h"
#import "RKResponse.h"
#import "RKNotifications.h"
#import "RKClient.h"
#import "../Support/RKLog.h"
static RKRequestQueue* gSharedQueue = nil;
@@ -67,15 +66,7 @@ static const NSTimeInterval kFlushDelay = 0.3;
_concurrentRequestsLimit = 5;
_requestTimeout = 300;
_showsNetworkActivityIndicatorWhenBusy = NO;
[[NSNotificationCenter defaultCenter] addObserver:self
selector:@selector(responseDidLoad:)
name:RKResponseReceivedNotification
object:nil];
[[NSNotificationCenter defaultCenter] addObserver:self
selector:@selector(responseDidLoad:)
name:RKRequestFailedWithErrorNotification
object:nil];
#if TARGET_OS_IPHONE
BOOL backgroundOK = &UIApplicationDidEnterBackgroundNotification != NULL;
if (backgroundOK) {
@@ -152,14 +143,24 @@ static const NSTimeInterval kFlushDelay = 0.3;
}
}
- (RKRequest*)nextRequest {
for (NSUInteger i = 0; i < [_requests count]; i++) {
RKRequest* request = [_requests objectAtIndex:i];
if ([request isUnsent]) {
return request;
}
}
return nil;
}
- (void)loadNextInQueue {
// This makes sure that the Request Queue does not fire off any requests until the Reachability state has been determined.
if ([[[RKClient sharedClient] baseURLReachabilityObserver] networkStatus] == RKReachabilityIndeterminate ||
self.suspended) {
if (self.suspended) {
_queueTimer = nil;
[self loadNextInQueueDelayed];
RKLogTrace(@"Deferring queue loading because of %@", self.suspended ? @"queue suspension" : @"indeterminate network condition");
RKLogTrace(@"Deferring request loading for queue %@ due to suspension", self);
return;
}
@@ -167,32 +168,22 @@ static const NSTimeInterval kFlushDelay = 0.3;
_queueTimer = nil;
NSUInteger initialCount = [_requests count];
NSUInteger requestsDequeued = 0;
while ([_requests count] > 0 && requestsDequeued < initialCount) {
RKRequest* request = [_requests objectAtIndex:0];
requestsDequeued++;
RKRequest* request = [self nextRequest];
while (request && self.loadingCount < _concurrentRequestsLimit) {
RKLogTrace(@"Processing request %@ in queue %@", request, self);
if ([_delegate respondsToSelector:@selector(requestQueue:willSendRequest:)]) {
[_delegate requestQueue:self willSendRequest:request];
}
if ([request isLoading]) {
RKLogTrace(@"Skipping request %@: currently loading.", request);
} else if ([request isLoaded]) {
RKLogTrace(@"Skipping request %@: already loaded.", request);
} else if (self.loadingCount > _concurrentRequestsLimit) {
RKLogTrace(@"Skipping request %@: Maximum concurrent request limit of %d is reached", request, self.loadingCount);
} else {
if ([_delegate respondsToSelector:@selector(requestQueue:willSendRequest:)]) {
[_delegate requestQueue:self willSendRequest:request];
}
self.loadingCount = self.loadingCount + 1;
[request sendAsynchronously];
RKLogDebug(@"Sent request %@ from top of queue %@. Loading count = %d", request, self, self.loadingCount);
self.loadingCount = self.loadingCount + 1;
[request sendAsynchronously];
RKLogDebug(@"Sent request %@ from queue %@. Loading count = %d of %d", request, self, self.loadingCount, _concurrentRequestsLimit);
if ([_delegate respondsToSelector:@selector(requestQueue:didSendRequest:)]) {
[_delegate requestQueue:self didSendRequest:request];
}
}
if ([_delegate respondsToSelector:@selector(requestQueue:didSendRequest:)]) {
[_delegate requestQueue:self didSendRequest:request];
}
request = [self nextRequest];
}
if (_requests.count && !_suspended) {
@@ -235,9 +226,38 @@ static const NSTimeInterval kFlushDelay = 0.3;
RKLogTrace(@"Request %@ added to queue %@", request, self);
[_requests addObject:request];
[[NSNotificationCenter defaultCenter] addObserver:self
selector:@selector(responseDidLoad:)
name:RKResponseReceivedNotification
object:request];
[[NSNotificationCenter defaultCenter] addObserver:self
selector:@selector(responseDidLoad:)
name:RKRequestFailedWithErrorNotification
object:request];
[self loadNextInQueue];
}
- (BOOL)removeRequest:(RKRequest*)request decrementCounter:(BOOL)decrementCounter {
if ([self containsRequest:request]) {
RKLogTrace(@"Removing request %@ from queue %@", request, self);
[_requests removeObject:request];
[[NSNotificationCenter defaultCenter] removeObserver:self name:RKResponseReceivedNotification object:request];
[[NSNotificationCenter defaultCenter] removeObserver:self name:RKRequestFailedWithErrorNotification object:request];
if (decrementCounter) {
self.loadingCount = self.loadingCount - 1;
RKLogTrace(@"Decremented the loading count to %d", self.loadingCount);
}
return YES;
}
RKLogWarning(@"Failed to remove request %@ from queue %@: it is not in the queue.", request, self);
return NO;
}
- (BOOL)containsRequest:(RKRequest*)request {
return [_requests containsObject:request];
}
@@ -246,7 +266,8 @@ static const NSTimeInterval kFlushDelay = 0.3;
if (![request isLoading]) {
RKLogDebug(@"Canceled undispatched request %@ and removed from queue %@", request, self);
[_requests removeObject:request];
// Do not decrement counter
[self removeRequest:request decrementCounter:NO];
request.delegate = nil;
if ([_delegate respondsToSelector:@selector(requestQueue:didCancelRequest:)]) {
@@ -261,9 +282,9 @@ static const NSTimeInterval kFlushDelay = 0.3;
if ([_delegate respondsToSelector:@selector(requestQueue:didCancelRequest:)]) {
[_delegate requestQueue:self didCancelRequest:request];
}
[_requests removeObject:request];
self.loadingCount = self.loadingCount - 1;
// Decrement the counter
[self removeRequest:request decrementCounter:YES];
if (loadNext) {
[self loadNextInQueue];
@@ -324,8 +345,8 @@ static const NSTimeInterval kFlushDelay = 0.3;
if ([notification.object isKindOfClass:[RKResponse class]]) {
RKLogTrace(@"Received response for request %@, removing from queue.", request);
[_requests removeObject:request];
self.loadingCount = self.loadingCount - 1;
// Decrement the counter
[self removeRequest:request decrementCounter:YES];
if ([_delegate respondsToSelector:@selector(requestQueue:didLoadResponse:)]) {
[_delegate requestQueue:self didLoadResponse:(RKResponse*)notification.object];
@@ -335,8 +356,8 @@ static const NSTimeInterval kFlushDelay = 0.3;
} else if ([notification.object isKindOfClass:[RKRequest class]]) {
RKLogTrace(@"Received failure notification for request %@, removing from queue.", request);
[_requests removeObject:request];
self.loadingCount = self.loadingCount - 1;
// Decrement the counter
[self removeRequest:request decrementCounter:YES];
NSDictionary* userInfo = [notification userInfo];
NSError* error = nil;