iOS开发

Effective Objective - C 2.0读书笔记

2016-01-08  本文已影响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(总结)

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

上一篇下一篇

猜你喜欢

热点阅读