GULSwizzlingCache.m 4.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134
  1. // Copyright 2018 Google LLC
  2. //
  3. // Licensed under the Apache License, Version 2.0 (the "License");
  4. // you may not use this file except in compliance with the License.
  5. // You may obtain a copy of the License at
  6. //
  7. // http://www.apache.org/licenses/LICENSE-2.0
  8. //
  9. // Unless required by applicable law or agreed to in writing, software
  10. // distributed under the License is distributed on an "AS IS" BASIS,
  11. // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  12. // See the License for the specific language governing permissions and
  13. // limitations under the License.
  14. #import "GULSwizzlingCache.h"
  15. #import <objc/runtime.h>
  16. @interface GULSwizzlingCache ()
  17. - (IMP)originalIMPOfCurrentIMP:(IMP)currentIMP;
  18. @end
  19. @implementation GULSwizzlingCache {
  20. /** A mapping from the new IMP to the original IMP. */
  21. CFMutableDictionaryRef _newToOriginalImps;
  22. /** A mapping from a Class and SEL (stored in a CFArray) to the original IMP that existed for it.
  23. */
  24. CFMutableDictionaryRef _originalImps;
  25. }
  26. + (instancetype)sharedInstance {
  27. static GULSwizzlingCache *sharedInstance;
  28. static dispatch_once_t token;
  29. dispatch_once(&token, ^{
  30. sharedInstance = [[GULSwizzlingCache alloc] init];
  31. });
  32. return sharedInstance;
  33. }
  34. - (instancetype)init {
  35. self = [super init];
  36. if (self) {
  37. _newToOriginalImps = CFDictionaryCreateMutable(kCFAllocatorDefault,
  38. 0, // Size.
  39. NULL, // Keys are pointers, so this is NULL.
  40. NULL); // Values are pointers so this is NULL.
  41. _originalImps = CFDictionaryCreateMutable(kCFAllocatorDefault,
  42. 0, // Size.
  43. &kCFTypeDictionaryKeyCallBacks, // Keys are CFArrays.
  44. NULL); // Values are pointers so this is NULL.
  45. }
  46. return self;
  47. }
  48. - (void)dealloc {
  49. CFRelease(_newToOriginalImps);
  50. CFRelease(_originalImps);
  51. }
  52. - (void)cacheCurrentIMP:(IMP)currentIMP
  53. forNewIMP:(IMP)newIMP
  54. forClass:(Class)aClass
  55. withSelector:(SEL)selector {
  56. IMP originalIMP = [self originalIMPOfCurrentIMP:currentIMP];
  57. CFDictionaryAddValue(_newToOriginalImps, newIMP, originalIMP);
  58. const void *classSELCArray[2] = {(__bridge void *)(aClass), selector};
  59. CFArrayRef classSELPair = CFArrayCreate(kCFAllocatorDefault, classSELCArray,
  60. 2, // Size.
  61. NULL); // Elements are pointers so this is NULL.
  62. CFDictionaryAddValue(_originalImps, classSELPair, originalIMP);
  63. CFRelease(classSELPair);
  64. }
  65. + (void)cacheCurrentIMP:(IMP)currentIMP
  66. forNewIMP:(IMP)newIMP
  67. forClass:(Class)aClass
  68. withSelector:(SEL)selector {
  69. [[GULSwizzlingCache sharedInstance] cacheCurrentIMP:currentIMP
  70. forNewIMP:newIMP
  71. forClass:aClass
  72. withSelector:selector];
  73. }
  74. - (IMP)cachedIMPForClass:(Class)aClass withSelector:(SEL)selector {
  75. const void *classSELCArray[2] = {(__bridge void *)(aClass), selector};
  76. CFArrayRef classSELPair = CFArrayCreate(kCFAllocatorDefault, classSELCArray,
  77. 2, // Size.
  78. NULL); // Elements are pointers so this is NULL.
  79. const void *returnedIMP = CFDictionaryGetValue(_originalImps, classSELPair);
  80. CFRelease(classSELPair);
  81. return (IMP)returnedIMP;
  82. }
  83. - (void)clearCacheForSwizzledIMP:(IMP)swizzledIMP selector:(SEL)selector aClass:(Class)aClass {
  84. CFDictionaryRemoveValue(_newToOriginalImps, swizzledIMP);
  85. const void *classSELCArray[2] = {(__bridge void *)(aClass), selector};
  86. CFArrayRef classSELPair = CFArrayCreate(kCFAllocatorDefault, classSELCArray,
  87. 2, // Size.
  88. NULL); // Elements are pointers so this is NULL.
  89. CFDictionaryRemoveValue(_originalImps, classSELPair);
  90. CFRelease(classSELPair);
  91. }
  92. - (IMP)originalIMPOfCurrentIMP:(IMP)currentIMP {
  93. const void *returnedIMP = CFDictionaryGetValue(_newToOriginalImps, currentIMP);
  94. if (returnedIMP != NULL) {
  95. return (IMP)returnedIMP;
  96. } else {
  97. return currentIMP;
  98. }
  99. }
  100. + (IMP)originalIMPOfCurrentIMP:(IMP)currentIMP {
  101. return [[GULSwizzlingCache sharedInstance] originalIMPOfCurrentIMP:currentIMP];
  102. }
  103. #pragma mark - Helper methods for testing
  104. - (void)clearCache {
  105. CFDictionaryRemoveAllValues(_originalImps);
  106. CFDictionaryRemoveAllValues(_newToOriginalImps);
  107. }
  108. - (CFMutableDictionaryRef)originalImps {
  109. return _originalImps;
  110. }
  111. - (CFMutableDictionaryRef)newToOriginalImps {
  112. return _newToOriginalImps;
  113. }
  114. @end