iOS浅谈-runtime

RunTime简称运行时。就是系统在运行的时候的一些机制,其中最主要的是消息机制。对于C语言,函数的调用在编译的时候会决定调用哪个函数。编译完成之后直接顺序执行,无任何二义性。OC的函数调用成为消息发送。属于动态调用过程。在编译的时候并不能决定真正调用哪个函数(事实证明,在编 译阶段,OC可以调用任何函数,即使这个函数并未实现,只要申明过就不会报错。而C语言在编译阶段就会报错)。只有在真正运行的时候才会根据函数的名称找 到对应的函数来调用。

那OC是怎么实现动态调用的呢?下面我们来看看OC通过发送消息来达到动态调用的秘密。假如在OC中写了这样的一个代码:

Person *p = [[Person alloc] init];

对于这样一个简单的调用。在编译时RunTime会将上述代码转化成:

  Person *p = ((Person *(*)(id, SEL))(void *)objc_msgSend)((id)((Person *(*)(id, SEL))(void *)objc_msgSend)((id)objc_getClass("Person"), sel_registerName("alloc")), sel_registerName("init"));

在NSObjcet中存在一个Class的isa指针。然后我们看看Class:

  typedef struct objc_class *Class;

  struct objc_class {

  Class isa; // 指向metaclass

  Class super_class ; // 指向其父类

  const char *name ; // 类名

  long version ; // 类的版本信息,初始化默认为0,可以通过runtime函数  class_setVersion和class_getVersion进行修改、读取

  long info; // 一些标识信息,如CLS_CLASS (0x1L) 表示该类为普通 class ,其中包含对象方法和成员变量;CLS_META (0x2L) 表示该类为 metaclass,其中包含类方法;

  long instance_size ; // 该类的实例变量大小(包括从父类继承下来的实例变量);

  struct objc_ivar_list *ivars; // 用于存储每个成员变量的地址

  struct objc_method_list **methodLists ; // 与 info 的一些标志位有关,如CLS_CLASS (0x1L),则存储对象方法,如CLS_META (0x2L),则存储类方法;

  struct objc_cache *cache; // 指向最近使用的方法的指针,用于提升效率;

  struct objc_protocol_list *protocols; // 存储该类遵守的协议

}

2.runtime有什么作用?

runtime属于OC的底层操作,可以实现使用OC语法无法实现的方法和功能:获取一个类所有的方法,包含私有方法

// 获取类中的方法

- (void)getMethodList

{

  int a = 0;

  int b = 10;

  int c = 2;

  int arr[] = {a,b,c};

  int *p;
  
  p = arr;

  NSLog(@"%d %d",arr[1],p[1]);

// 获取Person类中所有方法

// 参数一:获取哪个类的方法列表

// 参数二:方法列表总数

  unsigned int count = 0;

// 调用完这个方法,count就有值,记录方法列表总数

// 获取仅仅是当前类

// 返回指向方法列表数组

  Method *methodList = class_copyMethodList([Person class], &count);

  // 2 0 1

  for (int i = 0; i < count; i++) {

// 取出对应的方法

  Method method = methodList[i];

// 获取方法名(方法编号)

  SEL methodSel =  method_getName(method);

  NSLog(@"%@",NSStringFromSelector(methodSel));

  }

}

获取类所有的属性,包含私有属性

其实打断点也可以获取一个类的所有属性

方法交换

// 加载分类进内存的时候调用

  + (void)load

{

  NSLog(@"%s",__func__);

// 交换imageNamed和yc_imageNamed实现

// 获取imageNamed方法

// Class:获取哪个类方法

// SEL:获取方法方法编号

  Method imageNameMethod = class_getClassMethod(self, @selector(imageNamed:));

// 获取yc_imageNamed方法

  Method yc_imageNameMethod = class_getClassMethod(self, @selector(yc_imageNamed:));

// 交换方法实现

method_exchangeImplementations(imageNameMethod, yc_imageNameMethod);

}

+ (instancetype)yc_imageNamed:(NSString *)name

{

// 加载图片

  UIImage *image = [UIImage yc_imageNamed:name];

  if (image == nil) {
  
  NSLog(@"加载失败");

  }else{

  NSLog(@"加载成功");

  }

  return image;

}

动态添加方法

#import "Person.h"#import@implementation Person

// 没有返回值,没有参数

// void(*)()

// 完整方法实现

void eat(id self, SEL _cmd, NSString *str)

{

NSLog(@"动态添加的吃方法 %@",str);

}

//  注意:任何一个方法都有两个隐式参数,self,_cmd

// 动态添加方法步骤

// 作用:处理未实现方法

// 什么时候调用:调用了一个未实现的方法就会调用这个方法

// 参数:未实现方法的方法编号

+ (BOOL)resolveInstanceMethod:(SEL)sel

{

NSLog(@"%@",NSStringFromSelector(sel));

// 动态添加eat

if (sel == @selector(eat:)) {

// 动态添加方法

// Class:给哪个类添加方法

// SEL:添加什么方法

// IMP:方法实现,函数入口 -> 函数名

// type:方法类型 v:void @:id :->SEL

class_addMethod(self, sel, (IMP)eat, "v@:@");

return YES;

}

return [super resolveInstanceMethod:sel];

}

@end

动态添加属性

#import "NSObject+Property.h"#import@implementation NSObject (Property)

- (void)setName:(NSString *)name

{

// name保存到对应对象

// 动态添加属性

// object:给那个对象添加属性

// key:属性名

// value:把什么对象保存起来

// policy:策略,用什么策略

objc_setAssociatedObject(self, @"name", name, OBJC_ASSOCIATION_RETAIN_NONATOMIC);

}

- (NSString *)name

{

  return objc_getAssociatedObject(self, @"name");

}

@end

你可能感兴趣的:(iOS浅谈-runtime)