天天看点

OC--消息机制

消息机制深入理解:

  1. Objective-C是一门动态语言,其动态特性很大程度上是归因于其消息机制。
  2. Objective-C采用了消息发送、消息选择器、动态类型和运行时检查等机制。
  3. 消息选择器
    1. 方法和选择器:
      1. 在OC中,selector有两重含义:一是在源代码中它代表了向对象发送消息的方法名称;二是在编译后它代表了方法名称被编译过后一个统一的标识。
      2. 编译后的方法名称有一个类型名称为SEL,所有的同名方法都共享同一个selector。
      3. 一定要注意:方法名称和方法不是一个东西。方法是method,类型是IMP,类似C语言的函数,是指对象的一段可运行的代码。  而方法名称是selector,类型是SEL,是给方法取得名字,不同的类的同名方法共享一个方法名称的编号。
      4. 采用选择器体现了OC的多态性和动态绑定性。
      5. 要使用动态绑定,同名的方法必须采用同样的参数类型和返回类型。
      6. _cmd是什么?  _cmd表示当前方法的selector,可以赋值给SEL类型的变量,可以作为参数传递。
      7. 运行时改变消息:在NSObject协议中定义了performSelector:方法,它起到的作用和直接调用方法是一样的,但是由于performSelector:的参数是可以替换的,因此容许在运行时改变消息。
    2. Target-Action模式:
      1. target是一个指向其它对象的指针,action是会发给target的消息。例如:[button addTarget:anObject action:angel forControlEvents:UIControlEventTouchUpInside]; // 这条语句给button对象添加了一个target,是对象anObject,并确定了待发送消息的是angel,相应的动作是当用户触摸按钮之后抬起手指时。 当用户触摸按钮并抬起手指后,button按钮会发送消息给anOjbject,实际上button内部会调用方法:[button performSelector:angel withOjbect:anObject]; // 所有的动作消息只有一个参数,就是消息的发送者,类型为id。
    3. 获取方法的地址:
      1. OC专门定义了一种类型IMP,可以通过IMP来获取方法地址:例如:IMP = impStudy = [student methodForSelector:@selector(study)];
      2. 其实IMP是一个函数指针,他的定义式:typedef id (*IMP) (id, SEL, …);
      3. 这个函数有两个以上的参数,第一个是运行该函数的对象,第二个是对象的方法名,后面是这个方法所带的参数。我们可以如下调用:impStudy(student, @selector(study)); // 这个语句和[student study];的作用是一样的,区别在于使用函数指针是直接调用方法的地址,更快一点。而使用[student study];要先给student对象发送study消息,然后运行系统会去寻找消息对应的名称,再找到方法的地址,然后去运行,效率会低一点。
  4. 容许静态行为:OC是一门动态语言,有很大的灵活性和优点,但是也会带来一些不好的地方,有时候我们需要暂时屏蔽其动态特性。
    1. 默认的动态行为:
      1. 对象的内存空间是动态申请的,由运行时刻的类方法来创建对象实例。
      2. 对象都是动态类型的。在编译时期的源代码中,对象可以是id类型的,知道运行时刻才确定具体的类(从而确定其方法和数据结构)。
      3. 消息和方法也是动态绑定的。运行时系统会将消息中得消息选择器与消息接受者的方法进行匹配。
    2.  静态类型
      1. 在定义变量的时候如果我们使用id,而使用具体的某一类的名称,我们称为静态类型:例如:id person; // 动态类型         Student *stu; // 静态类型
      2. 静态类型给了编译器有关对象更多得信息,相比单纯的采用id类型,有以下不同点:1.容许编译期间进行类型检查 2.使代码更具可读性 3.静态类型的对象不用遵循“同名的方法必须具备同样的参数和返回值”的规则 4.可以通过结构体指针直接访问对象的实例变量 
      3. isMemberOfClass: //是否是给定类的实例
      4. isKingOfClass: // 
  5.  运行时消息检查:
    1. 当对象接收到一条它无法实现的消息的时候,会产生错误。因为消息是在运行时刻才发出,所以出错的时候不一定那么明显。这时候我们可以用respondsToSelector:方法来检测 一个对象时候能响应某条消息。
    2. 例子:Student *stu = [[Student alloc] init];if ([stu respondsToSelector:@selector(englishTest)])       [stu englistTest];else      fprintf(stderr, "%s can't, test \n", [NSStringFromClass([stu class]) UTF8String]);
  6.  消息传递:
    1. 有两个对象A、B,当给A发送一条消息,但是A无法处理的时候它会不会传递给B对象呢? 答案是肯定的。
    2. 在添加消息转发功能前,必须要覆盖两个方法:methodSignatureForSelector: // 为另一个类实现消息创建一个有效的方法签名forwardInvocation: // 将选择器转发给一个真正实现了该消息的对象
oc