c语言编程笔录

首页 >   > 笔记大全

笔记大全

使用iOS Method Swizzling会遇到哪些问题

更新时间:2023-12-26

使用iOS Method Swizzling会遇到哪些问题

Method Swizzling是iOS中一个有用的特性,它可以更改原有方法的实现。我们可以通过Method Swizzling更改在运行时编写的方法的行为,但它也可能会导致一些问题。

1. 可能导致崩溃

由于Method Swizzling是在运行时进行的,如果它被不当使用或者不正确地实现,那么就可能导致应用程序崩溃。这是因为钩子函数中的代码会被直接插入原函数的实现代码中,如果处理不恰当,会破坏方法的结构,导致崩溃。以下是一段调用系统函数的代码。

  #import <objc/runtime.h>

  @implementation NSString (Custom)

  - (NSString *)custom_uppercaseString {
      return [self custom_uppercaseString]; // This will cause a crash
  }

  + (void)load {
      Method originalMethod = class_getInstanceMethod(self, @selector(uppercaseString));
      Method swizzledMethod = class_getInstanceMethod(self, @selector(custom_uppercaseString));
      method_exchangeImplementations(originalMethod, swizzledMethod);
  }

  @end
  

在上面的代码中,我们使用Method Swizzling更改了NSString的uppercaseString方法。但是,我们没有更改我们的自定义实现方法。在我们的自定义实现中,我们调用了uppercaseString方法,这会导致应用程序崩溃。

2. 可能会影响其他类

Method Swizzling可以更改任何类的方法,但是它可能会影响到其他类的代码。如果您以后更新了某个框架或库,那么之前实现过Swizzling的方法的行为可能会与您的预期不一致。

  #import <objc/runtime.h>

  @implementation NSArray (Custom)

  - (id)custom_objectAtIndex:(NSUInteger)index {
      if (index < self.count) {
          return [self custom_objectAtIndex:index];
      }
      return nil;
  }

  + (void)load {
      Method originalMethod = class_getInstanceMethod(self, @selector(objectAtIndex:));
      Method swizzledMethod = class_getInstanceMethod(self, @selector(custom_objectAtIndex:));
      method_exchangeImplementations(originalMethod, swizzledMethod);
  }

  @end

  NSArray *arr = @[@"1", @"2", @"3"];
  NSLog(@"arr[4]: %@", arr[4]);
  

在上面的代码中,我们使用Method Swizzling扩展了NSArray的objectAtIndex方法。然而,这个方法实际上会影响所有对象组合的行为。在上面的代码中,当我们访问arr[4]时,应用程序将崩溃,因为我们的自定义方法返回的是nil。

3. 可能会破坏应用程序的一些实现

Method Swizzling可能会破坏应用程序的一些实现,比如缓存。如果您更改了用于缓存的方法,那么将会失效缓存,可能会导致大量的额外计算。

  #import <objc/runtime.h>

  @implementation NSURLRequest (Custom)

  - (NSDictionary *)custom_headers {

      NSDictionary *originalHeaders = [self custom_headers];
      NSMutableDictionary *modifiedHeaders = [[NSMutableDictionary alloc] initWithDictionary:originalHeaders];

      if (self.URL) {
          [modifiedHeaders setObject:@"application/json" forKey:@"Content-Type"];
      }

      return [NSDictionary dictionaryWithDictionary:modifiedHeaders];
  }

  + (void)load {
      Method originalMethod = class_getInstanceMethod(self, @selector(allHTTPHeaderFields));
      Method swizzledMethod = class_getInstanceMethod(self, @selector(custom_headers));
      method_exchangeImplementations(originalMethod, swizzledMethod);
  }

  @end
  

在上面的代码中,我们使用Method Swizzling更改了NSURLRequest的allHTTPHeaderFields方法。我们的自定义方法添加了一个Content-Type标头字段,然后返回所有标头的字典。但是,NSURLRequest使用缓存来存储标头,这意味着我们没有更改NSURLRequest对象的标头。这会导致自定义标头字段不被添加到请求中。

4. 在多线程环境中可能会出现竞态条件

在多线程环境中,使用Swizzling也需要特别注意,因为它可能会导致竞态条件。由于Swizzling不局限于任何特定的线程或队列,因此可能会在不同线程之间交叉执行钩子函数和原函数的代码。

  #import <objc/runtime.h>

  @implementation NSString (Custom)

  - (NSString *)custom_uppercaseString {
      return [self custom_uppercaseString];
  }

  + (void)load {
      Method originalMethod = class_getInstanceMethod(self, @selector(uppercaseString));
      Method swizzledMethod = class_getInstanceMethod(self, @selector(custom_uppercaseString));
      method_exchangeImplementations(originalMethod, swizzledMethod);
  }

  @end

  - (void)testSwizzling {
      dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
      NSString *string = @"test string";

      dispatch_async(queue, ^{
          NSString *uppercaseString = [string uppercaseString];
      });

      dispatch_async(queue, ^{
          NSString *customUppercaseString = [string custom_uppercaseString];
      });
  }
  

在上面的代码中,我们使用Method Swizzling更改了NSString的uppercaseString方法,并在两个不同的异步队列中运行它。由于Method Swizzling是在运行时进行的,我们不知道哪个线程会首先运行自定义方法和原始方法。在这种情况下,我们将会遇到一个竞态条件。

总结

Method Swizzling是iOS开发中非常神奇的特性,可以帮助我们更改原有方法的实现,但其实现需要特别小心,因为它可能会带来一些问题。在使用Method Swizzling时,请特别注意以下问题:

  • Method Swizzling可能会导致应用程序崩溃。
  • Method Swizzling可能会影响其他类的代码,导致程序的不稳定。
  • Method Swizzling可能会破坏应用程序的一些实现,比如缓存。
  • 在多线程环境中,Method Swizzling可能会导致竞态条件。