From 32d6b4f306ccbbfc1cf1216bcb7a3324eabc41b4 Mon Sep 17 00:00:00 2001 From: Ethan Arbuckle Date: Wed, 15 Feb 2017 13:06:46 -0800 Subject: [PATCH 1/6] Replaced deprecated OSSpinLock with os_unfair_lock --- RSSwizzle/RSSwizzle.m | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/RSSwizzle/RSSwizzle.m b/RSSwizzle/RSSwizzle.m index 209a7ab..fd8f7a5 100644 --- a/RSSwizzle/RSSwizzle.m +++ b/RSSwizzle/RSSwizzle.m @@ -8,7 +8,7 @@ #import "RSSwizzle.h" #import -#import +#import #if !__has_feature(objc_arc) #error This code needs ARC. Use compiler option -fobjc-arc @@ -196,7 +196,7 @@ static void swizzle(Class classToSwizzle, NSCAssert(blockIsAnImpFactoryBlock(factoryBlock), @"Wrong type of implementation factory block."); - __block OSSpinLock lock = OS_SPINLOCK_INIT; + __block os_unfair_lock lock = OS_UNFAIR_LOCK_INIT; // To keep things thread-safe, we fill in the originalIMP later, // with the result of the class_replaceMethod call below. __block IMP originalIMP = NULL; @@ -206,9 +206,9 @@ static void swizzle(Class classToSwizzle, // It's possible that another thread can call the method between the call to // class_replaceMethod and its return value being set. // So to be sure originalIMP has the right value, we need a lock. - OSSpinLockLock(&lock); + os_unfair_lock_lock(&lock); IMP imp = originalIMP; - OSSpinLockUnlock(&lock); + os_unfair_lock_unlock(&lock); if (NULL == imp){ // If the class does not implement the method @@ -244,9 +244,9 @@ static void swizzle(Class classToSwizzle, // // We need a lock to be sure that originalIMP has the right value in the // originalImpProvider block above. - OSSpinLockLock(&lock); + os_unfair_lock_lock(&lock); originalIMP = class_replaceMethod(classToSwizzle, selector, newIMP, methodType); - OSSpinLockUnlock(&lock); + os_unfair_lock_unlock(&lock); } static NSMutableDictionary *swizzledClassesDictionary(){ From fc6b0a4fb89244ccc64c82472fff28f15d2dacf3 Mon Sep 17 00:00:00 2001 From: Ethan Arbuckle Date: Wed, 15 Feb 2017 15:48:10 -0800 Subject: [PATCH 2/6] Use OSSpinLock when building for SDKs lower than iOS10/macOS10.12 --- RSSwizzle/RSSwizzle.m | 46 ++++++++++++++++++++++++++++++++++++++----- 1 file changed, 41 insertions(+), 5 deletions(-) diff --git a/RSSwizzle/RSSwizzle.m b/RSSwizzle/RSSwizzle.m index fd8f7a5..3e96dcf 100644 --- a/RSSwizzle/RSSwizzle.m +++ b/RSSwizzle/RSSwizzle.m @@ -8,7 +8,15 @@ #import "RSSwizzle.h" #import + +//use os_unfair_lock over OSSpinLock when building with ios sdk 10 or osx sdk 10.12 +#define TARGET_SDK_GE_10 ((TARGET_OS_IOS &&__IPHONE_OS_VERSION_MAX_ALLOWED >= 100000) || (!TARGET_OS_IPHONE && __MAC_OS_X_VERSION_MAX_ALLOWED >= 101200)) + +#if TARGET_SDK_GE_10 #import +#else +#import +#endif #if !__has_feature(objc_arc) #error This code needs ARC. Use compiler option -fobjc-arc @@ -92,7 +100,7 @@ static BOOL blockIsCompatibleWithMethodType(id block, const char *methodType){ } NSMethodSignature *methodSignature = - [NSMethodSignature signatureWithObjCTypes:methodType]; + [NSMethodSignature signatureWithObjCTypes:methodType]; if (!blockSignature || !methodSignature) { return NO; @@ -194,21 +202,36 @@ static void swizzle(Class classToSwizzle, classToSwizzle); NSCAssert(blockIsAnImpFactoryBlock(factoryBlock), - @"Wrong type of implementation factory block."); + @"Wrong type of implementation factory block."); +#if TARGET_SDK_GE_10 __block os_unfair_lock lock = OS_UNFAIR_LOCK_INIT; +#else + __block OSSpinLock lock = OS_SPINLOCK_INIT; +#endif + // To keep things thread-safe, we fill in the originalIMP later, // with the result of the class_replaceMethod call below. __block IMP originalIMP = NULL; - + // This block will be called by the client to get original implementation and call it. RSSWizzleImpProvider originalImpProvider = ^IMP{ // It's possible that another thread can call the method between the call to // class_replaceMethod and its return value being set. // So to be sure originalIMP has the right value, we need a lock. + +#if TARGET_SDK_GE_10 os_unfair_lock_lock(&lock); +#else + OSSpinLockLock(&lock); +#endif IMP imp = originalIMP; + +#if TARGET_SDK_GE_10 os_unfair_lock_unlock(&lock); +#else + OSSpinLockUnlock(&lock); +#endif if (NULL == imp){ // If the class does not implement the method @@ -231,7 +254,7 @@ static void swizzle(Class classToSwizzle, const char *methodType = method_getTypeEncoding(method); NSCAssert(blockIsCompatibleWithMethodType(newIMPBlock,methodType), - @"Block returned from factory is not compatible with method type."); + @"Block returned from factory is not compatible with method type."); IMP newIMP = imp_implementationWithBlock(newIMPBlock); @@ -244,11 +267,24 @@ static void swizzle(Class classToSwizzle, // // We need a lock to be sure that originalIMP has the right value in the // originalImpProvider block above. + +#if TARGET_SDK_GE_10 os_unfair_lock_lock(&lock); +#else + OSSpinLockLock(&lock); +#endif + originalIMP = class_replaceMethod(classToSwizzle, selector, newIMP, methodType); + +#if TARGET_SDK_GE_10 os_unfair_lock_unlock(&lock); +#else + OSSpinLockUnlock(&lock); +#endif + } + static NSMutableDictionary *swizzledClassesDictionary(){ static NSMutableDictionary *swizzledClasses; static dispatch_once_t onceToken; @@ -277,7 +313,7 @@ +(BOOL)swizzleInstanceMethod:(SEL)selector { NSAssert(!(NULL == key && RSSwizzleModeAlways != mode), @"Key may not be NULL if mode is not RSSwizzleModeAlways."); - + @synchronized(swizzledClassesDictionary()){ if (key){ NSSet *swizzledClasses = swizzledClassesForKey(key); From af67db2c06b783e61756f8b2af6353d30e8735e7 Mon Sep 17 00:00:00 2001 From: Ethan Arbuckle Date: Tue, 21 Feb 2017 16:49:09 -0800 Subject: [PATCH 3/6] unfair_lock will be used when applicable, falling back on OSSpinLock --- RSSwizzle/RSSwizzle.m | 97 +++++++++++++++++++++++++++++++------------ 1 file changed, 70 insertions(+), 27 deletions(-) diff --git a/RSSwizzle/RSSwizzle.m b/RSSwizzle/RSSwizzle.m index 3e96dcf..4add6db 100644 --- a/RSSwizzle/RSSwizzle.m +++ b/RSSwizzle/RSSwizzle.m @@ -8,20 +8,78 @@ #import "RSSwizzle.h" #import +#include -//use os_unfair_lock over OSSpinLock when building with ios sdk 10 or osx sdk 10.12 -#define TARGET_SDK_GE_10 ((TARGET_OS_IOS &&__IPHONE_OS_VERSION_MAX_ALLOWED >= 100000) || (!TARGET_OS_IPHONE && __MAC_OS_X_VERSION_MAX_ALLOWED >= 101200)) +// Use os_unfair_lock over OSSpinLock when building with ios sdk 10 or macos sdk 10.12 +#define TARGET_SDK_GE_10 ((TARGET_OS_IOS && __IPHONE_OS_VERSION_MIN_REQUIRED >= 100000) || (!TARGET_OS_IPHONE && __MAC_OS_X_VERSION_MAX_ALLOWED >= 101200)) #if TARGET_SDK_GE_10 #import #else -#import +//use this struct to inialize an unfair_lock +typedef struct _os_unfair_lock_s { +uint32_t _os_unfair_lock_opaque; +} os_unfair_lock, *os_unfair_lock_t; #endif +#import + #if !__has_feature(objc_arc) #error This code needs ARC. Use compiler option -fobjc-arc #endif +#pragma mark Locking + +// This function will lock a lock using os_unfair_lock_lock (on ios10/macos10.12) or OSSpinLockLock (9 and lower). +void chooseLock(void *lock) +{ +#if TARGET_SDK_GE_10 + // iOS 10+, os_unfair_lock_lock is available + os_unfair_lock_lock(lock); +#else + // NSDimension was introduced at the same time that os_unfair_lock_lock was made public. + // To make sure we dont access unfair_lock on an iOS version where it isnt public, check for the presence of NSDimension before proceeding. + if (objc_getClass("NSDimension")) + { + // Attempt to use 'os_unfair_lock_lock' + void (*os_unfair_lock_lock)(void *lock) = dlsym(dlopen(NULL, RTLD_NOW | RTLD_GLOBAL), "os_unfair_lock_lock"); + if (os_unfair_lock_lock != NULL) + { + os_unfair_lock_lock(lock); + return; + } + } + + // Unfair locks are not available on iOS 9 and lower, using deprecated OSSpinLock. + OSSpinLockLock(lock); +#endif +} + +// This function will unlock a lock using os_unfair_lock_unlock (on ios10/macos10.12) or OSSpinLockUnlock (9 and lower). +void chooseUnlock(void *lock) +{ +#if TARGET_SDK_GE_10 + // iOS 10+, os_unfair_lock_unlock is available + os_unfair_lock_unlock(lock); +#else + // NSDimension was introduced at the same time that os_unfair_lock_unlock was made public. + // To make sure we dont access unfair_unlock on an iOS version where it isnt public, check for the presence of NSDimension before proceeding. + if (objc_getClass("NSDimension")) + { + // Attempt to use 'os_unfair_lock_unlock'. + void (*os_unfair_lock_unlock)(void *lock) = dlsym(dlopen(NULL, RTLD_NOW | RTLD_GLOBAL), "os_unfair_lock_unlock"); + if (os_unfair_lock_unlock != NULL) + { + os_unfair_lock_unlock(lock); + return; + } + } + + // Unfair locks are not available on iOS 9 and lower, using deprecated OSSpinUnlock. + OSSpinLockUnlock(lock); +#endif +} + #pragma mark - Block Helpers #if !defined(NS_BLOCK_ASSERTIONS) @@ -207,9 +265,10 @@ static void swizzle(Class classToSwizzle, #if TARGET_SDK_GE_10 __block os_unfair_lock lock = OS_UNFAIR_LOCK_INIT; #else - __block OSSpinLock lock = OS_SPINLOCK_INIT; + // Below iOS 10, OS_UNFAIR_LOCK_INIT will not exist. Note that this type works with OSSpinLock + __block os_unfair_lock lock = ((os_unfair_lock){0}); #endif - + // To keep things thread-safe, we fill in the originalIMP later, // with the result of the class_replaceMethod call below. __block IMP originalIMP = NULL; @@ -220,18 +279,12 @@ static void swizzle(Class classToSwizzle, // class_replaceMethod and its return value being set. // So to be sure originalIMP has the right value, we need a lock. -#if TARGET_SDK_GE_10 - os_unfair_lock_lock(&lock); -#else - OSSpinLockLock(&lock); -#endif + + chooseLock(&lock); + IMP imp = originalIMP; -#if TARGET_SDK_GE_10 - os_unfair_lock_unlock(&lock); -#else - OSSpinLockUnlock(&lock); -#endif + chooseUnlock(&lock); if (NULL == imp){ // If the class does not implement the method @@ -268,20 +321,11 @@ static void swizzle(Class classToSwizzle, // We need a lock to be sure that originalIMP has the right value in the // originalImpProvider block above. -#if TARGET_SDK_GE_10 - os_unfair_lock_lock(&lock); -#else - OSSpinLockLock(&lock); -#endif + chooseLock(&lock); originalIMP = class_replaceMethod(classToSwizzle, selector, newIMP, methodType); -#if TARGET_SDK_GE_10 - os_unfair_lock_unlock(&lock); -#else - OSSpinLockUnlock(&lock); -#endif - + chooseUnlock(&lock); } @@ -354,5 +398,4 @@ +(void)swizzleClassMethod:(SEL)selector key:NULL]; } - @end From c6fef972bd5e5b50a280671630d8a5701f381182 Mon Sep 17 00:00:00 2001 From: Alban Diquet Date: Wed, 1 Mar 2017 08:59:38 -0800 Subject: [PATCH 4/6] Make functions static --- RSSwizzle/RSSwizzle.m | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/RSSwizzle/RSSwizzle.m b/RSSwizzle/RSSwizzle.m index 4add6db..d1a334c 100644 --- a/RSSwizzle/RSSwizzle.m +++ b/RSSwizzle/RSSwizzle.m @@ -18,7 +18,7 @@ #else //use this struct to inialize an unfair_lock typedef struct _os_unfair_lock_s { -uint32_t _os_unfair_lock_opaque; + uint32_t _os_unfair_lock_opaque; } os_unfair_lock, *os_unfair_lock_t; #endif @@ -31,7 +31,7 @@ #pragma mark Locking // This function will lock a lock using os_unfair_lock_lock (on ios10/macos10.12) or OSSpinLockLock (9 and lower). -void chooseLock(void *lock) +static void chooseLock(void *lock) { #if TARGET_SDK_GE_10 // iOS 10+, os_unfair_lock_lock is available @@ -56,7 +56,7 @@ void chooseLock(void *lock) } // This function will unlock a lock using os_unfair_lock_unlock (on ios10/macos10.12) or OSSpinLockUnlock (9 and lower). -void chooseUnlock(void *lock) +static void chooseUnlock(void *lock) { #if TARGET_SDK_GE_10 // iOS 10+, os_unfair_lock_unlock is available @@ -268,7 +268,7 @@ static void swizzle(Class classToSwizzle, // Below iOS 10, OS_UNFAIR_LOCK_INIT will not exist. Note that this type works with OSSpinLock __block os_unfair_lock lock = ((os_unfair_lock){0}); #endif - + // To keep things thread-safe, we fill in the originalIMP later, // with the result of the class_replaceMethod call below. __block IMP originalIMP = NULL; @@ -279,7 +279,7 @@ static void swizzle(Class classToSwizzle, // class_replaceMethod and its return value being set. // So to be sure originalIMP has the right value, we need a lock. - + chooseLock(&lock); IMP imp = originalIMP; From fb5b928a951c768eada46d0d09ded43e7365edb2 Mon Sep 17 00:00:00 2001 From: Alban Diquet Date: Wed, 1 Mar 2017 10:38:46 -0800 Subject: [PATCH 5/6] Properly handle imports --- RSSwizzle/RSSwizzle.m | 30 +++++++++++++++++------------- 1 file changed, 17 insertions(+), 13 deletions(-) diff --git a/RSSwizzle/RSSwizzle.m b/RSSwizzle/RSSwizzle.m index d1a334c..b16b5d6 100644 --- a/RSSwizzle/RSSwizzle.m +++ b/RSSwizzle/RSSwizzle.m @@ -10,30 +10,34 @@ #import #include -// Use os_unfair_lock over OSSpinLock when building with ios sdk 10 or macos sdk 10.12 -#define TARGET_SDK_GE_10 ((TARGET_OS_IOS && __IPHONE_OS_VERSION_MIN_REQUIRED >= 100000) || (!TARGET_OS_IPHONE && __MAC_OS_X_VERSION_MAX_ALLOWED >= 101200)) -#if TARGET_SDK_GE_10 +#if !__has_feature(objc_arc) +#error This code needs ARC. Use compiler option -fobjc-arc +#endif + + +// Use os_unfair_lock over OSSpinLock when building with the following SDKs: iOS 10, macOS 10.12 and any tvOS and watchOS +#define DEPLOYMENT_TARGET_HIGHER_THAN_10 TARGET_OS_WATCH || TARGET_OS_TV || (TARGET_OS_IOS &&__IPHONE_OS_VERSION_MIN_REQUIRED >= 100000) || (!TARGET_OS_IPHONE && __MAC_OS_X_VERSION_MIN_ALLOWED >= 101200) + +#define BASE_SDK_HIGHER_THAN_10 (TARGET_OS_WATCH || TARGET_OS_TV || (TARGET_OS_IOS &&__IPHONE_OS_VERSION_MAX_ALLOWED >= 100000) || (!TARGET_OS_IPHONE && __MAC_OS_X_VERSION_MAX_ALLOWED >= 101200)) + + +#if BASE_SDK_HIGHER_THAN_10 #import -#else -//use this struct to inialize an unfair_lock -typedef struct _os_unfair_lock_s { - uint32_t _os_unfair_lock_opaque; -} os_unfair_lock, *os_unfair_lock_t; #endif -#import -#if !__has_feature(objc_arc) -#error This code needs ARC. Use compiler option -fobjc-arc +#if !DEPLOYMENT_TARGET_HIGHER_THAN_10 +#import #endif + #pragma mark Locking // This function will lock a lock using os_unfair_lock_lock (on ios10/macos10.12) or OSSpinLockLock (9 and lower). static void chooseLock(void *lock) { -#if TARGET_SDK_GE_10 +#if DEPLOYMENT_TARGET_HIGHER_THAN_10 // iOS 10+, os_unfair_lock_lock is available os_unfair_lock_lock(lock); #else @@ -58,7 +62,7 @@ static void chooseLock(void *lock) // This function will unlock a lock using os_unfair_lock_unlock (on ios10/macos10.12) or OSSpinLockUnlock (9 and lower). static void chooseUnlock(void *lock) { -#if TARGET_SDK_GE_10 +#if DEPLOYMENT_TARGET_HIGHER_THAN_10 // iOS 10+, os_unfair_lock_unlock is available os_unfair_lock_unlock(lock); #else From b601e7dfd21e4335738e4ad9633da4ea2bcf12fd Mon Sep 17 00:00:00 2001 From: Alban Diquet Date: Wed, 1 Mar 2017 11:57:32 -0800 Subject: [PATCH 6/6] Simplify lock definition --- RSSwizzle/RSSwizzle.m | 38 +++++++++++++++++++++----------------- 1 file changed, 21 insertions(+), 17 deletions(-) diff --git a/RSSwizzle/RSSwizzle.m b/RSSwizzle/RSSwizzle.m index b16b5d6..3145daa 100644 --- a/RSSwizzle/RSSwizzle.m +++ b/RSSwizzle/RSSwizzle.m @@ -24,6 +24,13 @@ #if BASE_SDK_HIGHER_THAN_10 #import +#else +// Below iOS 10, OS_UNFAIR_LOCK_INIT will not exist. Note that this type works with OSSpinLock +#define OS_UNFAIR_LOCK_INIT ((os_unfair_lock){0}) + +typedef struct _os_unfair_lock_s { + uint32_t _os_unfair_lock_opaque; +} os_unfair_lock, *os_unfair_lock_t; #endif @@ -32,20 +39,22 @@ #endif +// NSDimension was introduced at the same time that os_unfair_lock_lock was made public, ie. iOS 10 +#define DEVICE_HIGHER_THAN_10 objc_getClass("NSDimension") + + #pragma mark Locking // This function will lock a lock using os_unfair_lock_lock (on ios10/macos10.12) or OSSpinLockLock (9 and lower). -static void chooseLock(void *lock) +static void chooseLock(os_unfair_lock *lock) { #if DEPLOYMENT_TARGET_HIGHER_THAN_10 // iOS 10+, os_unfair_lock_lock is available os_unfair_lock_lock(lock); #else - // NSDimension was introduced at the same time that os_unfair_lock_lock was made public. - // To make sure we dont access unfair_lock on an iOS version where it isnt public, check for the presence of NSDimension before proceeding. - if (objc_getClass("NSDimension")) + if (DEVICE_HIGHER_THAN_10) { - // Attempt to use 'os_unfair_lock_lock' + // Attempt to use os_unfair_lock_lock(). void (*os_unfair_lock_lock)(void *lock) = dlsym(dlopen(NULL, RTLD_NOW | RTLD_GLOBAL), "os_unfair_lock_lock"); if (os_unfair_lock_lock != NULL) { @@ -55,22 +64,20 @@ static void chooseLock(void *lock) } // Unfair locks are not available on iOS 9 and lower, using deprecated OSSpinLock. - OSSpinLockLock(lock); + OSSpinLockLock((void *)lock); #endif } // This function will unlock a lock using os_unfair_lock_unlock (on ios10/macos10.12) or OSSpinLockUnlock (9 and lower). -static void chooseUnlock(void *lock) +static void chooseUnlock(os_unfair_lock *lock) { #if DEPLOYMENT_TARGET_HIGHER_THAN_10 // iOS 10+, os_unfair_lock_unlock is available os_unfair_lock_unlock(lock); #else - // NSDimension was introduced at the same time that os_unfair_lock_unlock was made public. - // To make sure we dont access unfair_unlock on an iOS version where it isnt public, check for the presence of NSDimension before proceeding. - if (objc_getClass("NSDimension")) + if (DEVICE_HIGHER_THAN_10) { - // Attempt to use 'os_unfair_lock_unlock'. + // Attempt to use os_unfair_lock_unlock(). void (*os_unfair_lock_unlock)(void *lock) = dlsym(dlopen(NULL, RTLD_NOW | RTLD_GLOBAL), "os_unfair_lock_unlock"); if (os_unfair_lock_unlock != NULL) { @@ -80,11 +87,13 @@ static void chooseUnlock(void *lock) } // Unfair locks are not available on iOS 9 and lower, using deprecated OSSpinUnlock. - OSSpinLockUnlock(lock); + OSSpinLockUnlock((void *)lock); #endif } + #pragma mark - Block Helpers + #if !defined(NS_BLOCK_ASSERTIONS) // See http://clang.llvm.org/docs/Block-ABI-Apple.html#high-level @@ -266,12 +275,7 @@ static void swizzle(Class classToSwizzle, NSCAssert(blockIsAnImpFactoryBlock(factoryBlock), @"Wrong type of implementation factory block."); -#if TARGET_SDK_GE_10 __block os_unfair_lock lock = OS_UNFAIR_LOCK_INIT; -#else - // Below iOS 10, OS_UNFAIR_LOCK_INIT will not exist. Note that this type works with OSSpinLock - __block os_unfair_lock lock = ((os_unfair_lock){0}); -#endif // To keep things thread-safe, we fill in the originalIMP later, // with the result of the class_replaceMethod call below.