瀏覽代碼

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;
+    }
 }