美文网首页
《编写高质量iOS与OS X代码的52个有效方法》10-13

《编写高质量iOS与OS X代码的52个有效方法》10-13

作者: 小如99 | 来源:发表于2018-06-30 15:30 被阅读11次

    第10条:在既有类中使用关联对象存放自定义数据

    可以给某对象关联许多其他对象,这些对象通过“键”来区分。存储对象值的时候可以致命“存储策略”,用以维护相应的“内存管理语义”。

    1. 存储策略由名为objc_AssociationPolicy的枚举所定义,如下

      存储策略.png
    2. 管理关联对象

    • void objc_setAssociatedObject(id object, const void *key, id value, objc_AssociationPolicy policy)
      此方法以给定的键和策略为某对象设置关联对象值。
    • id objc_getAssociatedObject(id object, const void *key)
      此方法根据给定的键从某对象中获取相应的关联对象值。
    • void objc_removeAssociatedObjects(id object)
      此方法移除指定对象的全部关联对象。
    1. 我们可以把某对象想象成NSDictionary,存储关联对象就相当于在NSDictionary对象上调用setObject和objectForKey方法,然而两者之间有个重要差别:设置关联对象用的key是个“不透明的指针”(opaque pointer),即其所指向的数据结构不局限于某个特定类型的指针(void *),鉴于此,在设置关联对象值时,通常使用静态全局变量做键。

    代码示例:

    static void *const stringKey = @"stringKey";
    - (void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event {
        NSString *str = @"1234";
        UIAlertView *alerView = [[UIAlertView alloc] initWithTitle:@"touchBegan" message:@"Message" delegate:self cancelButtonTitle:@"cancel" otherButtonTitles:@"continue", nil];
        objc_setAssociatedObject(str, stringKey, alerView, OBJC_ASSOCIATION_RETAIN);
    
        sleep(4);
    
         UIAlertView *alerView1 = objc_getAssociatedObject(str, stringKey);
         if (alerView1) {
             [alerView1 show];
         }
    
         return;
    }
    

    这儿把UIAlertView的对象关联到NSString的key值中去了,然后再取出来

    要点:

    • 可以通过“关联对象”机制来把两个对象连起来。

    • 定义关联对象时可指定内存管理语义,用以模仿定义属性时所采用的“拥有关系”与“非拥有关系”

    • 只有在其他做法不可行时才应选用关联对象,因为这种做法通常会引入难于查找的bug。

    第11条:理解object_msgSend的作用

    1. 动态绑定:所要调用的函数要到运行期才能确定
    2. id returnValue = [someObject messageName:parameter];
      someObject -> 接收者(receiver)
      messageName: ->选择子(selector)
      messageName:parameter ->选择子+参数=消息(message)

    上面OC消息传递底层用C语言实现如下:

    void objc_msgSend(someObject, @selector(messageName:), parameter)

    1. 尾调用优化(tail-call optimization)技术细节参考: http://en.wikipedia.org/wiki/Tail_call
    2. Objective-C运行环境中objc_msgSend的特殊处理函数
    • objc_msgSend_stret。如果待发送的消息要返回结构体,交由此函数处理。
    • objc_msgSend_fpret。如果消息返回的是浮点数,交由此函数处理。
    • objc_msgSendSuper。如果要给超类发消息,交由此函数处理

    要点:

    • 消息由接收者、选择子及参数构成。给某对象“发送消息”也就相当于在该对象上“调用方法”。

    • 发给某对象的全部消息都要由“动态消息派发系统”来处理,该系统会查出对应的方法,并执行其代码

    第12条:理解消息转发机制

    1. 代理就是采用了消息转发机制,自己不实现,通过转发机制转给delegate的对象去实现。

    2. 下列代码演示了如何使用“resolveInstanceMethod:” 来实现@dynamic属性

      id autoDictionaryGetter(id self,SEL _cmd);
      void autoDictionarySetter(id self,SEL _cmd,id value);
      + (BOOL)resolveInstanceMethod:(SEL)sel
      {
          //* 将选择子化为字符串 */
          NSString *selectorString = NSStringFromSelector(sel);
          if (/* selector is from a @dynamic property */)
          {
               //* 检测其是否表示设置方法,若前缀未set,则表示设置方法,否则就是获取方法 */
               if ([selectorString hasPrefix:@"set"])
              {
                  class_addMethod(self, sel, (IMP)autoDictionarySetter, “v@:@“);
              }
              else
              {
                  class_addMethod(self, sel, (IMP)autoDictionaryGetter, "@@:");
               }
              return YES;
          }
          return [super resolveInstanceMethod:sel];
      } 
      

    v@:@ 为类型编码(Type Encodings),详细信息请到官网Type Encodings

    1. 消息转发流程图 消息转发流程图.png
      我自己测的时候没有走forwarInvocation方法,不知道为什么。

    要点:

    • 若对象无法响应某个选择子,则进入消息转发流程。
    • 通过运行期的动态方法解析功能,我们可以再需要用到某个方法时再将其加入类中。
    • 对象可以把其无法解读的某些选择子转交给其他对象来处理。
    • 经过上述两步之后,如果还是没办法处理选择子,那就启动完成的消息转发机制。

    第13条:用“方法调配技术”调试“黑盒方法”

    method swizzing 没啥好记录的

    要点:

    • 在运行期,可以向类中新增或替换选择子所对应的方法实现。
    • 使用另一份实现来替换原有的方法实现,这道工序叫做“方法调配”,开发者常用此技术向原有实现中添加新功能。
    • 一般来说,只有调试程序的时候才需要在运行期修改方法实现,这种做法不宜滥用。若是滥用,反而会令代码变得不易读懂且难于维护

    相关文章

      网友评论

          本文标题:《编写高质量iOS与OS X代码的52个有效方法》10-13

          本文链接:https://www.haomeiwen.com/subject/keituftx.html