Permalink
Browse files

initial commit

  • Loading branch information...
0 parents commit 47056c4f0c5cc81ddeda4343be7c0cf4cbd51b2b @wbyoung wbyoung committed Aug 6, 2011
Showing with 1,017 additions and 0 deletions.
  1. +2 −0 .gitignore
  2. +49 −0 FRClassSetup.h
  3. +155 −0 FRClassSetup.m
  4. +41 −0 FRRuntimeAdditions.h
  5. +50 −0 FRRuntimeAdditions.m
  6. +28 −0 FRSingleton.h
  7. +257 −0 FRSingleton.m
  8. +14 −0 License
  9. +62 −0 Makefile
  10. +29 −0 Readme.md
  11. +28 −0 tests/lib_loadable.m
  12. +67 −0 tests/test_compare_speed.m
  13. +95 −0 tests/test_general.m
  14. +38 −0 tests/test_loading.m
  15. +51 −0 tests/test_macros.h
  16. +51 −0 tests/test_speed.m
@@ -0,0 +1,2 @@
+.DS_Store
+build
@@ -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);
@@ -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
@@ -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
@@ -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
@@ -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
Oops, something went wrong.

0 comments on commit 47056c4

Please sign in to comment.