Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
19 changes: 16 additions & 3 deletions GCReusableQueue.h → KWReusableQueue/GCReusableQueue.h
Original file line number Diff line number Diff line change
Expand Up @@ -24,16 +24,29 @@
#import <Foundation/Foundation.h>

/* Make sure that objects conform to this protocol and return -reuseIdentifier. */
@protocol ReusableObject <NSObject>
@protocol GCReusableObject <NSObject>

- (NSString *)reuseIdentifier;

@optional
- (void)prepareForReuse;

@end

@interface GCReusableQueue : NSObject

- (void)enqueueReusableObject:(id <ReusableObject>)obj;
- (id <ReusableObject>)dequeueReusableObjectWithIdentifier:(NSString *)identifier;
+ (instancetype)sharedInstance;

- (void)enqueueReusableObject:(id)obj;

- (void)enqueueReusableObject:(id)obj withReuseIdentifier:(NSString *)reuseIdentifier;

/* If no identifier is specified, the pool will try to return a queued object of the same class
* with no reuse identifier. If none exists, it will return one of the same class with any reuse
* identifier. If a reuse identifier is explicitely specified here, this will only return an object
* bearing it, or nil if none exists.
*/
- (id)dequeueReusableObjectOfClass:(Class)class withIdentifier:(NSString *)identifier;

/* This method should not be used, as the queue will discard objects automatically
when memory gets tight. Use this method to discard objects manually. */
Expand Down
88 changes: 72 additions & 16 deletions GCReusableQueue.m → KWReusableQueue/GCReusableQueue.m
Original file line number Diff line number Diff line change
Expand Up @@ -24,12 +24,23 @@
#import "GCReusableQueue.h"
#import <TargetConditionals.h>

NSString *const GCNoReuseIdentifierKey = @"GCNoReuseIdentifierKey";

@implementation GCReusableQueue
{
NSCache *_reusableObjects;
id _observer;
}

+ (instancetype)sharedInstance {
static GCReusableQueue *sharedInstance = nil;
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
sharedInstance = [[GCReusableQueue alloc] init];
});
return sharedInstance;
}

- (id)init
{
self = [super init];
Expand Down Expand Up @@ -62,29 +73,74 @@ - (NSCache *)reusableObjects
return self->_reusableObjects ?: (self->_reusableObjects = [NSCache new]);
}

- (id <ReusableObject>)dequeueReusableObjectWithIdentifier:(NSString *)identifier
- (id)dequeueReusableObjectOfClass:(Class)class withIdentifier:(NSString *)identifier
{
NSParameterAssert(identifier);

NSMutableSet *objects = [[self reusableObjects] objectForKey:identifier];
NSParameterAssert(class);

id <ReusableObject> obj = [objects anyObject];

if (obj) [objects removeObject:obj];
id obj = nil;

NSDictionary *objectsByIdentifier = [[self reusableObjects] objectForKey:NSStringFromClass(class)];

if (![identifier length]) {
if ([[objectsByIdentifier valueForKey:GCNoReuseIdentifierKey] count]) {
NSMutableSet *objects = [objectsByIdentifier valueForKey:GCNoReuseIdentifierKey];
obj = [objects anyObject];
if (obj) {
[objects removeObject:obj];
}
} else {
// Look for an object with any reuse identifier
for (NSString *identifier in objectsByIdentifier) {
NSMutableSet *objects = objectsByIdentifier[identifier];
obj = [objects anyObject];

if (obj) {
[objects removeObject:obj];
break;
}
}
}
} else {
NSMutableSet *objects = [objectsByIdentifier valueForKey:identifier];
obj = [objects anyObject];
if (obj) {
[objects removeObject:obj];
}
}

if (obj && [obj respondsToSelector:@selector(prepareForReuse)]) {
[obj prepareForReuse];
}

return obj;
}

- (void)enqueueReusableObject:(id <ReusableObject>)obj
- (void)enqueueReusableObject:(id)obj
{
NSMutableSet *objects = [[self reusableObjects] objectForKey:[obj reuseIdentifier]];

if (!objects)
{
objects = [NSMutableSet set];
[[self reusableObjects] setObject:objects forKey:[obj reuseIdentifier]];
}

[self enqueueReusableObject:obj withReuseIdentifier:nil];
}

- (void)enqueueReusableObject:(id)obj withReuseIdentifier:(NSString *)reuseIdentifier {
NSMutableDictionary *objectsByReuseIdentifier = [[self reusableObjects] objectForKey:NSStringFromClass([obj class])];

if (!objectsByReuseIdentifier) {
objectsByReuseIdentifier = [NSMutableDictionary new];
[[self reusableObjects] setObject:objectsByReuseIdentifier forKey:NSStringFromClass([obj class])];
}

if (![reuseIdentifier length] && [obj respondsToSelector:@selector(reuseIdentifier)]) {
reuseIdentifier = [obj valueForKey:NSStringFromSelector(@selector(reuseIdentifier))];
}
if (![reuseIdentifier length]) {
reuseIdentifier = GCNoReuseIdentifierKey;
}

NSMutableSet *objects = [objectsByReuseIdentifier objectForKey:reuseIdentifier];
if (!objects) {
objects = [NSMutableSet set];
[objectsByReuseIdentifier setObject:objects forKey:reuseIdentifier];
}

[objects addObject:obj];
}

Expand Down
13 changes: 13 additions & 0 deletions Kwarter-KWReusableQueue.podspec
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
Pod::Spec.new do |s|
s.name = 'Kwarter-KWReusableQueue'
s.version = '1.0'
s.license = 'MIT'
s.summary = 'Class to store objects in a reusable queue to minimize memory allocations for better efficiency.'
s.homepage = 'https://github.com/kwarter/KWReusableQueue'
s.authors = 'Glenn Chiu'
s.source = { :git => 'https://github.com/kwarter/KWReusableQueue.git', :tag => '1.0' }
s.source_files = 'KWReusableQueue/'
s.requires_arc = true
s.ios.deployment_target = '4.3'
s.osx.deployment_target = '10.6'
end
Loading