Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with
or
.
Download ZIP
Browse files

initial commit

  • Loading branch information...
commit 47056c4f0c5cc81ddeda4343be7c0cf4cbd51b2b 0 parents
@wbyoung wbyoung authored
2  .gitignore
@@ -0,0 +1,2 @@
+.DS_Store
+build
49 FRClassSetup.h
@@ -0,0 +1,49 @@
+//
+// Copyright (c) 2011 FadingRed LLC
+//
+// 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>
+
+typedef void (*FRClassSetupCallback)(Class class, BOOL dynamicLoaded);
+
+/*!
+ \brief Add a class setup callback
+ \details The class setup callback will be called immediately for all classes in the runtime.
+ It will also be called when FREnsureClassCallbacksHaveBeenCalled is called (either
+ explicaitly or because a library was loaded). Returns TRUE if the callback was added.
+ */
+BOOL FRAddClassSetupCallback(FRClassSetupCallback);
+
+/*!
+ \brief Remove a class setup callback
+ \details The class setup callback will no longer be used. Returns TRUE if the callback was removed.
+ */
+BOOL FRRemoveClassSetupCallback(FRClassSetupCallback);
+
+/*!
+ \brief Ensure the class setup callbacks have been performed for all classes.
+ \details This will make class setup callbacks for any classes that have been added to the runtime
+ (the method may safely be called multiple times). You should call this method if you're
+ dynamically adding classes that need to use features depending on class setup callbacks.
+ Additionally, the implementation that is paired with this file will watch for any images
+ that get loaded into the process. This is useful, but uses a private function from the dyld
+ API. If you wish to disable this feature, define RED_SINGLETON_DISABLE_DYLD_PRIVATE_API_USE
+ during compilation. If you choose to disable the use of the dyld API, you may wish to call
+ FREnsureClassCallbacksHaveBeenCalled early on in your application's startup (and/or) after
+ you load any code in order to ensure that features depending on class setup callbacks work
+ properly.
+ */
+void FREnsureClassCallbacksHaveBeenCalled(void);
155 FRClassSetup.m
@@ -0,0 +1,155 @@
+//
+// Copyright (c) 2011 FadingRed LLC
+//
+// 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 <pthread.h>
+#import <mach-o/dyld.h>
+#import <mach-o/dyld_images.h>
+#import <objc/runtime.h>
+
+#import "FRClassSetup.h"
+
+#if __has_feature(objc_arc)
+#define bridge_ptr __bridge void *
+#else
+#define bridge_ptr void *
+#endif
+
+typedef struct {
+ CFMutableSetRef functions;
+ pthread_mutex_t lock;
+} FRCallbackInfo;
+
+static FRCallbackInfo FRClassSetupCallbackInfo(void);
+static FRCallbackInfo FRClassSetupCallbackInfo(void) {
+ static FRCallbackInfo callbackFunctions = {};
+ static dispatch_once_t once;
+ dispatch_once(&once, ^{
+ callbackFunctions.functions = CFSetCreateMutable(NULL, 0, &(CFSetCallBacks){});
+ pthread_mutex_init(&callbackFunctions.lock, NULL);
+ });
+ return callbackFunctions;
+}
+
+BOOL FRAddClassSetupCallback(FRClassSetupCallback callback) {
+ FRCallbackInfo callbackInfo = FRClassSetupCallbackInfo();
+ pthread_mutex_lock(&callbackInfo.lock);
+ BOOL add = !CFSetContainsValue(callbackInfo.functions, callback);
+ if (add) {
+ CFSetAddValue(callbackInfo.functions, callback);
+
+ // call for all classes in the runtime
+ int classCount = objc_getClassList(NULL, 0);
+ if (classCount > 0) {
+ Class *classes = (Class *)malloc(sizeof(Class) * classCount);
+ classCount = objc_getClassList(classes, classCount);
+ for (int i = 0; i < classCount; i++) {
+ callback(classes[i], FALSE);
+ }
+ free(classes);
+ }
+ }
+ pthread_mutex_unlock(&callbackInfo.lock);
+ return add;
+}
+
+BOOL FRRemoveClassSetupCallback(FRClassSetupCallback callback) {
+ FRCallbackInfo callbackInfo = FRClassSetupCallbackInfo();
+ pthread_mutex_lock(&callbackInfo.lock);
+ BOOL remove = CFSetContainsValue(callbackInfo.functions, callback);
+ if (remove) {
+ CFSetRemoveValue(callbackInfo.functions, callback);
+ }
+ pthread_mutex_unlock(&callbackInfo.lock);
+ return remove;
+}
+
+static void _FREnsureClassCallbacksHaveBeenCalled(BOOL dynamic);
+static void _FREnsureClassCallbacksHaveBeenCalled(BOOL dynamic) {
+ static CFMutableSetRef setupClasses = NULL;
+ static dispatch_once_t once;
+ dispatch_once(&once, ^{
+ setupClasses = CFSetCreateMutable(NULL, 0, &(CFSetCallBacks){});
+ });
+
+ FRCallbackInfo callbackInfo = FRClassSetupCallbackInfo();
+ pthread_mutex_lock(&callbackInfo.lock);
+
+ int classCount = objc_getClassList(NULL, 0);
+ CFIndex callbackCount = CFSetGetCount(callbackInfo.functions);
+
+ // this is declared in the dynamic support header file. it can be used to re-setup the runtime
+ // support for singletons after images have been added or after classes have been created at
+ // runtime.
+ if (classCount > 0 && callbackCount > 0) {
+ Class *classes = (Class *)malloc(sizeof(Class) * classCount);
+ classCount = objc_getClassList(classes, classCount);
+
+ FRClassSetupCallback *callbacks = malloc(sizeof(FRClassSetupCallback) * callbackCount);
+ CFSetGetValues(callbackInfo.functions, (const void **)callbacks);
+
+ for (int i = 0; i < classCount; i++) {
+ Class class = classes[i];
+ if (!CFSetContainsValue(setupClasses, (bridge_ptr)class)) {
+ for (int j = 0; j < callbackCount; j++) {
+ callbacks[j](class, dynamic);
+ }
+ }
+ }
+ free(classes);
+ free(callbacks);
+ }
+
+ pthread_mutex_unlock(&callbackInfo.lock);
+}
+
+void FREnsureClassCallbacksHaveBeenCalled(void) {
+ _FREnsureClassCallbacksHaveBeenCalled(FALSE);
+}
+
+#ifndef RED_SINGLETON_DISABLE_DYLD_PRIVATE_API_USE
+
+// dyld private
+enum dyld_image_states {
+ dyld_image_state_mapped = 10, // no batch
+ dyld_image_state_dependents_mapped = 20, // only batch
+ dyld_image_state_rebased = 30,
+ dyld_image_state_bound = 40,
+ dyld_image_state_dependents_initialized = 45, // single notification
+ dyld_image_state_initialized = 50,
+ dyld_image_state_terminated = 60, // single notification
+};
+typedef const char *(*dyld_image_state_change_handler)(enum dyld_image_states, uint32_t, const struct dyld_image_info[]);
+extern void dyld_register_image_state_change_handler(enum dyld_image_states, bool, dyld_image_state_change_handler);
+
+static bool gHandlerSetup = false;
+static const char* FRImageInitializedHandler(enum dyld_image_states state, uint32_t infoCount, const struct dyld_image_info info[]);
+static const char* FRImageInitializedHandler(enum dyld_image_states state, uint32_t infoCount, const struct dyld_image_info info[]) {
+ if (gHandlerSetup) {
+ // only ensure runtime setup once the handler is fully set up since there's
+ // no need to ensure setup for all images already in the intialized state.
+ _FREnsureClassCallbacksHaveBeenCalled(TRUE);
+ }
+ return NULL;
+}
+
+static void initialize(void) __attribute__((constructor));
+static void initialize(void) {
+ dyld_register_image_state_change_handler(dyld_image_state_initialized, true, FRImageInitializedHandler);
+ gHandlerSetup = true;
+}
+
+#endif
41 FRRuntimeAdditions.h
@@ -0,0 +1,41 @@
+//
+// Copyright (c) 2011 FadingRed LLC
+//
+// 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>
+
+typedef IMP *IMPPointer;
+
+@interface NSObject (FRRuntimeAdditions)
+
+/*!
+ \brief Performs a method swizzle
+ \details Swizzles original with replacement.
+ This is a little different from traditional method swizzling, but the concept is the same. The
+ original method will be replaced by a new method, and you can have the old implementation stored
+ in an IMP pointer for use in the swizzled method. This works out to be a little safer than method
+ swizzling since the _cmd argument can be passed through to the original IMP unaltered.
+ */
++ (BOOL)swizzle:(SEL)original with:(IMP)replacement store:(IMPPointer)store;
+
+/*!
+ \brief Performs a class method swizzle
+ \details Swizzles original with replacement.
+ \see NSObject::swizzle:with:store: for details.
+ */
++ (BOOL)swizzleClassMethod:(SEL)original with:(IMP)replacement store:(IMPPointer)store;
+
+@end
50 FRRuntimeAdditions.m
@@ -0,0 +1,50 @@
+//
+// Copyright (c) 2011 FadingRed LLC
+//
+// 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/runtime.h>
+
+#import "FRRuntimeAdditions.h"
+
+BOOL class_swizzleMethodAndStore(Class class, SEL original, IMP replacement, IMPPointer store);
+BOOL class_swizzleMethodAndStore(Class class, SEL original, IMP replacement, IMPPointer store) {
+ BOOL success = FALSE;
+ IMP imp = NULL;
+ Method method = class_getInstanceMethod(class, original);
+ if (method) {
+ // ensure the method is defined in this class by trying to add it to the class, get the new method
+ // pointer if it was actually added (this is now considered the original), then get the imp for the
+ // original method & replace the method.
+ const char *type = method_getTypeEncoding(method);
+ if (class_addMethod(class, original, method_getImplementation(method), type)) {
+ method = class_getInstanceMethod(class, original);
+ }
+ imp = method_getImplementation(method);
+ success = TRUE;
+ class_replaceMethod(class, original, replacement, type);
+ }
+ if (imp && store) { *store = imp; }
+ return success;
+}
+
+@implementation NSObject (FRRuntimeAdditions)
++ (BOOL)swizzle:(SEL)original with:(IMP)replacement store:(IMPPointer)store {
+ return class_swizzleMethodAndStore(self, original, replacement, store);
+}
++ (BOOL)swizzleClassMethod:(SEL)original with:(IMP)replacement store:(IMPPointer)store {
+ return class_swizzleMethodAndStore(object_getClass(self), original, replacement, store);
+}
+@end
28 FRSingleton.h
@@ -0,0 +1,28 @@
+//
+// Copyright (c) 2011 FadingRed LLC
+//
+// 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>
+
+/*!
+ \brief Classes that implement this protocol will be singletons
+ \details The class will return the same object every time alloc is called. The alloc method is
+ still thread safe and can be used to simply implement shared instance class methods.
+ Relies on class setup callbacks.
+ \see FREnsureClassCallbacksHaveBeenCalled
+ */
+@protocol FRSingleton
+@end
257 FRSingleton.m
@@ -0,0 +1,257 @@
+//
+// Copyright (c) 2011 FadingRed LLC
+//
+// 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 <dlfcn.h>
+#import <objc/runtime.h>
+
+#import "FRSingleton.h"
+#import "FRClassSetup.h"
+#import "FRRuntimeAdditions.h"
+
+#if __has_feature(objc_arc)
+typedef __unsafe_unretained id unid;
+#define bridge_ptr __bridge void *
+#define bridge_obj __bridge id
+#define obj_explicit_retain(x) (__bridge id)(__bridge_retained void *)x
+#else
+typedef id unid;
+#define bridge_ptr void *
+#define bridge_obj id
+#define obj_explicit_retain(x) [x retain]
+#endif
+
+@interface NSObject (FRObjectSingletonAdditionsDynamic)
++ (BOOL)hasSingletonProtocol;
++ (unid)singletonObjectWithZone:(NSZone *)zone; // must call hasSingletonProtocol first
+@end
+
+// load & setup
+static void FRSingletonLoad(unid self);
+static void FRSingletonClassSetup(Class self, BOOL dynamic);
+
+// swizzling
+static void *(*SSingletonAllocWithZone)(unid self, SEL _cmd, NSZone *zone) = NULL;
+static void *(FRSingletonAllocWithZone)(unid self, SEL _cmd, NSZone *zone);
+
+// dynamic methods
+static BOOL FRSingletonProtocolYes(unid self, SEL _cmd);
+static BOOL FRSingletonProtocolNo(unid self, SEL _cmd);
+static BOOL FRSingletonProtocolInitialCheck(unid self, SEL _cmd);
+static unid FRSingletonAllocWithZoneBlockIMP(unid self, SEL _cmd, NSZone *zone);
+static unid FRSingletonAllocWithZoneAssociations(unid self, SEL _cmd, NSZone *zone);
+
+// helpers & debugging
+static unid FRSingletonCreate(unid self, SEL _cmd, NSZone *zone);
+static unid FRSingletonVerify(unid self, SEL _cmd, unid singleton, Class assocclass);
+static Class FRSingletonAssociationClass(unid self);
+unid red_singleton_debug(void);
+
+// globals (constant, calculated during load)
+static const char *gBoolMethodType = NULL;
+static const char *gAllocMethodType = NULL;
+IMP (*gIMP_implementationWithBlockFunc)(void *block) = NULL;
+
+
+@implementation NSObject (FRObjectSingletonAdditions)
+
+#pragma mark -
+#pragma mark general setup
+// ----------------------------------------------------------------------------------------------------
+// general setup
+// ----------------------------------------------------------------------------------------------------
+
++ (void)load {
+ // need to get the type encoding for bool methods
+ unid objectMetaClass = object_getClass([NSObject class]);
+ Method boolMethod = class_getInstanceMethod(objectMetaClass, @selector(isProxy));
+ Method objectMethod = class_getInstanceMethod(objectMetaClass, @selector(allocWithZone:));
+ gBoolMethodType = method_getTypeEncoding(boolMethod);
+ gAllocMethodType = method_getTypeEncoding(objectMethod);
+ gIMP_implementationWithBlockFunc = dlsym(RTLD_DEFAULT, "imp_implementationWithBlock");
+
+ // perform general setup
+ FRAddClassSetupCallback(FRSingletonClassSetup);
+ FRSingletonLoad(self);
+}
+
+
+#pragma mark -
+#pragma mark singleton load
+// ----------------------------------------------------------------------------------------------------
+// singleton load
+// ----------------------------------------------------------------------------------------------------
+
+static void FRSingletonLoad(unid self) {
+ [self swizzleClassMethod:@selector(allocWithZone:)
+ with:(IMP)FRSingletonAllocWithZone
+ store:(IMPPointer)&SSingletonAllocWithZone];
+}
+
+
+#pragma mark -
+#pragma mark singleton setup (for every class)
+// ----------------------------------------------------------------------------------------------------
+// singleton setup (for every class)
+// ----------------------------------------------------------------------------------------------------
+
+static void FRSingletonClassSetup(Class class, BOOL dynamic) {
+ // we explicitly add a class method for singleton checks to every class to ensure that all classes
+ // handle singleton checking on their own.
+ Class metaClass = object_getClass(class);
+ SEL selector = @selector(hasSingletonProtocol);
+ class_addMethod(metaClass, selector, (IMP)FRSingletonProtocolInitialCheck, gBoolMethodType);
+}
+
+
+#pragma mark -
+#pragma mark helper methods
+// ----------------------------------------------------------------------------------------------------
+// helper methods
+// ----------------------------------------------------------------------------------------------------
+
+static unid FRSingletonCreate(unid self, SEL _cmd, NSZone *zone) {
+ id singleton = [(bridge_obj)SSingletonAllocWithZone(self, _cmd, zone) init];
+ // from now on, init should simply return self. the standard alloc, init sequence will always return
+ // the singleton and will only perform init on it once. this allows use of the singleton in nib files
+ // when it has already been used before nib loading occurs.
+ [self swizzle:@selector(init) with:[self instanceMethodForSelector:@selector(self)] store:NULL];
+ return singleton;
+}
+
+static unid FRSingletonVerify(unid self, SEL _cmd, unid singleton, Class assocclass) {
+ if (assocclass != self && ![singleton isKindOfClass:self]) {
+ NSLog(@"An attempt to access a singleton through %@ was "
+ @"made after first creating the singleton through %@. "
+ @"The result of this atempt has been set to is nil. "
+ @"Break on red_singleton_debug to debug.", self, assocclass);
+ singleton = red_singleton_debug();
+ }
+ return singleton;
+}
+
+static Class FRSingletonAssociationClass(unid self) {
+ Class assocclass = self;
+ Class superclass = [self superclass];
+ while (superclass) {
+ if (![superclass hasSingletonProtocol]) { break; }
+ assocclass = superclass;
+ superclass = [superclass superclass];
+ }
+ return assocclass;
+}
+
+
+#pragma mark -
+#pragma mark debug methods
+// ----------------------------------------------------------------------------------------------------
+// debug methods
+// ----------------------------------------------------------------------------------------------------
+
+unid red_singleton_debug(void) {
+ return nil;
+}
+
+
+#pragma mark -
+#pragma mark dynamic methods
+// ----------------------------------------------------------------------------------------------------
+// dynamic methods
+// ----------------------------------------------------------------------------------------------------
+
+static BOOL FRSingletonProtocolYes(unid self, SEL _cmd) { return YES; }
+static BOOL FRSingletonProtocolNo(unid self, SEL _cmd) { return NO; }
+static BOOL FRSingletonProtocolInitialCheck(unid self, SEL _cmd) {
+ // we check this one time and then from then on, we just use a cached version. it's okay to swizzle
+ // this since every class has it's own implementation of this method (thanks to the work done in
+ // the singleton setup).
+
+ // race condition: the intial check could happen for a class at the same time. if this method is
+ // called simultaneously, the result will be the same, and there will be no negative side effects.
+ BOOL has = [self conformsToProtocol:@protocol(FRSingleton)];
+ BOOL (*method)(unid, SEL) = has ?
+ FRSingletonProtocolYes :
+ FRSingletonProtocolNo;
+ [self swizzleClassMethod:@selector(hasSingletonProtocol) with:(IMP)method store:NULL];
+
+ // add the object calculation method in on the assocation class (the top most ancestor class of
+ // self that supports the singleton protocol).
+ Class assocmeta = object_getClass(FRSingletonAssociationClass(self));
+ unid (*lookup)(unid, SEL, NSZone *) = gIMP_implementationWithBlockFunc ?
+ FRSingletonAllocWithZoneBlockIMP :
+ FRSingletonAllocWithZoneAssociations;
+ class_addMethod(assocmeta, @selector(singletonObjectWithZone:), (IMP)lookup, gAllocMethodType);
+
+ return [self hasSingletonProtocol];
+}
+
+static unid FRSingletonAllocWithZoneBlockIMP(unid self, SEL _cmd, NSZone *zone) {
+ Class assocclass = FRSingletonAssociationClass(self);
+
+ __block unid singleton = nil;
+ IMP result = gIMP_implementationWithBlockFunc((bridge_ptr)^(unid _self, NSZone *_zone) {
+ return FRSingletonVerify(_self, _cmd, singleton, assocclass);
+ });
+
+ @synchronized(self) {
+ unid (*lookup)(unid, SEL, NSZone *) = (void *)[assocclass methodForSelector:@selector(singletonObjectWithZone:)];
+ if (lookup == FRSingletonAllocWithZoneBlockIMP) {
+ singleton = FRSingletonCreate(self, _cmd, zone);
+ [assocclass swizzleClassMethod:@selector(singletonObjectWithZone:) with:(IMP)result store:NULL];
+ }
+ }
+
+ return [self singletonObjectWithZone:zone];
+}
+
+static unid FRSingletonAllocWithZoneAssociations(unid self, SEL _cmd, NSZone *zone) {
+ // storing associations in a global object to avoid issues with associations when
+ // using garbage collection and classes sometimes not being allocated in a gc zone.
+ static unid FRSingletonObjects = @"FRSingletonObjects";
+ void *assocclass = (bridge_ptr)FRSingletonAssociationClass(self);
+ unid singleton = nil;
+ if ((singleton = objc_getAssociatedObject(FRSingletonObjects, assocclass)) == nil) {
+ @synchronized(self) {
+ if ((singleton = objc_getAssociatedObject(FRSingletonObjects, assocclass)) == nil) {
+ singleton = FRSingletonCreate(self, _cmd, zone);
+ objc_setAssociatedObject(FRSingletonObjects, assocclass, singleton, OBJC_ASSOCIATION_ASSIGN);
+ }
+ }
+ }
+ return FRSingletonVerify(self, _cmd, singleton, (bridge_obj)assocclass);
+}
+
+
+#pragma mark -
+#pragma mark swizzling (implementation)
+// ----------------------------------------------------------------------------------------------------
+// swizzling (implementation)
+// ----------------------------------------------------------------------------------------------------
+
+static void *FRSingletonAllocWithZone(unid self, SEL _cmd, NSZone *zone) {
+ if ([self hasSingletonProtocol]) {
+ // calling through imp to preserve the _cmd argument
+ IMP singletonObjectWithZone = [self methodForSelector:@selector(singletonObjectWithZone:)];
+ void *singleton = nil;
+ if (singletonObjectWithZone) {
+ singleton = (bridge_ptr)obj_explicit_retain(singletonObjectWithZone(self, _cmd, zone));
+ }
+ return singleton;
+ }
+ else { return SSingletonAllocWithZone(self, _cmd, zone); }
+}
+
+@end
14 License
@@ -0,0 +1,14 @@
+Copyright (c) 2011 FadingRed LLC
+
+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.
62 Makefile
@@ -0,0 +1,62 @@
+#
+# Copyright (c) 2011 FadingRed LLC
+#
+# 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.
+#
+
+# Flags for clang with ARC
+CC=clang
+CFLAGS=-std=c99 -Wall -fobjc-arc
+LDFLAGS=
+
+# # Flags for clang with GC
+# CC=clang
+# CFLAGS=-std=c99 -Wall -fobjc-gc-only
+# LDFLAGS=
+
+# # Flags for gcc
+# CC=gcc
+# CFLAGS=-std=c99 -Wall
+# LDFLAGS=
+
+SOURCES:=$(shell echo *.m)
+HEADERS:=$(shell echo *.h)
+OBJECTS:=$(foreach obj,$(SOURCES:.m=.o),build/$(obj))
+TEST_SOURCES:=$(shell echo tests/test_*.m)
+TEST_LIBSRCS:=$(shell echo tests/lib_*.m)
+TEST_HEADERS:=$(shell echo tests/test_*.h)
+TEST_EXECUTABLES:=$(foreach test,$(TEST_SOURCES:.m=),$(subst tests/,build/,$(test))) build/test_speedwithout
+TEST_LIBS:=$(foreach lib,$(TEST_LIBSRCS:.m=.dylib),$(subst tests/,build/,$(lib)))
+
+all: build $(TEST_EXECUTABLES) $(TEST_LIBS) $(OBJECTS)
+
+build:
+ mkdir -p build
+
+build/%.o: %.m $(HEADERS)
+ $(CC) -c $(CFLAGS) $< -o $@
+
+build/%.dylib: tests/%.m $(TEST_HEADERS)
+ $(CC) -dynamiclib -framework Foundation $(CFLAGS) $< -o $@
+
+build/%: tests/%.m $(OBJECTS) $(TEST_HEADERS)
+ $(CC) $(CFLAGS) -I. -framework Foundation $< $(OBJECTS) -o $@
+
+build/test_speedwithout: tests/test_speed.m $(TEST_HEADERS)
+ $(CC) $(CFLAGS) -I. -framework Foundation $< -o $@
+
+clean:
+ rm -rf build
+
+.PHONY: all clean
29 Readme.md
@@ -0,0 +1,29 @@
+# Simple Singletons
+
+This project allows you to easily make a class a singleton in Objective-C. With this project you can write:
+
+ @interface FRDatabaseManager : NSObject <FRSingleton>
+ @end
+
+And your class is now a singleton -- that's all there is to it!
+
+This method works by returning your singleton object from the alloc method. For convenience and clarity, you can also implement an accessor for the singleton:
+
+ @implementation FRDatabaseManager
+ + (id)sharedDatabaseManager { return [[self alloc] autorelease]; }
+ @end
+
+
+## Background
+
+The project was first presented at [SpikedCocoa](http://www.spikedcocoa.com/) as an example of using the Objective-C runtime to our advantage. This project is by no means intended to promote wide use of the singleton design pattern. You should be aware of the drawbacks that singletons have before using them. If you decide to use singletons, though, this will significantly decrease the code you write and you'll have a better, more consistent implementation.
+
+
+## Considerations
+
+Singleton support is achieved by this project by [swizzling](http://www.cocoadev.com/index.pl?MethodSwizzling) the `NSObject::allocWithZone:` method. The implementation takes into consideration the safety and speed implications of swizzling this method.
+
+
+## Compatibility
+
+This code is compatible with Mac OS X 10.6 and later. It is most optimized on Mac OS X 10.7, as it is able to use `IMP_implementationWithBlock`. It will compile with `gcc` and `clang` and works with Automatic Reference Counting (ARC) as well as Garbage Collection.
28 tests/lib_loadable.m
@@ -0,0 +1,28 @@
+//
+// Copyright (c) 2011 FadingRed LLC
+//
+// 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>
+
+@protocol FRSingleton
+@end
+
+@interface FRDynamicExample : NSObject <FRSingleton> {
+}
+@end
+
+@implementation FRDynamicExample
+@end
67 tests/test_compare_speed.m
@@ -0,0 +1,67 @@
+//
+// Copyright (c) 2011 FadingRed LLC
+//
+// 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>
+
+#import "FRSingleton.h"
+#import "test_macros.h"
+
+@interface FRSingletonAuto : NSObject <FRSingleton>
+@end
+@implementation FRSingletonAuto
++ (id)sharedInstance { return obj_autorelease([self alloc]); }
+@end
+
+// directly from apple's recommendation
+@interface FRSingletonManual : NSObject
+@end
+@implementation FRSingletonManual
+static FRSingletonManual *sharedInstance = nil;
++ (id)sharedInstance {
+ if (sharedInstance == nil) {
+ sharedInstance = [[super allocWithZone:NULL] init];
+ }
+ return sharedInstance;
+}
++ (id)allocWithZone:(NSZone *)zone {
+ return obj_retain([self sharedInstance]);
+}
+@end
+
+
+int main() {
+ AUTORELEASE_BEGIN;
+
+ // allocation speed
+ // --------------------------------------------------------------------------------
+ void (^go)(Class class, unsigned int count) = nil;
+ go = ^void(Class class, unsigned int count) {
+ NSDate *date = [NSDate date];
+ for (NSUInteger i = 0; i < count; i++) {
+ obj_release([class alloc]);
+ }
+ fprintf(stderr, "Averaged %f miliseconds for %u calls to %s.\n",
+ (float)(-[date timeIntervalSinceNow] * 1000), count, [[class description] UTF8String]);
+ };
+
+ unsigned int count = 100000;
+ go([FRSingletonAuto class], count);
+ go([FRSingletonManual class], count);
+
+ AUTORELEASE_END;
+ return 0;
+}
95 tests/test_general.m
@@ -0,0 +1,95 @@
+//
+// Copyright (c) 2011 FadingRed LLC
+//
+// 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>
+
+#import "FRSingleton.h"
+#import "test_macros.h"
+
+static bool deallocated;
+
+@interface FRSingleton : NSObject <FRSingleton>
+@end
+@implementation FRSingleton
+- (void)dealloc {
+ deallocated = TRUE;
+ #if !__has_feature(objc_arc)
+ [super dealloc];
+ #endif
+}
+@end
+
+@interface FRSingletonBase1 : NSObject <FRSingleton>
+@end
+@implementation FRSingletonBase1
+@end
+
+@interface FRSingletonSubclass1 : FRSingletonBase1
+@end
+@implementation FRSingletonSubclass1
+@end
+
+@interface FRSingletonBase2 : NSObject <FRSingleton>
+@end
+@implementation FRSingletonBase2
+@end
+
+@interface FRSingletonSubclass2 : FRSingletonBase2
+@end
+@implementation FRSingletonSubclass2
+@end
+
+#import <objc/message.h>
+
+int main() {
+ BOOL gcEnabled = [NSClassFromString(@"NSGarbageCollector") defaultCollector] != nil;
+ AUTORELEASE_BEGIN;
+
+ id base = nil;
+ id subclass = nil;
+
+ // test 1
+ // --------------------------------------------------------------------------------
+ FRAssertEquals(obj_autorelease([FRSingleton alloc]), obj_autorelease([[FRSingleton alloc] init]), nil);
+
+ // test 2
+ // --------------------------------------------------------------------------------
+ subclass = obj_autorelease([FRSingletonSubclass1 alloc]);
+ base = obj_autorelease([FRSingletonBase1 alloc]);
+ FRAssertEquals([subclass class], [FRSingletonSubclass1 class], nil);
+ FRAssertEquals([base class], [FRSingletonSubclass1 class], nil);
+ FRAssertEquals(subclass, base, nil);
+
+ // test 3
+ // --------------------------------------------------------------------------------
+ base = obj_autorelease([FRSingletonBase2 alloc]);
+ NSLog(@"------------- expecting error output -------------");
+ subclass = obj_autorelease([FRSingletonSubclass2 alloc]);
+ NSLog(@"-------- no longer expecting error output --------");
+ FRAssertEquals([base class], [FRSingletonBase2 class], nil);
+ FRAssertNotNil(base, nil);
+ FRAssertNil(subclass, nil);
+
+ FRAssertFalse(deallocated, nil);
+ id singleton = obj_autorelease([[FRSingleton alloc] init]);
+ obj_explicit_release(singleton);
+ FRAssertFalse(deallocated, nil);
+
+ AUTORELEASE_END;
+ FRAssertTrue(deallocated || gcEnabled, nil); // must check outside of autorelease pool
+ return 0;
+}
38 tests/test_loading.m
@@ -0,0 +1,38 @@
+//
+// Copyright (c) 2011 FadingRed LLC
+//
+// 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>
+#import <dlfcn.h>
+
+#import "FRSingleton.h"
+#import "test_macros.h"
+
+int main(int argc, char **argv) {
+ AUTORELEASE_BEGIN;
+
+ NSString *executablePath = [NSString stringWithUTF8String:argv[0]];
+ NSString *directory = [executablePath stringByDeletingLastPathComponent];
+ NSString *dylibPath = [directory stringByAppendingPathComponent:@"lib_loadable.dylib"];
+
+ dlopen([dylibPath fileSystemRepresentation], RTLD_NOW);
+ Class dynamicClass = NSClassFromString(@"FRDynamicExample");
+ FRAssertNotNil(dynamicClass, nil);
+ FRAssertEquals(obj_autorelease([dynamicClass alloc]), obj_autorelease([dynamicClass alloc]), nil);
+
+ AUTORELEASE_END;
+ return 0;
+}
51 tests/test_macros.h
@@ -0,0 +1,51 @@
+//
+// Copyright (c) 2011 FadingRed LLC
+//
+// 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.
+//
+
+#define FRAssertGeneral(condition, default_msg, fmt, ...) do {\
+ if (!(condition)) { \
+ NSString *__reason = fmt ? \
+ [NSString stringWithFormat:fmt ? fmt : @"", ##__VA_ARGS__] : default_msg; \
+ [[NSException exceptionWithName:@"TestFailure" reason:__reason userInfo:nil] raise]; \
+ } \
+} while (0)
+
+#define FRAssertEquals(arg1, arg2, fmt, ...) \
+ FRAssertGeneral(arg1 == arg2, ([NSString stringWithFormat:@"%s == %s failed", #arg1, #arg2]), fmt, ##__VA_ARGS__)
+
+#define FRAssertNotEquals(arg1, arg2, fmt, ...) \
+ FRAssertGeneral(arg1 != arg2, ([NSString stringWithFormat:@"%s != %s failed", #arg1, #arg2]), fmt, ##__VA_ARGS__)
+
+#define FRAssertNil(arg, fmt, ...) FRAssertEquals(arg, nil, fmt, ##__VA_ARGS__)
+#define FRAssertNotNil(arg, fmt, ...) FRAssertNotEquals(arg, nil, fmt, ##__VA_ARGS__)
+#define FRAssertFalse(arg, fmt, ...) FRAssertEquals(arg, FALSE, fmt, ##__VA_ARGS__)
+#define FRAssertTrue(arg, fmt, ...) FRAssertEquals(arg, TRUE, fmt, ##__VA_ARGS__)
+
+#if __has_feature(objc_arc)
+#define AUTORELEASE_BEGIN @autoreleasepool {
+#define AUTORELEASE_END }
+#define obj_retain(x) x
+#define obj_release(x) (void)x
+#define obj_autorelease(x) x
+#define obj_explicit_release(x) (void)(__bridge_transfer id)(__bridge void *)x
+#else
+#define AUTORELEASE_BEGIN NSAutoreleasePool *__pool = [[NSAutoreleasePool alloc] init];
+#define AUTORELEASE_END [__pool release];
+#define obj_retain(x) [x retain]
+#define obj_release(x) [x release]
+#define obj_autorelease(x) [x autorelease]
+#define obj_explicit_release(x) [x release]
+#endif
51 tests/test_speed.m
@@ -0,0 +1,51 @@
+//
+// Copyright (c) 2011 FadingRed LLC
+//
+// 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>
+
+#import "FRSingleton.h"
+#import "test_macros.h"
+
+int main() {
+ AUTORELEASE_BEGIN;
+
+ // allocation speed
+ // --------------------------------------------------------------------------------
+ void (^go)(Class class, SEL through, unsigned int count) = nil;
+ go = ^void(Class class, SEL through, unsigned int count) {
+ NSDate *date = [NSDate date];
+ for (NSUInteger i = 0; i < count; i++) {
+ id object = [class alloc];
+ if (through == @selector(alloc)) { continue; }
+ object = [object init];
+ if (through == @selector(init)) { continue; }
+ obj_release(object);
+ object = nil;
+ }
+ fprintf(stderr, "Averaged %f miliseconds for %u allocations through %s %s.\n",
+ (float)(-[date timeIntervalSinceNow] * 1000), count, [NSStringFromSelector(through) UTF8String],
+ [NSObject respondsToSelector:@selector(hasSingletonProtocol)] ? "with singleton support" : "normally");
+ };
+
+ unsigned int count = 100000;
+ go([NSMutableDictionary class], @selector(self), count);
+ go([NSMutableDictionary class], @selector(init), count);
+ go([NSMutableDictionary class], @selector(alloc), count);
+
+ AUTORELEASE_END;
+ return 0;
+}
Please sign in to comment.
Something went wrong with that request. Please try again.