NullSafe.m 4.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134
  1. //
  2. // NullSafe.m
  3. //
  4. // Version 1.2.2
  5. //
  6. // Created by Nick Lockwood on 19/12/2012.
  7. // Copyright 2012 Charcoal Design
  8. //
  9. // Distributed under the permissive zlib License
  10. // Get the latest version from here:
  11. //
  12. // https://github.com/nicklockwood/NullSafe
  13. //
  14. // This software is provided 'as-is', without any express or implied
  15. // warranty. In no event will the authors be held liable for any damages
  16. // arising from the use of this software.
  17. //
  18. // Permission is granted to anyone to use this software for any purpose,
  19. // including commercial applications, and to alter it and redistribute it
  20. // freely, subject to the following restrictions:
  21. //
  22. // 1. The origin of this software must not be misrepresented; you must not
  23. // claim that you wrote the original software. If you use this software
  24. // in a product, an acknowledgment in the product documentation would be
  25. // appreciated but is not required.
  26. //
  27. // 2. Altered source versions must be plainly marked as such, and must not be
  28. // misrepresented as being the original software.
  29. //
  30. // 3. This notice may not be removed or altered from any source distribution.
  31. //
  32. #import <objc/runtime.h>
  33. #import <Foundation/Foundation.h>
  34. #ifndef NULLSAFE_ENABLED
  35. #define NULLSAFE_ENABLED 1
  36. #endif
  37. #pragma GCC diagnostic ignored "-Wgnu-conditional-omitted-operand"
  38. @implementation NSNull (NullSafe)
  39. #if NULLSAFE_ENABLED
  40. - (NSMethodSignature *)methodSignatureForSelector:(SEL)selector
  41. {
  42. @synchronized([self class])
  43. {
  44. //look up method signature
  45. NSMethodSignature *signature = [super methodSignatureForSelector:selector];
  46. if (!signature)
  47. {
  48. //not supported by NSNull, search other classes
  49. static NSMutableSet *classList = nil;
  50. static NSMutableDictionary *signatureCache = nil;
  51. if (signatureCache == nil)
  52. {
  53. classList = [[NSMutableSet alloc] init];
  54. signatureCache = [[NSMutableDictionary alloc] init];
  55. //get class list
  56. int numClasses = objc_getClassList(NULL, 0);
  57. Class *classes = (Class *)malloc(sizeof(Class) * (unsigned long)numClasses);
  58. numClasses = objc_getClassList(classes, numClasses);
  59. //add to list for checking
  60. NSMutableSet *excluded = [NSMutableSet set];
  61. for (int i = 0; i < numClasses; i++)
  62. {
  63. //determine if class has a superclass
  64. Class someClass = classes[i];
  65. Class superclass = class_getSuperclass(someClass);
  66. while (superclass)
  67. {
  68. if (superclass == [NSObject class])
  69. {
  70. [classList addObject:someClass];
  71. break;
  72. }
  73. [excluded addObject:NSStringFromClass(superclass)];
  74. superclass = class_getSuperclass(superclass);
  75. }
  76. }
  77. //remove all classes that have subclasses
  78. for (Class someClass in excluded)
  79. {
  80. [classList removeObject:someClass];
  81. }
  82. //free class list
  83. free(classes);
  84. }
  85. //check implementation cache first
  86. NSString *selectorString = NSStringFromSelector(selector);
  87. signature = signatureCache[selectorString];
  88. if (!signature)
  89. {
  90. //find implementation
  91. for (Class someClass in classList)
  92. {
  93. if ([someClass instancesRespondToSelector:selector])
  94. {
  95. signature = [someClass instanceMethodSignatureForSelector:selector];
  96. break;
  97. }
  98. }
  99. //cache for next time
  100. signatureCache[selectorString] = signature ?: [NSNull null];
  101. }
  102. else if ([signature isKindOfClass:[NSNull class]])
  103. {
  104. signature = nil;
  105. }
  106. }
  107. return signature;
  108. }
  109. }
  110. - (void)forwardInvocation:(NSInvocation *)invocation
  111. {
  112. invocation.target = nil;
  113. [invocation invoke];
  114. }
  115. #endif
  116. @end