소스 검색

Improve safeObject:respondsToSelector:

The old code assumes the object at least responds to respondsToSelector: which may not always be true. I also I think the old code was not quite correct for all cases anyway.

Using the runtime functions ensures no methods are ever called on the object, and properly handles metaclass objects, in theory.
Tanner Bennett 5 년 전
부모
커밋
6bbcc55cf6
1개의 변경된 파일12개의 추가작업 그리고 15개의 파일을 삭제
  1. 12 15
      Classes/Utility/Runtime/FLEXRuntimeUtility.m

+ 12 - 15
Classes/Utility/Runtime/FLEXRuntimeUtility.m

@@ -98,8 +98,7 @@ typedef NS_ENUM(NSInteger, FLEXRuntimeUtilityErrorCode) {
 
 /// Could be nil
 + (NSString *)safeDescriptionForObject:(id)object {
-    // Don't assume that we have an NSObject subclass.
-    // Check to make sure the object responds to the description method
+    // Don't assume that we have an NSObject subclass; not all objects respond to -description
     if ([self safeObject:object respondsToSelector:@selector(description)]) {
         return [object description];
     }
@@ -111,8 +110,6 @@ typedef NS_ENUM(NSInteger, FLEXRuntimeUtilityErrorCode) {
 + (NSString *)safeDebugDescriptionForObject:(id)object {
     NSString *description = nil;
 
-    // Don't assume that we have an NSObject subclass.
-    // Check to make sure the object responds to the description method
     if ([self safeObject:object respondsToSelector:@selector(debugDescription)]) {
         description = [object debugDescription];
     } else {
@@ -177,18 +174,18 @@ typedef NS_ENUM(NSInteger, FLEXRuntimeUtilityErrorCode) {
 }
 
 + (BOOL)safeObject:(id)object respondsToSelector:(SEL)sel {
-    static BOOL (*respondsToSelector)(id, SEL, SEL) = nil;
-    static BOOL (*respondsToSelector_meta)(id, SEL, SEL) = nil;
-    static dispatch_once_t onceToken;
-    dispatch_once(&onceToken, ^{
-        respondsToSelector = (BOOL(*)(id, SEL, SEL))[NSObject instanceMethodForSelector:@selector(respondsToSelector:)];
-        respondsToSelector_meta = (BOOL(*)(id, SEL, SEL))[NSObject methodForSelector:@selector(respondsToSelector:)];
-    });
-    
+    // If we're given a class, we want to know if classes respond to this selector.
+    // Similarly, if we're given an instance, we want to know if instances respond. 
     BOOL isClass = object_isClass(object);
-    return (isClass ? respondsToSelector_meta : respondsToSelector)(
-        object, @selector(respondsToSelector:), sel
-    );
+    Class cls = isClass ? object : object_getClass(object);
+    // BOOL isMetaclass = class_isMetaClass(cls);
+    
+    if (isClass) {
+        // In theory, this should also work for metaclasses...
+        return class_getClassMethod(cls, sel) != nil;
+    } else {
+        return class_getInstanceMethod(cls, sel) != nil;
+    }
 }