美文网首页iOS开发
Effective Objective - C 2.0读书笔记

Effective Objective - C 2.0读书笔记

作者: 庸者的救赎 | 来源:发表于2016-01-08 00:05 被阅读157次

    概述

    有时候我们需要关联一些数据给已有的对象,通常你可能会使用子类化的方式.然而,有可能不会一直这么做,因为有些实例可能是你因为某些意义而创建,而你又不想把这个暴露给创建的子类,或者说你只想加到已有的类中去,那么这个时候你该尝试一下Objective - C的对象关联(Associated Objects).

    当对象被关联的时候,通常会使用一个key来标识.并且对象会指定一种存储策略来管理被存储值.管理策略如下:

    typedef OBJC_ENUM(uintptr_t, objc_AssociationPolicy) {
        OBJC_ASSOCIATION_ASSIGN = 0, // 指定关联对象为弱引用
        OBJC_ASSOCIATION_RETAIN_NONATOMIC = 1, // 指定关联对象为强引用,并且为非原子性
        OBJC_ASSOCIATION_COPY_NONATOMIC = 3, // 指定关联对象为copy,并且为非原子性
        OBJC_ASSOCIATION_RETAIN = 01401, // 指定关联对象为强引用 
        OBJC_ASSOCIATION_COPY = 01403 // 指定关联对象为copy
    };
    

    关联管理使用以下方法来执行:

    // 设置关联对象的key和策略
    void objc_setAssociatedObject(id object, void *key,id value, objc_AssociationPolicy policy);
    
    // 通过给定的key取出关联对象
    id objc_getAssociatedObject(id object, void *key);
    
    // 移除关联的对象
    void objc_removeAssociatedObjects(id object);
    

    访问关联对象看起来很像NSDictionary的存取值方法

    // 设置
    [object setObject:value forKey:key];
    
    // 取出
    [object objectForKey:key];
    

    但是他们有一个关键的不同点,就是如果是NSDictionary在设置key的时候可以随意使用一个key,不用在意其作用域(scope),而对于Associated Objects来说这个key必须是全局的.

    示例

    在iOS开发中,我们经常会使用到一个控件UIAlertView,通常我们会先使用系统提供的初始化方法设置其title,message,delegate和相应的button.这样很是不方便,有时候我们会遇到这样的问题,我们要根据用户的点击来做一些处理,比如改变一个控件的颜色,那么这时候我们就要把该控变成全局的,然后在UIAlertViewdelegate回调方法中去改变颜色.代码如下:

    - (void)changeViewBackGroundColor
    {
        UIAlertView *alert = [[UIAlertView alloc] initWithTitle:@"友情提示" 
                                                       message:@"你确定要改变view的颜色?" 
                                                      delegate:self 
                                             cancelButtonTitle:@"取消" 
                                              otherButtonTitle:@"确定",nil];
        [alert show];
    }
    
    // UIAlertViewDelegate protocol method
    - (void)alertView:(UIAlertView *)alertView clickedButtonAtIndex:(NSInteger)buttonIndex
    {
        if (buttonIndex == 0) {
            // 改变颜色
        } else {
            // 不改变颜色
        }
    }
    

    这个方法看起来很方便,但是如果你需要在同一个试图控制器里面弹出多个不同的alert,这绝对会让你蛋疼不已,你需要在delegate方法中判断是哪一个alerView,然后根据不同的alerView做不同的处理,哦对了,最蛋疼的是你需要把这些alerView写成全局的... …

    当然啦,如果你能灵活的使用Associated Objects给系统的UIAlertView关联一个block回调,那么事情就简单多了

    呃... …

    是不是感觉要掉渣天了?要起飞了?Let’s Go!!!

    #import <UIKit/UIKit.h>
    
    // 声明block类型
    typedef void(^AlertViewCallBack)(NSInteger buttonIndex);
    
    @interface UIAlertView (Block)<UIAlertViewDelegate>
    
    /** 关联值block,在Category里面写属性自带@dynamic,所以在.m里面已定要对应的实现其setter和getter */
    @property (copy, nonatomic) AlertViewCallBack callBackBlock;
    
    /** 去掉了delegate,加入了block*/
    + (void)alertWithCallBackBlock:(AlertViewCallBack)callBackBlock
                             title:(NSString *)title
                           message:(NSString *)message
                 cancelButtonTitle:(NSString *)cancelButtonTitle
                 otherButtonTitles:(NSString *)otherButtonTitles, ... NS_REQUIRES_NIL_TERMINATION;
    
    @end
    
    #import "UIAlertView+Block.h"
    #import <objc/runtime.h>
    
    // 声明全局的key
    static NSString *UIAlertViewKey = @"AlertViewKey";
    
    @implementation UIAlertView (Block)
    
    + (void)alertWithCallBackBlock:(AlertViewCallBack)callBackBlock title:(NSString *)title message:(NSString *)message cancelButtonTitle:(NSString *)cancelButtonTitle otherButtonTitles:(NSString *)otherButtonTitles, ...
    {
        UIAlertView *alert = [[UIAlertView alloc] initWithTitle:title
                                                        message:message
                                                       delegate:nil
                                              cancelButtonTitle:cancelButtonTitle
                                              otherButtonTitles:otherButtonTitles, nil];
        // 使用系统的宏来实现多参数
        NSString *other = nil;
        va_list args;
        if (otherButtonTitles) {
            va_start(args, otherButtonTitles);
            while ((other =  va_arg(args, NSString*))) {
                [alert addButtonWithTitle:other];
            }
            va_end(args);
        }
        alert.delegate = alert;
        [alert show];
        
        // 形参block赋值给属性block
        alert.callBackBlock = callBackBlock;
    }
    
    // setter
    - (void)setCallBackBlock:(AlertViewCallBack)callBackBlock
    {
        // 设置关联值
        [self willChangeValueForKey:@"callBackBlock"];
        objc_setAssociatedObject(self, &UIAlertViewKey, callBackBlock, OBJC_ASSOCIATION_COPY);
        [self didChangeValueForKey:@"callBackBlock"];
    }
    
    // getter
    - (AlertViewCallBack)callBackBlock
    {
        // 取出关联值
        return objc_getAssociatedObject(self, &UIAlertViewKey);
    }
    
    // delegate
    - (void)alertView:(UIAlertView *)alertView clickedButtonAtIndex:(NSInteger)buttonIndex
    {
        if (self.callBackBlock) {
            
            self.callBackBlock(buttonIndex);
        }
    }
    
    @end
    

    使用方法如下:

        // 使用方法,注意这里会造成循环引用,所以记住加__weak,其实更为优雅的方式是使用影子变量
        // __weak typeof(self) weakSelf = self;
        // @weakify(self);
        [UIAlertView alertWithCallBackBlock:^(NSInteger buttonIndex) {
        
            // @strongify(self);
        
            // 这里就可以直接拿到点击button的index,爽歪歪有木有?
            // 再也不用写又臭又长的delegate方法了,在不怕需要弹出多个alertView了
            // 想怎么弹就怎么弹,想再哪里弹就在哪里弹
            
            if (buttonIndex == 0) { // 确定
                NSLog(@"我弹弹弹,弹走鱼尾纹...😂");
            }
            
            // 如果用到了本类的属性或者调用方法,一定要注意循环引用问题💣💣💣
            
            // 非影子变量
            // [weakSelf.view setBackgroundColor:[UIColor redColor]];
            
            // 影子变量
            // [self.view setBackgroundColor:[UIColor redColor]];
        } title:@"友情提示" message:@"你确定要改变view的颜色?" cancelButtonTitle:@"取消" otherButtonTitles:@"确定", nil];
    

    Conclusion(总结)

    感受到了么?这是个多么强大的东西!

    • Associated objects提供了一种关联两个对象的方法
    • 注意使用内存管理的策略
    • 不要滥用Associated objects,因为这玩意一旦出bug很难查找

    相关文章

      网友评论

        本文标题:Effective Objective - C 2.0读书笔记

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