Created
July 31, 2011 02:28
-
-
Save rob-brown/1116294 to your computer and use it in GitHub Desktop.
A singleton that can be safely subclassed to reduce code duplication.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// | |
// 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 |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// | |
// 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 |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// | |
// 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 |
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.
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Don't forget to include
libobjc.dylib
sinceRBSingleton
uses the Objective-C dynamic runtime.