iOS 开发之黑科技—runtime

runtime其实就是oc底层的一套C语音的API

调用方法的本质就是发消息,

1、动态交换两个方法的实现(特别是交换系统自动的方法)

2、动态添加对象的成员变量和成员方法

3、获得某个类的所有成员方法、所有成员变量

注意:

对于一般OC代码的method swizzling, 在load方法中执行即可. 而Swift没有load, 所以要在initialize中执行.

应用

1、block的实现原理

2、拦截系统自带的方法调用

  1. 1
  2. 2
  3. 3
  4. 4
  5. 5
  6. 6
  7. 7
  8. 8
  9. 9
  10. 10
  1. + (void)load {
  2. // 获取两个类的类方法
  3. Method m1 = class_getClassMethod([UIImage class], @selector(imageNamed:));
  4. Method m2 = class_getClassMethod([UIImage class], @selector(xh_imageNamed:));
  5. // 开始交换方法实现
  6. method_exchangeImplementations(m1, m2);
  7. }
  8. + (UIImage *)xh_imageNamed:(NSString *)name {
  9. return [UIImage xh_imageNamed:name];
  10. }

3、实现类别也可以增加属性

  1. 1
  2. 2
  3. 3
  4. 4
  5. 5
  6. 6
  7. 7
  1. -(NSString *)name{
  2. return objc_getAssociatedObject(self, @"nameKey");
  3. }
  4. -(void)setName:(NSString *)name{
  5. objc_setAssociatedObject(self, @"nameKey", name , OBJC_ASSOCIATION_COPY_NONATOMIC);
  6. }

4、实现nscodeing的自动归档和自动接档

  1. 1
  2. 2
  3. 3
  4. 4
  5. 5
  6. 6
  7. 7
  8. 8
  9. 9
  10. 10
  11. 11
  12. 12
  13. 13
  14. 14
  15. 15
  16. 16
  17. 17
  18. 18
  19. 19
  20. 20
  21. 21
  22. 22
  23. 23
  24. 24
  25. 25
  26. 26
  27. 27
  28. 28
  29. 29
  30. 30
  31. 31
  32. 32
  1. - (void)decode:(NSCoder *)aDecoder {
  2. // 一层层父类往上查找,对父类的属性执行归解档方法
  3. Class c = self.class;
  4. while (c &&c != [NSObject class]) {
  5. unsigned int outCount = 0;
  6. Ivar *ivars = class_copyIvarList(c, &outCount);
  7. for (int i = 0; i < outCount; i++) {
  8. Ivar ivar = ivars[i];
  9. NSString *key = [NSString stringWithUTF8String:ivar_getName(ivar)];
  10. id value = [aDecoder decodeObjectForKey:key];
  11. [self setValue:value forKey:key];
  12. }
  13. }
  14. }
  15. - (void)encode:(NSCoder *)aCoder {
  16. // 一层层父类往上查找,对父类的属性执行归解档方法
  17. Class c = self.class;
  18. while (c &&c != [NSObject class]) {
  19. unsigned int outCount = 0;
  20. Ivar *ivars = class_copyIvarList([self class], &outCount);
  21. for (int i = 0; i < outCount; i++) {
  22. Ivar ivar = ivars[i];
  23. NSString *key = [NSString stringWithUTF8String:ivar_getName(ivar)];
  24. // 如果有实现该方法再去调用
  25. id value = [self valueForKeyPath:key];
  26. [aCoder encodeObject:value forKey:key];
  27. }
  28. }
  29. }

