Proyectos de Subversion Iphone Microlearning

Rev

| Ultima modificación | Ver Log |

Rev Autor Línea Nro. Línea
1 efrain 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
 
15
#import "GoogleUtilities/MethodSwizzler/Public/GoogleUtilities/GULSwizzler.h"
16
 
17
#import <objc/runtime.h>
18
 
19
#ifdef DEBUG
20
#import "GoogleUtilities/Common/GULLoggerCodes.h"
21
#import "GoogleUtilities/Logger/Public/GoogleUtilities/GULLogger.h"
22
 
23
static GULLoggerService kGULLoggerSwizzler = @"[GoogleUtilities/MethodSwizzler]";
24
#endif
25
 
26
dispatch_queue_t GetGULSwizzlingQueue(void) {
27
  static dispatch_queue_t queue;
28
  static dispatch_once_t onceToken;
29
  dispatch_once(&onceToken, ^{
30
    queue = dispatch_queue_create("com.google.GULSwizzler", DISPATCH_QUEUE_SERIAL);
31
  });
32
  return queue;
33
}
34
 
35
@implementation GULSwizzler
36
 
37
+ (void)swizzleClass:(Class)aClass
38
            selector:(SEL)selector
39
     isClassSelector:(BOOL)isClassSelector
40
           withBlock:(nullable id)block {
41
  dispatch_sync(GetGULSwizzlingQueue(), ^{
42
    NSAssert(selector, @"The selector cannot be NULL");
43
    NSAssert(aClass, @"The class cannot be Nil");
44
    Class resolvedClass = aClass;
45
    Method method = nil;
46
    if (isClassSelector) {
47
      method = class_getClassMethod(aClass, selector);
48
      resolvedClass = object_getClass(aClass);
49
    } else {
50
      method = class_getInstanceMethod(aClass, selector);
51
    }
52
    NSAssert(method, @"You're attempting to swizzle a method that doesn't exist. (%@, %@)",
53
             NSStringFromClass(resolvedClass), NSStringFromSelector(selector));
54
    IMP newImp = imp_implementationWithBlock(block);
55
#ifdef DEBUG
56
    IMP currentImp = class_getMethodImplementation(resolvedClass, selector);
57
    Class class = NSClassFromString(@"GULSwizzlingCache");
58
    if (class) {
59
      SEL cacheSelector = NSSelectorFromString(@"cacheCurrentIMP:forNewIMP:forClass:withSelector:");
60
      NSMethodSignature *methodSignature = [class methodSignatureForSelector:cacheSelector];
61
      if (methodSignature != nil) {
62
        NSInvocation *inv = [NSInvocation invocationWithMethodSignature:methodSignature];
63
        [inv setSelector:cacheSelector];
64
        [inv setTarget:class];
65
        [inv setArgument:&(currentImp) atIndex:2];
66
        [inv setArgument:&(newImp) atIndex:3];
67
        [inv setArgument:&(resolvedClass) atIndex:4];
68
        [inv setArgument:(void *_Nonnull)&(selector) atIndex:5];
69
        [inv invoke];
70
      }
71
    }
72
#endif
73
 
74
    const char *typeEncoding = method_getTypeEncoding(method);
75
    __unused IMP originalImpOfClass =
76
        class_replaceMethod(resolvedClass, selector, newImp, typeEncoding);
77
 
78
#ifdef DEBUG
79
    // If !originalImpOfClass, then the IMP came from a superclass.
80
    if (originalImpOfClass) {
81
      SEL selector = NSSelectorFromString(@"originalIMPOfCurrentIMP:");
82
      NSMethodSignature *methodSignature = [class methodSignatureForSelector:selector];
83
      if (methodSignature != nil) {
84
        NSInvocation *inv = [NSInvocation invocationWithMethodSignature:methodSignature];
85
        [inv setSelector:selector];
86
        [inv setTarget:class];
87
        [inv setArgument:&(currentImp) atIndex:2];
88
        [inv invoke];
89
        IMP testOriginal;
90
        [inv getReturnValue:&testOriginal];
91
        if (originalImpOfClass != testOriginal) {
92
          GULLogWarning(kGULLoggerSwizzler, NO,
93
                        [NSString stringWithFormat:@"I-SWZ%06ld",
94
                                                   (long)kGULSwizzlerMessageCodeMethodSwizzling000],
95
                        @"Swizzling class: %@ SEL:%@ after it has been previously been swizzled.",
96
                        NSStringFromClass(resolvedClass), NSStringFromSelector(selector));
97
        }
98
      }
99
    }
100
#endif
101
  });
102
}
103
 
104
+ (nullable IMP)currentImplementationForClass:(Class)aClass
105
                                     selector:(SEL)selector
106
                              isClassSelector:(BOOL)isClassSelector {
107
  NSAssert(selector, @"The selector cannot be NULL");
108
  NSAssert(aClass, @"The class cannot be Nil");
109
  if (selector == NULL || aClass == nil) {
110
    return nil;
111
  }
112
  __block IMP currentIMP = nil;
113
  dispatch_sync(GetGULSwizzlingQueue(), ^{
114
    Method method = nil;
115
    if (isClassSelector) {
116
      method = class_getClassMethod(aClass, selector);
117
    } else {
118
      method = class_getInstanceMethod(aClass, selector);
119
    }
120
    NSAssert(method, @"The Method for this class/selector combo doesn't exist (%@, %@).",
121
             NSStringFromClass(aClass), NSStringFromSelector(selector));
122
    if (method == nil) {
123
      return;
124
    }
125
    currentIMP = method_getImplementation(method);
126
    NSAssert(currentIMP, @"The IMP for this class/selector combo doesn't exist (%@, %@).",
127
             NSStringFromClass(aClass), NSStringFromSelector(selector));
128
  });
129
  return currentIMP;
130
}
131
 
132
+ (BOOL)selector:(SEL)selector existsInClass:(Class)aClass isClassSelector:(BOOL)isClassSelector {
133
  Method method = isClassSelector ? class_getClassMethod(aClass, selector)
134
                                  : class_getInstanceMethod(aClass, selector);
135
  return method != nil;
136
}
137
 
138
+ (NSArray<id> *)ivarObjectsForObject:(id)object {
139
  NSMutableArray *array = [NSMutableArray array];
140
  unsigned int count;
141
  Ivar *vars = class_copyIvarList([object class], &count);
142
  for (NSUInteger i = 0; i < count; i++) {
143
    const char *typeEncoding = ivar_getTypeEncoding(vars[i]);
144
    // Check to see if the ivar is an object.
145
    if (strncmp(typeEncoding, "@", 1) == 0) {
146
      id ivarObject = object_getIvar(object, vars[i]);
147
      [array addObject:ivarObject];
148
    }
149
  }
150
  free(vars);
151
  return array;
152
}
153
@end