public
Last active

A singleton that can be safely subclassed to reduce code duplication.

  • Download Gist
RBSimpleSingleton.h
Objective-C
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45
//
// RBSimpleSingleton.h
//
// Copyright (c) 2011 Robert Brown
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.
//
 
#import <Foundation/Foundation.h>
 
/**
* RBSimpleSingleton is a singleton that can be safely subclassed. There is only
* one benefit to RBSimpleSingleton. It allows you to put any common singleton
* code you may have in one location, which you probably won't ever have.
* RBSimpleSingleton is so basic it really doesn't need to exist. It's purpose
* is mostly to show an alternative to RBSingleton (or any other standard
* singleton for that matter). Classes in
* Objective-C are singletons. So we just take advantage of that fact. Since
* there is no instance, there are no intance variables (ivars). All data needed
* for this class should be put in either static class variables or static methods variables. To ensure
* thread safety, you can @synchronize(self) ('self' references the class in
* class methods) or use your own locks or Grand Central Dispatch queues. You
* may be surprised with what you can do with just a class.
*/
@interface RBSimpleSingleton : NSObject
 
// It doesn't get any more simple than this.
 
@end
RBSingleton.h
Objective-C
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74
//
// RBSingleton.h
//
// Copyright (c) 2011 Robert Brown
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.
//
 
 
#import <Foundation/Foundation.h>
 
/**
* RBSingleton is a singleton that can be safely subclassed. There are two main
* benefits to RBSingleton. First, you don't need to include the same singleton
* code in all of your singleton classes. I often forget everything that is
* needed, so I have to hunt down an old singleton and copy and paste its code.
* With RBSingleton, all you need to do is subclass it and you're done. Second,
* if something ever happens that requires you to change the standard singleton
* code, you only need to change it in one place. This situation happened to me
* when Xcode's static analyzer started recognizing singletons as memory leaks.
* I had to change all my singletons as a result.
*
* NOTE: Don't forget to include libobjc.dylib since RBSingleton uses the
* Objective-C dynamic runtime.
*/
@interface RBSingleton : NSObject
 
/**
* Returns the shared instance of this class. You may want to indirectly call
* this method with your own method to rename it as you like or change the
* return type to be specific to your class (see example 1 below). If you want
* to make the request to get your singleton more efficient, you can cache the
* singleton instance in a static class variable (see example 2 below).
*
* @code
* // Example 1
* + (MyManager *)sharedManager {
* return (MyManager *)[super sharedInstance];
* }
*
* // Example 2
* static MyManager * sharedManager = nil;
*
* + (MyManager *)sharedManager {
*
* @synchronized(self) {
*
* if(!sharedManager) {
* sharedManager = (MyManager *)[super sharedInstance];
* }
*
* return sharedManager;
* }
* }
*/
+ (id)sharedInstance;
 
@end
RBSingleton.m
Objective-C
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167
//
// RBSingleton.m
//
// Copyright (c) 2011 Robert Brown
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.
//
 
#import <objc/message.h>
 
#import "RBSingleton.h"
 
/**
* A dictionary that holds all of the shared instances for this class and all
* its subclasses. Each class should only have one instance (otherwise it
* wouldn't be a singleton). The key for each class is defined by
* -sharedInstanceKey.
*/
static NSMutableDictionary * sharedInstances = nil;
 
 
@interface RBSingleton ()
 
/**
* A getter that lazy creates the dictionary of instances.
*
* @return A dictionary with all of the allocated singleton instances.
*/
+ (NSMutableDictionary *)sharedInstances;
 
/**
* The key that is used to store the single in the sharedInstances dictionary.
* By default this returns the class's name. You should have no need to override
* this method, but it's here if you want to.
*
* @return The key that is used to store the single in the sharedInstances
* dictionary.
*/
+ (NSString *)sharedInstanceKey;
 
/**
* Simulated protected initializer. Override if you need custom initialization.
*
* @return The singleton instance.
*/
- (id)initialize;
 
@end
 
 
@implementation RBSingleton
 
 
#pragma mark - Singleton methods
 
+ (id)sharedInstance {
id sharedInstance = nil;
// Technically a singleton is a memory leak, but we won't tell the static
// analyzer that minor detail.
#if !defined(__clang_analyzer__)
NSMutableDictionary * instances = [self sharedInstances];
// We must synchronize on 'instances' to guarantee thread safety.
@synchronized(instances) {
NSString * classKey = [self sharedInstanceKey];
sharedInstance = [instances valueForKey:classKey];
if (!sharedInstance) {
// We must go straight to the grandsuper class because calling super
// is not safe. Calling super from a subclass of RBSingleton will
// call RBSingleton's implementation of allocWithZone:, which is
// designed to be a no-op. If for some reason, RBSingleton needs to
// be a sublass of some class besides NSObject, this line needs to
// be changed too. It's not hard to dynamically discover the
// grandsuper class, but since we know it, we can hard code it for
// simplicity.
Method allocMethod = class_getClassMethod([NSObject class], @selector(allocWithZone:));
sharedInstance = [method_invoke(self, allocMethod, nil) initialize];
[instances setValue:sharedInstance forKey:classKey];
}
}
#endif
return sharedInstance;
}
 
+ (NSMutableDictionary *)sharedInstances {
if (!sharedInstances)
#if __has_feature(objc_arc)
sharedInstances = [NSMutableDictionary dictionary];
#else
sharedInstances = [[NSMutableDictionary dictionary] retain];
#endif
return sharedInstances;
}
 
+ (NSString *)sharedInstanceKey {
return NSStringFromClass(self);
}
 
- (id)initialize {
return self;
}
 
+ (id)allocWithZone:(NSZone *)zone {
#if __has_feature(objc_arc)
return [self sharedInstance];
#else
// The retain is needed to satisfy the static analyzer.
return [[self sharedInstance] retain];
#endif
}
 
- (id)copyWithZone:(NSZone *)zone {
return self;
}
 
- (id)init {
return self;
}
 
#if !__has_feature(objc_arc)
 
- (id)retain {
return self;
}
 
- (oneway void)release {
// Do nothing.
}
 
- (id)autorelease {
return self;
}
 
- (NSUInteger)retainCount {
return NSUIntegerMax;
}
 
#endif
 
@end

Don't forget to include libobjc.dylib since RBSingleton uses the Objective-C dynamic runtime.

Just too bad that it returns "id" instead of the actual class. Can't use properties ([MyClass sharedInstance].myProperty) and autocompletion for methods now includes all methods from all classes. I don't see a solution though.

Please sign in to comment on this gist.

Something went wrong with that request. Please try again.