AssociatedObject封装AlertView

UIAlertController/UIAlertView

自从iOS8之后苹果推出了UIAlertController代替UIAlertView,慢慢的UIAlertView也逐渐“失了宠”被deprecated了。当然在使用的过程中我们也发现了UIAlertController的优势,自由度更高,封装起来更方便,至少不用再像UIAlertView那样设置代理,实现代理方法那么繁琐了,所有的操作都可以添加UIAlertAction通过block来进行操作。但是有的比较老的项目还要适配iOS7(当然这种情况是越来越少了),UIAlertController在iOS8之前的版本里边就不能用了,然后我就想能不能封装一个工具类,在iOS8以前的系统里边使用UIAlertView,之后的使用UIAlertController。一直觉得iOS中恰当的使用block是一件非常优雅的事情,当然要注意循环引用的问题,所以就想用block来进行封装,代替代理那一套繁琐的东西。

AlertTool

之前在学习runtime相关知识的时候,看到关联对象这个知识点,可以完美的解决这个问题。创建一个UIAlertView的分类
上代码:

#import 

typedef void (^alertBlock)(NSInteger selectedIndex);

@interface UIAlertView (Category)

- (void)showAlertView:(alertBlock)block;

@end

然后用associateObject把alertBlock与UIAlertView关联起来

- (void)showAlertView:(alertBlock)block{
    if (block) {
        objc_setAssociatedObject(self, &alertKey, block, OBJC_ASSOCIATION_RETAIN_NONATOMIC);
        self.delegate = self;
    }
    [self show];
}
- (void)alertView:(UIAlertView *)alertView clickedButtonAtIndex:(NSInteger)buttonIndex{
    alertBlock block = objc_getAssociatedObject(self, &alertKey);
    if (block) {
        block(buttonIndex);
    }
}

然后封装出来一个工具类alertTool

+ (void)showAlertWithTitle:(NSString *)title message:(NSString *)message confirmBlock:(void (^)())confirmBlock cancelBlock:(void (^)())cancelBlock{
    NSString *version = [UIDevice currentDevice].systemVersion;
    NSString *alertTitle = !title ? title : @"提示";
    NSString *alertMess = !message ? message : @"提示信息";
    if (version.floatValue >= 8.0) {
        UIAlertController *alertController = [UIAlertController alertControllerWithTitle:alertTitle message:alertMess preferredStyle:UIAlertControllerStyleAlert];
        UIAlertAction *confirmAction = [UIAlertAction actionWithTitle:@"确定" style:UIAlertActionStyleDefault handler:^(UIAlertAction * _Nonnull action) {
            confirmBlock();
        }];
        UIAlertAction *cancelAction = [UIAlertAction actionWithTitle:@"取消" style:UIAlertActionStyleCancel handler:^(UIAlertAction * _Nonnull action) {
            cancelBlock();
        }];
        [alertController addAction:cancelAction];
        [alertController addAction:confirmAction];
        [[UIApplication sharedApplication].keyWindow.rootViewController presentViewController:alertController animated:YES completion:nil];
    }else{
        UIAlertView *alertView = [[UIAlertView alloc] initWithTitle:alertTitle message:alertMess delegate:self cancelButtonTitle:@"取消" otherButtonTitles:@"确定", nil];
        [alertView showAlertView:^(NSInteger selectedIndex) {
            if (selectedIndex == 1) {
                confirmBlock();
            }else{
                cancelBlock();
            }
        }];
        
    }
}

以为这里是一个类方法,所以也不用考虑循环引用的问题AlertTool Demo。其实看到这里就会发现真正的主角是objc_setAssociatedObject/objc_getAssociatedObject

objc_setAssociatedObject/objc_getAssociatedObject

上边两个函数的使用需要导入,网上关于关联对象的文章很多,也分析的很透彻。而且发现很多优秀的三方库中都有用到这个方法,我就结合自己的理解总结一下,以便以后学习、使用。
就从一个常见的面试题开始 --- 可以在分类中给对象添加属性吗?虽然在分类中声明的属性在别的类中也能通过点语法获取到,但是会warning没有实现setter/getter方法,运行的时候也会crash。其实严格意义上来讲是不可以的,关联对象却可以在某种角度来帮我们“实现”这个功能。主要有三个方法:

  • void objc_setAssociatedObject(id object, void *key, id value, objc_AssociationPolicy) 此方法用于通过使用给定的键和策略为某对象设置关联对象值,传入 nil 则可以移除已有的关联对象
  • id objc_getAssociatedObject(id object, void *key)此方法用于通过使用给定的键获取关联对象值
  • void objc_removeAssociatedObjects(id object)移除指定对象所有关联对象,不常用

使用过程中,如果想要两个键匹配同一个值,则二者必须是完全相同的指针,即set和get中两个key必须完全相同,所以一般会使用静态全局变量作为键。在分类中添加属性时,还可以使用“属性”getter方法名做key,这样省得去定义变量。

- (NSString *)propertyName {
    return objc_getAssociatedObject(self, _cmd);
}

- (void)setAssociatedObject_assign:(NSString *)associatedObject_assign {
    objc_setAssociatedObject(self, @selector(propertyName), associatedObject_assign, OBJC_ASSOCIATION_ASSIGN);
}

其实到这里就可以调用对象点语法去获取设置属性值和获取属性值了,但其实这跟我们平时在类中定义的属性是有区别的。我们在类中声明的属性系统会帮我们生成下划线开头的同名的成员变量,实现setter/getter方法。分类中我们虽然手动实现了setter/getter方法,但是没有并没有生成成员变量,你在setter方法中也无法访问到该成员变量,具体验证可以看iOS分类不能添加属性原因的探索。

总的来说,在分类里使用@property声明属性,只是将该属性添加到该类的属性列表,并声明了setter和getter方法,但是没有生成相应的成员变量,也没有实现setter和getter方法。但我们手动实现了setter和getter方法也就可以在别的类中通过点语法给该属性赋值和取值,但是依然没有相应的成员变量,当然你可以自己生成一个成员变量,这也是前边“实现”要加引号的原因。

关联对象还可以用来传值,某些场景下可以免去一些繁琐的步骤,挺好用的工具。其实觉得得runtime里边的东西越来越有趣,我也会继续研究下去。理解的比较粗浅,有错误的地方欢迎大家指正。

参考:http://blog.leichunfeng.com/blog/2015/06/26/objective-c-associated-objects-implementation-principle/
http://www.jianshu.com/p/79479a09a8c0

你可能感兴趣的:(AssociatedObject封装AlertView)