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

第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 *)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)