Stay weird. Stay different.

0%

Effective-Objective-c-2-0读书笔记(三)

  • 第九条:以类族模式隐藏实现细节

    类族可以隐藏抽象基类的实现细节,其中IOS中常用的UIButton就是使用的这种模式。

    1
    + (UIButton *)buttonWithType:(UIButtonType)type;

    在具体实现中,会根据type的不同,调用不同子类的初始化方法。

    在具体的应用中,也可以使用这种模式,通过一个通用的基类创建不同的子类对象。

    1. 声明枚举类型

      1
      2
      3
      4
      5
      typdef NS_ENUM(NSUInteger, EOCEmployeeType) {
      EOCEmployeeTypeDeveloper,
      EOCEmployeeTypeDesigner,
      EOCEmployeeTypeFinance
      }
    2. 声明工厂方法

      1
      + (EOCEmployee *)employeeWithType:(EOCEmployeeType)type;
    3. 实现工厂方法

      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      13
      + (EOCEmployee *)employeeWithType:(EOCEmployeeType)type {
      switch (type) {
      case EOCEmployeeTypeDeveloper:
      return [EOCEmployeeTypeDeveloper new];
      break;
      case EOCEmployeeTypeDesigner:
      return [EOCEmployeeTypeDesigner new];
      break;
      case EOCEmployeeTypeFinance:
      return [EOCEmployeeTypeFinance new];
      break;
      }
      }
  • 第十条:关联对象

    关联对象可以在不改变类的实现或继承类的情况下,实现自己的功能

    • 常用的关联方法有:

      1. 给某对象设置关联对象,通过键值的方法

        1
        void objc_setAssociatedObject (id object, void *key, id value, objc_AssociationPolicy policy)
      2. 获取制定key的获取关联对象值

        1
        id objc_getAssociatedObject(id object, void *key)
      3. 移除关联

        1
        void objc_removeAssociatedObjects(id object)s
      4. objc_AssociationPolicy含义
        OBJC_ASSOCIATION_ASSIGN对应assign
        OBJC_ASSOCIATION_RETAIN_NONATOMIC对应nonatomic, retain
        OBJC_ASSOCIATION_COPY_NONATOMIC对应nonatomic, copy
        OBJC_ASSOCIATION_RETAIN对应retain
        OBJC_ASSOCIATION_COPY对应copy

    • 应用场景:

      1
      2
      3
      #import <objc/runtime.h>

      static void *EOCMyAlertViewKey = "EOCMyAlertViewKey";
      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      13
      14
      15
      16
      17
      18
      19
      20
      21
      - (void)askUserAQuestion {
      UIAlertView *alert = [[UIAlertView alloc]initWithTitle:@"Question"
      message :@"What do you want"
      delegate:self
      cancelButtonTittle:@"Cancel"
      otherButtonTitle:@"continue",nil];
      void (^block)(NSInteger) = ^(NSInteger buttonIndex) {
      if (buttonIndex) == 0 {
      [self doCancel];
      }else {
      [self doContinue];
      }
      };

      objc_setAssociatedObject(alert,
      EOCMyAlertViewKey,
      block,
      OBJC_ASSOCIATION_COPY)

      [alert show];
      }
      1
      2
      3
      4
      // UIAlertViewDelegate
      - (void)alertView:(UIAlertView *)alertView clickedButtonAtIndex:(NSInteger)buttonIndex {
      void (^block)(NSInteger) = objc_getAssociatedObject(alertView, EOCMyAlertViewKey); block(buttonIndex);
      }
  • 第十一条:理解objc_msgSend的作用

    给对象发送消息可以这样写:

    1
    id returnValue = [someObject messageName:parameters];

    转化成运行时语句:

    1
    id returnValue = objc_msgSend(someObject, @selector(messageName:),parameters);
    • 此时objc_msgSend会检索方法列表,如果找到就跳转到实现代码,如果找不到就沿着继承关系继续向上查找,等找到合适的方法再跳转,如果一直追溯到最顶层的基类仍没找到方法,就会采用消息转发的方式。
    • 当找到了方法的实现代码,就会放到每个类对应的缓存中。下次再调用相同的方法的时候就不用经历上面的过程,直接调用缓存中的方法实现就行了。
    • 在特别的情况下会调用另一些方法:
      objc_msgSend_stret:返回结构体
      objc_msgSend_fpret:返回的是浮点数
      objc_msgSendSuper:直接调用父类方法
  • 第十二条:理解消息转发机制

    当对象在运行时接收到无法解读的消息后,会启动消息转发机制,在编写代码是可以通过加入消息转发机制,来处理未知消息。
    消息转发可以分为两大阶段:

    1. 动态添加方法
      对象在收到无法解读的消息时,首先调用下列方法:

      1
      2
      + (BOOL)resolveInstanceMethod:(SEL)selector;
      + (BOOL)resolveClassMethod:(SEL)selector;

      例如下面的方法实现会通过选择字的名称生成对应的settergetter

      1
      2
      3
      4
      5
      6
      7
      8
      + (BOOL)resolveInstanceMethod:(SEL)selector {
      NSString *selectorString = NSStringFromSelector(selector);
      if ([selectorString hasPrefix:@"set"]) {
      class_addMethod(self, selector, (IMP)autoDictionarySetter, "v@:@");
      }else {
      class_addMethod(self, selector, (IMP)autoDictionaryGetter, "@@:");
      }
      }

      这种办法的前提是相关方法的实现代码已经写好,常用在@dynamic属性

    2. 完整的消息转发机制

      • 备援接收者
        当通过动态添加方法失败后,运行时系统会尝试转发给其他的接收者来处理

        1
        - (id)forwardingTargetForSelector:(SEL)selector;

        该方法返回备选的接收对象,如果找不到就返回nil

        通过此方案可以用组合模拟多继承,具体实现是,在一个对象内部可以有多个其他对象,这时可以通过消息转发获取到备援接收者,并向其发送消息。这样在使用时从外界看来就是本体对象处理了这些消息,造成多继承的假象。

        但是要注意的一点是,此种方法不能修改信息内容,并且编译报错,要想解决编译错误就要新建一个category把要转发的消息声明在.h中,但并不需要实现,这样编译就不会报错了。

      • 完整的消息转发

        调用下面方法实现转发

        1
        2
        3
        4
        5
        6
        - (void)forwardInvocation:(NSInvocation *)anInvocation {
        if ([someOtherObject respondsToSelector:[anInvocation selector]])
        [anInvocation invokeWithTarget:someOtherObject];
        else
        [super forwardInvocation:anInvocation];
        }