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/ISASwizzler/Public/GoogleUtilities/GULObjectSwizzler.h"
16
 
17
#import <objc/runtime.h>
18
 
19
#import "GoogleUtilities/ISASwizzler/GULObjectSwizzler+Internal.h"
20
#import "GoogleUtilities/ISASwizzler/Public/GoogleUtilities/GULSwizzledObject.h"
21
 
22
@implementation GULObjectSwizzler {
23
  // The swizzled object.
24
  __weak id _swizzledObject;
25
 
26
  // The original class of the object.
27
  Class _originalClass;
28
 
29
  // The dynamically generated subclass of _originalClass.
30
  Class _generatedClass;
31
}
32
 
33
#pragma mark - Class methods
34
 
35
+ (void)setAssociatedObject:(id)object
36
                        key:(NSString *)key
37
                      value:(nullable id)value
38
                association:(GUL_ASSOCIATION)association {
39
  objc_AssociationPolicy resolvedAssociation;
40
  switch (association) {
41
    case GUL_ASSOCIATION_ASSIGN:
42
      resolvedAssociation = OBJC_ASSOCIATION_ASSIGN;
43
      break;
44
 
45
    case GUL_ASSOCIATION_RETAIN_NONATOMIC:
46
      resolvedAssociation = OBJC_ASSOCIATION_RETAIN_NONATOMIC;
47
      break;
48
 
49
    case GUL_ASSOCIATION_COPY_NONATOMIC:
50
      resolvedAssociation = OBJC_ASSOCIATION_COPY_NONATOMIC;
51
      break;
52
 
53
    case GUL_ASSOCIATION_RETAIN:
54
      resolvedAssociation = OBJC_ASSOCIATION_RETAIN;
55
      break;
56
 
57
    case GUL_ASSOCIATION_COPY:
58
      resolvedAssociation = OBJC_ASSOCIATION_COPY;
59
      break;
60
 
61
    default:
62
      break;
63
  }
64
  objc_setAssociatedObject(object, key.UTF8String, value, resolvedAssociation);
65
}
66
 
67
+ (nullable id)getAssociatedObject:(id)object key:(NSString *)key {
68
  return objc_getAssociatedObject(object, key.UTF8String);
69
}
70
 
71
#pragma mark - Instance methods
72
 
73
/** Instantiates an instance of this class.
74
 *
75
 *  @param object The object to swizzle.
76
 *  @return An instance of this class.
77
 */
78
- (instancetype)initWithObject:(id)object {
79
  if (object == nil) {
80
    return nil;
81
  }
82
 
83
  GULObjectSwizzler *existingSwizzler =
84
      [[self class] getAssociatedObject:object key:kGULSwizzlerAssociatedObjectKey];
85
  if ([existingSwizzler isKindOfClass:[GULObjectSwizzler class]]) {
86
    // The object has been swizzled already, no need to swizzle again.
87
    return existingSwizzler;
88
  }
89
 
90
  self = [super init];
91
  if (self) {
92
    _swizzledObject = object;
93
    _originalClass = object_getClass(object);
94
    NSString *newClassName = [NSString stringWithFormat:@"fir_%@_%@", [[NSUUID UUID] UUIDString],
95
                                                        NSStringFromClass(_originalClass)];
96
    _generatedClass = objc_allocateClassPair(_originalClass, newClassName.UTF8String, 0);
97
    NSAssert(_generatedClass, @"Wasn't able to allocate the class pair.");
98
  }
99
  return self;
100
}
101
 
102
- (void)copySelector:(SEL)selector fromClass:(Class)aClass isClassSelector:(BOOL)isClassSelector {
103
  NSAssert(_generatedClass, @"This object has already been unswizzled.");
104
  Method method = isClassSelector ? class_getClassMethod(aClass, selector)
105
                                  : class_getInstanceMethod(aClass, selector);
106
  Class targetClass = isClassSelector ? object_getClass(_generatedClass) : _generatedClass;
107
  IMP implementation = method_getImplementation(method);
108
 
109
  const char *typeEncoding = method_getTypeEncoding(method);
110
  class_replaceMethod(targetClass, selector, implementation, typeEncoding);
111
}
112
 
113
- (void)setAssociatedObjectWithKey:(NSString *)key
114
                             value:(id)value
115
                       association:(GUL_ASSOCIATION)association {
116
  __strong id swizzledObject = _swizzledObject;
117
  if (swizzledObject) {
118
    [[self class] setAssociatedObject:swizzledObject key:key value:value association:association];
119
  }
120
}
121
 