5、实现字典和模型的自动转换

  1. 1
  2. 2
  3. 3
  4. 4
  5. 5
  6. 6
  7. 7
  8. 8
  9. 9
  10. 10
  11. 11
  12. 12
  13. 13
  14. 14
  15. 15
  16. 16
  17. 17
  18. 18
  19. 19
  20. 20
  21. 21
  22. 22
  23. 23
  24. 24
  25. 25
  26. 26
  27. 27
  28. 28
  29. 29
  30. 30
  31. 31
  32. 33
  33. 34
  34. 35
  35. 36
  36. 37
  37. 38
  38. 39
  39. 40
  40. 41
  41. 42
  42. 43
  43. 44
  44. 45
  45. 46
  46. 47
  47. 48
  48. 49
  49. 50
  50. 51
  51. 52
  52. 53
  53. 54
  54. 55
  55. 56
  56. 57
  57. 58
  58. 59
  59. 60
  60. 61
  61. 62
  62. 63
  63. 64
  64. 65
  65. 66
  66. 67
  67. 68
  68. 69
  69. 70
  70. 71
  71. 72
  72. 73
  1. - (void)setDict:(NSDictionary *)dict {
  2. Class c = self.class;
  3. while (c &&c != [NSObject class]) {
  4. unsigned int outCount = 0;
  5. Ivar *ivars = class_copyIvarList(c, &outCount);
  6. for (int i = 0; i < outCount; i++) {
  7. Ivar ivar = ivars[i];
  8. NSString *key = [NSString stringWithUTF8String:ivar_getName(ivar)];
  9. // 成员变量名转为属性名(去掉下划线 _ )
  10. key = [key substringFromIndex:1];
  11. // 取出字典的值
  12. id value = dict[key];
  13. // 如果模型属性数量大于字典键值对数理,模型属性会被赋值为nil而报错
  14. if (value == nil) continue;
  15. // 获得成员变量的类型
  16. NSString *type = [NSString stringWithUTF8String:ivar_getTypeEncoding(ivar)];
  17. // 如果属性是对象类型
  18. NSRange range = [type rangeOfString:@"@"];
  19. if (range.location != NSNotFound) {
  20. // 那么截取对象的名字(比如@"Dog",截取为Dog)
  21. type = [type substringWithRange:NSMakeRange(2, type.length - 3)];
  22. // 排除系统的对象类型
  23. if (![type hasPrefix:@"NS"]) {
  24. // 将对象名转换为对象的类型,将新的对象字典转模型(递归)
  25. Class class = NSClassFromString(type);
  26. value = [class objectWithDict:value];
  27. }else if ([type isEqualToString:@"NSArray"]) {
  28. // 如果是数组类型,将数组中的每个模型进行字典转模型,先创建一个临时数组存放模型
  29. NSArray *array = (NSArray *)value;
  30. NSMutableArray *mArray = [NSMutableArray array];
  31. // 获取到每个模型的类型
  32. id class ;
  33. if ([self respondsToSelector:@selector(arrayObjectClass)]) {
  34. NSString *classStr = [self arrayObjectClass];
  35. class = NSClassFromString(classStr);
  36. }else {
  37. NSLog(@"数组内模型是未知类型");
  38. return;
  39. }
  40. // 将数组中的所有模型进行字典转模型
  41. for (int i = 0; i < array.count; i++) {
  42. [mArray addObject:[class objectWithDict:value[i]]];
  43. }
  44. value = mArray;
  45. }
  46. }
  47. // 将字典中的值设置到模型上
  48. [self setValue:value forKeyPath:key];
  49. }
  50. free(ivars);
  51. c = [c superclass];
  52. }
  53. }
  54. + (instancetype )objectWithDict:(NSDictionary *)dict {
  55. NSObject *obj = [[self alloc]init];
  56. [obj setDict:dict];
  57. return obj;
  58. }

这两个都是

Ivar * ivars = class_copyIvarList(self.class, &outCount);
就不用一个一个属性写了。
再说一下归档解档

如果不是系统的类,要进行归档解档要遵守协议,和实现协议中的方法

  1. 1
  2. 2
  3. 3
  4. 4
  5. 5
  6. 6
  7. 7
  8. 8
  9. 9
  1. -(void)encodeWithCoder:(NSCoder *)aCoder{
  2. [aCoder encodeObject:self.name forKey:@"name"];
  3. }
  4. -(id)initWithCoder:(NSCoder *)aDecoder{
  5. if (self = [super init]) {
  6. self.name = [aDecoder decodeObjectForKey:@"name"];
  7. }

⬅️ Go back