New file |
| | |
| | | // |
| | | // NullSafe.m |
| | | // |
| | | // Version 1.2.2 |
| | | // |
| | | // Created by Nick Lockwood on 19/12/2012. |
| | | // Copyright 2012 Charcoal Design |
| | | // |
| | | // Distributed under the permissive zlib License |
| | | // Get the latest version from here: |
| | | // |
| | | // https://github.com/nicklockwood/NullSafe |
| | | // |
| | | // This software is provided 'as-is', without any express or implied |
| | | // warranty. In no event will the authors be held liable for any damages |
| | | // arising from the use of this software. |
| | | // |
| | | // Permission is granted to anyone to use this software for any purpose, |
| | | // including commercial applications, and to alter it and redistribute it |
| | | // freely, subject to the following restrictions: |
| | | // |
| | | // 1. The origin of this software must not be misrepresented; you must not |
| | | // claim that you wrote the original software. If you use this software |
| | | // in a product, an acknowledgment in the product documentation would be |
| | | // appreciated but is not required. |
| | | // |
| | | // 2. Altered source versions must be plainly marked as such, and must not be |
| | | // misrepresented as being the original software. |
| | | // |
| | | // 3. This notice may not be removed or altered from any source distribution. |
| | | // |
| | | |
| | | #import <objc/runtime.h> |
| | | #import <Foundation/Foundation.h> |
| | | |
| | | |
| | | #ifndef NULLSAFE_ENABLED |
| | | #define NULLSAFE_ENABLED 1 |
| | | #endif |
| | | |
| | | |
| | | #pragma GCC diagnostic ignored "-Wgnu-conditional-omitted-operand" |
| | | |
| | | |
| | | @implementation NSNull (NullSafe) |
| | | |
| | | #if NULLSAFE_ENABLED |
| | | |
| | | - (NSMethodSignature *)methodSignatureForSelector:(SEL)selector |
| | | { |
| | | @synchronized([self class]) |
| | | { |
| | | //look up method signature |
| | | NSMethodSignature *signature = [super methodSignatureForSelector:selector]; |
| | | if (!signature) |
| | | { |
| | | //not supported by NSNull, search other classes |
| | | static NSMutableSet *classList = nil; |
| | | static NSMutableDictionary *signatureCache = nil; |
| | | if (signatureCache == nil) |
| | | { |
| | | classList = [[NSMutableSet alloc] init]; |
| | | signatureCache = [[NSMutableDictionary alloc] init]; |
| | | |
| | | //get class list |
| | | int numClasses = objc_getClassList(NULL, 0); |
| | | Class *classes = (Class *)malloc(sizeof(Class) * (unsigned long)numClasses); |
| | | numClasses = objc_getClassList(classes, numClasses); |
| | | |
| | | //add to list for checking |
| | | NSMutableSet *excluded = [NSMutableSet set]; |
| | | for (int i = 0; i < numClasses; i++) |
| | | { |
| | | //determine if class has a superclass |
| | | Class someClass = classes[i]; |
| | | Class superclass = class_getSuperclass(someClass); |
| | | while (superclass) |
| | | { |
| | | if (superclass == [NSObject class]) |
| | | { |
| | | [classList addObject:someClass]; |
| | | break; |
| | | } |
| | | [excluded addObject:NSStringFromClass(superclass)]; |
| | | superclass = class_getSuperclass(superclass); |
| | | } |
| | | } |
| | | |
| | | //remove all classes that have subclasses |
| | | for (Class someClass in excluded) |
| | | { |
| | | [classList removeObject:someClass]; |
| | | } |
| | | |
| | | //free class list |
| | | free(classes); |
| | | } |
| | | |
| | | //check implementation cache first |
| | | NSString *selectorString = NSStringFromSelector(selector); |
| | | signature = signatureCache[selectorString]; |
| | | if (!signature) |
| | | { |
| | | //find implementation |
| | | for (Class someClass in classList) |
| | | { |
| | | if ([someClass instancesRespondToSelector:selector]) |
| | | { |
| | | signature = [someClass instanceMethodSignatureForSelector:selector]; |
| | | break; |
| | | } |
| | | } |
| | | |
| | | //cache for next time |
| | | signatureCache[selectorString] = signature ?: [NSNull null]; |
| | | } |
| | | else if ([signature isKindOfClass:[NSNull class]]) |
| | | { |
| | | signature = nil; |
| | | } |
| | | } |
| | | return signature; |
| | | } |
| | | } |
| | | |
| | | - (void)forwardInvocation:(NSInvocation *)invocation |
| | | { |
| | | invocation.target = nil; |
| | | [invocation invoke]; |
| | | } |
| | | |
| | | #endif |
| | | |
| | | @end |