122
- (nullable id)getAssociatedObjectForKey:(NSString *)key {
123
  __strong id swizzledObject = _swizzledObject;
124
  if (swizzledObject) {
125
    return [[self class] getAssociatedObject:swizzledObject key:key];
126
  }
127
  return nil;
128
}
129
 
130
- (void)swizzle {
131
  __strong id swizzledObject = _swizzledObject;
132
 
133
  GULObjectSwizzler *existingSwizzler =
134
      [[self class] getAssociatedObject:swizzledObject key:kGULSwizzlerAssociatedObjectKey];
135
  if (existingSwizzler != nil) {
136
    NSAssert(existingSwizzler == self, @"The swizzled object has a different swizzler.");
137
    // The object has been swizzled already.
138
    return;
139
  }
140
 
141
  if (swizzledObject) {
142
    [GULObjectSwizzler setAssociatedObject:swizzledObject
143
                                       key:kGULSwizzlerAssociatedObjectKey
144
                                     value:self
145
                               association:GUL_ASSOCIATION_RETAIN];
146
 
147
    [GULSwizzledObject copyDonorSelectorsUsingObjectSwizzler:self];
148
 
149
    NSAssert(_originalClass == object_getClass(swizzledObject),
150
             @"The original class is not the reported class now.");
151
    NSAssert(class_getInstanceSize(_originalClass) == class_getInstanceSize(_generatedClass),
152
             @"The instance size of the generated class must be equal to the original class.");
153
    objc_registerClassPair(_generatedClass);
154
    Class doubleCheckOriginalClass __unused = object_setClass(_swizzledObject, _generatedClass);
155
    NSAssert(_originalClass == doubleCheckOriginalClass,
156
             @"The original class must be the same as the class returned by object_setClass");
157
  } else {
158
    NSAssert(NO, @"You can't swizzle a nil object");
159
  }
160
}
161
 
162
- (void)dealloc {
163
  // When the Zombies instrument is enabled, a zombie is created for the swizzled object upon
164
  // deallocation. Because this zombie subclasses the generated class, the swizzler should not
165
  // dispose it during the swizzler's deallocation.
166
  //
167
  // There are other special cases where the generated class might be subclassed by a third-party
168
  // generated classes, for example: https://github.com/firebase/firebase-ios-sdk/issues/9083
169
  // To avoid errors in such cases, the environment variable `GULGeneratedClassDisposeDisabled` can
170
  // be set with `YES`.
171
  NSDictionary *environment = [[NSProcessInfo processInfo] environment];
172
  if ([[environment objectForKey:@"NSZombieEnabled"] boolValue] ||
173
      [[environment objectForKey:@"GULGeneratedClassDisposeDisabled"] boolValue]) {
174
    return;
175
  }
176
 
177
  if (_generatedClass) {
178
    if (_swizzledObject == nil) {
179
      // The swizzled object has been deallocated already, so the generated class can be disposed
180
      // now.
181
      objc_disposeClassPair(_generatedClass);
182
      return;
183
    }
184
 
185
    // GULSwizzledObject is retained by the swizzled object which means that the swizzled object is
186
    // being deallocated now. Let's see if we should schedule the generated class disposal.
187
 
188
    // If the swizzled object has a different class, it most likely indicates that the object was
189
    // ISA swizzled one more time. In this case it is not safe to dispose the generated class. We
190
    // will have to keep it to prevent a crash.
191
 
192
    // TODO: Consider adding a flag that can be set by the host application to dispose the class
193
    // pair unconditionally. It may be used by apps that use ISA Swizzling themself and are
194
    // confident in disposing their subclasses.
195
    BOOL isSwizzledObjectInstanceOfGeneratedClass =
196
        object_getClass(_swizzledObject) == _generatedClass;
197
 
198
    if (isSwizzledObjectInstanceOfGeneratedClass) {
199
      Class generatedClass = _generatedClass;
200
 
201
      // Schedule the generated class disposal after the swizzled object has been deallocated.
202
      dispatch_async(dispatch_get_main_queue(), ^{
203
        objc_disposeClassPair(generatedClass);
204
      });
205
    }
206
  }
207
}
208
 
209
- (BOOL)isSwizzlingProxyObject {
210
  return [_swizzledObject isProxy];
211
}
212
 
213
@end