利用Associative机制为UIAlertView添加一个Block属性,将AlertView与button的响应事件关联

1、详解:associative

objective-c有两个扩展机制:category和associative。我们可以通过category对已经存在的类添加和扩展方法,但是它有一个很大的局限性,那就是不能扩展属性。于是,就有了专门用来扩展属性的机制:associative。关联对象是Runtime中一个非常实用的特性。

associative的主要原理,就是在运行时把两个对象相互关联起来,使得其中的一个对象(A)作为另外一个对象(B)的一部分。即A对象通过给定的key连接到B对象上,作为B对象的一个属性。使用associative,我们可以不用修改类的定义而为其对象增加存储空间,同时也可以保证被关联的对象在关联对象的整个生命周期都是可用的。

associative机制提供了三个方法:

OBJC_EXPORT void objc_setAssociatedObject(id object, const void *key, id value, objc_AssociationPolicy policy)
    __OSX_AVAILABLE_STARTING(__MAC_10_6, __IPHONE_3_1);
OBJC_EXPORT id objc_getAssociatedObject(id object, const void *key)
    __OSX_AVAILABLE_STARTING(__MAC_10_6, __IPHONE_3_1);
OBJC_EXPORT void objc_removeAssociatedObjects(id object)
    __OSX_AVAILABLE_STARTING(__MAC_10_6, __IPHONE_3_1);

objc_getAssociatedObject 函数可以动态的为已经存在的类关联一个对象(添加属性),同时objc_setAssociatedObject可以获得添加的关联对象(获得属性),objc_removeAssociatedObjects 可以断开添加的关联对象(移除属性)。object参数作为待扩展的源对象实例(需要增加属性的对象),key作为该对象实例将要增加的属性的键,key是一个void指针(constvoid
*)。而value就是将要增加的属性的值(关联的对象),policy作为关联的策略。关联策略表明了相关的对象是通过赋值,保留引用还是复制的方式进行关联的,它指定了关联对象的内存管理策略。

关联策略的枚举包括:

enum {
    OBJC_ASSOCIATION_ASSIGN = 0,           /**< Specifies a weak reference to the associated object. */
    OBJC_ASSOCIATION_RETAIN_NONATOMIC = 1, /**< Specifies a strong reference to the associated object.
                                            *   The association is not made atomically. */
    OBJC_ASSOCIATION_COPY_NONATOMIC = 3,   /**< Specifies that the associated object is copied.
                                            *   The association is not made atomically. */
    OBJC_ASSOCIATION_RETAIN = 01401,       /**< Specifies a strong reference to the associated object.
                                            *   The association is made atomically. */
    OBJC_ASSOCIATION_COPY = 01403          /**< Specifies that the associated object is copied.
                                            *   The association is made atomically. */
};

特别需要注意的是,当宿主对象被释放时,会根据指定的内存管理策略来处理关联对象。如果指定的策略是assign,则宿主释放时,关联的对象不会被释放;而如果指定的是retain或者是copy,则宿主释放时,关联的对象会被释放。

2、使用:动态的为NSObject类添加一个myProperty属性

首先创建NSObject的一个category类别,MyObject类。导入头文件<objc/runtime.h>。在头文件为MyObject类添加一个属性myProperty。

//
//  NSObject+MyObject.h
//  JBAlertView
//
//  Created by jaybin on 15/6/16.
//  Copyright (c) 2015年 jaybin. All rights reserved.
//

#import <Foundation/Foundation.h>

@interface NSObject (MyObject)

@property (nonatomic, retain) NSString *myProperty;//为NSObject动态增加的属性

@end
//
//  NSObject+MyObject.m
//  JBAlertView
//
//  Created by jaybin on 15/6/16.
//  Copyright (c) 2015年 jaybin. All rights reserved.
//

#import "NSObject+MyObject.h"
#import <objc/runtime.h>

@implementation NSObject (MyObject)

/**
 *  获取关联对象(获取属性)
 */
-(NSString *)myProperty{
    return objc_getAssociatedObject(self, @selector(myProperty));
}

/**
 *  设置关联对象(添加属性)
 */
-(void)setMyProperty:(NSString *)myProperty{
    objc_setAssociatedObject(self, @selector(myProperty), myProperty, OBJC_ASSOCIATION_RETAIN_NONATOMIC);
}

@end

到这里,我们就通过Runtime特性为NSObject动态添加了一个myProperty属性。

3、为UIAlertView动态添加一个Block属性,将AlertView与button的点击操作关联,简单的区分处理一个视图有多个AlertView弹窗。

我们经常会遇到一个视图里有多个AlertView弹窗的情况。这个时候,如果按照常规的做法,我们就会定义一个全局的变量来去区分到底现在弹出或者点击的是哪个弹窗。然后在AlertView的代理方法

- (void)alertView:(UIAlertView *)alertView clickedButtonAtIndex:(NSInteger)buttonIndex;

里面使用 if else 多个语句块来区分多个弹窗点击后的操作,反正用起来很麻烦。如果我们将每个AlertView与它们的button点击操作关联起来,那么,就不需要再在代理方法里面用多个语句块来区分执行每个弹窗按钮的响应事件了,将AlertView的声明初始化和按钮的响应事件代码放在一起,代码的清晰度和维护也会更好一点。所以,我们可以考虑为UIAlertView动态添加一个Block属性,将AlertView与button的点击操作关联。

首先,我们创建UIAlertView的一个category类别,JBAlertView类。在头文件为JBAlertView类添加一个属性 alertViewBlock。

//
//  UIAlertView+JBAlertView.h
//  JBAlertView
//
//  Created by jaybin on 15/4/25.
//  Copyright (c) 2015年 jaybin. All rights reserved.
//

#import <UIKit/UIKit.h>

/**
 *  JBAlertViewBlock
 *  cancel: index = 0
 *  ok: index = 1
 */
typedef void (^JBAlertViewBlock)(NSInteger index);

@interface UIAlertView (JBAlertView)

@property (nonatomic, copy) JBAlertViewBlock alertViewBlock;//为UIAlertView动态增加的Block属性

@end
//
//  UIAlertView+JBAlertView.m
//  JBAlertView
//
//  Created by jaybin on 15/4/25.
//  Copyright (c) 2015年 jaybin. All rights reserved.
//

#import "UIAlertView+JBAlertView.h"
#import <objc/runtime.h>

@implementation UIAlertView (JBAlertView)

/**
 *  获取关联对象(获取属性)
 */
-(JBAlertViewBlock)alertViewBlock{
    return objc_getAssociatedObject(self, @selector(alertViewBlock));
}

/**
 *  设置关联对象(添加属性)
 */
-(void)setAlertViewBlock:(JBAlertViewBlock)block{
    objc_setAssociatedObject(self, @selector(alertViewBlock), block, OBJC_ASSOCIATION_RETAIN_NONATOMIC);
}

@end

到这里,我们就为UIAlertView动态添加了一个alertViewBlock属性。

最后我们的使用就会非常方便了。假设页面有两个AlertView弹窗,那么我们的代码就会比以前简洁清晰了。

#import "ViewController.h"
#import "UIAlertView+JBAlertView.h"

@interface ViewController ()

@end

@implementation ViewController

- (void)viewDidLoad {
    [super viewDidLoad];
    // Do any additional setup after loading the view, typically from a nib.

    UIButton *btn1 = [UIButton buttonWithType:UIButtonTypeCustom];
    [btn1 setBackgroundColor:[UIColor lightGrayColor]];
    [btn1 setTitle:@"show alert1" forState:UIControlStateNormal];
    btn1.frame = CGRectMake(150, 100, 100, 50);
    [btn1 addTarget:self action:@selector(showAlertView1) forControlEvents:UIControlEventTouchUpInside];
    [self.view addSubview:btn1];

    UIButton *btn2 = [UIButton buttonWithType:UIButtonTypeCustom];
    [btn2 setTitle:@"show alert2" forState:UIControlStateNormal];
    [btn2 setBackgroundColor:[UIColor lightGrayColor]];
    btn2.frame = CGRectMake(150, 200, 100, 50);
    [btn2 addTarget:self action:@selector(showAlertView2) forControlEvents:UIControlEventTouchUpInside];
    [self.view addSubview:btn2];

}

//初始化AlertView1   AlertView的初始化和按钮的响应事件代码放在一起
- (void)showAlertView1{
    //AlertView的初始化
    UIAlertView *alertView1 = [[UIAlertView alloc] initWithTitle:@"alertView1" message:nil delegate:self cancelButtonTitle:@"cancel" otherButtonTitles:@"ok", nil];
    //按钮的响应事件处理
    alertView1.alertViewBlock = ^(NSInteger index){
        if(1==index){
            NSLog(@"click OK_button");
        }
        else if (0==index){
            NSLog(@"click Cancel_button");
        }
    };

    [alertView1 show];

}

//初始化AlertView2   AlertView的初始化和按钮的响应事件代码放在一起
- (void)showAlertView2{
    //AlertView的初始化
    UIAlertView *alertView2 = [[UIAlertView alloc] initWithTitle:@"alertView2" message:nil delegate:self cancelButtonTitle:@"cancel" otherButtonTitles:@"ok", nil];
    //按钮的响应事件处理
    alertView2.alertViewBlock = ^(NSInteger index){
        if(1==index){
            NSLog(@"click OK_button");
        }
        else if (0==index){
            NSLog(@"click Cancel_button");
        }
    };

    [alertView2 show];
}

//实现UIAlertViewDelegate方法
- (void)alertView:(UIAlertView *)alertView clickedButtonAtIndex:(NSInteger)buttonIndex{
    alertView.alertViewBlock(buttonIndex);
}

最后记得要在AlertView的代理方法里回调为AlertView动态添加的属性Block,并且传入点击的buttonIndex。

项目的代码已放上GitHub。https://github.com/jaybinhe/JBAlertView

时间: 2024-11-15 02:56:12

利用Associative机制为UIAlertView添加一个Block属性,将AlertView与button的响应事件关联的相关文章

为对象添加一个释放时触发的block

有时我们需要在一个对象生命周期结束的时候触发一个操作,希望当该对象dealloc的时候调用一个外部指定的block,但又不希望直接hook dealloc方法,这样侵入性太强了.下面贴一段非常简单的实现方式,通过一个category给外部暴露一个block注入的接口,内部将该block封装到一个寄生对象中(Parasite),该寄生对象在dealoc的时候触发block调用,所有的寄生对象通过runtime的AssociatedObject机制与宿主共存亡,从而达到监控宿主生命周期的目的. 注意

Associative机制使用场景

1.    概念 objective-c有两个扩展机制:category和associative.我们可以通过category来扩展方法,但是它有个很大的局限性,不能扩展属性.于是,就有了专门用来扩展属性的机制:associative. 2.    使用方法 在iOS开发过程中,category比较常见,而associative就用的比较少.associative的主要原理,就是把两个对象相互关联起来,使得其中的一个对象作为另外一个对象的一部分. 使用associative,我们可以不用修改类的

高并发情况利用锁机制处理缓存未命中

关于缓存的使用,个人经验还是比较欠缺,对于缓存在应用系统中的使用也只是前几个月在公司实习的时候,简单的使用过,且使用的都是人家把框架搭建好的,至于缓存在并发情况下会产生的一系列问题都已经被框架处理好了,我所做的只是set和get,至于使用时缓存在并发情况下到底会出现什么样的问题,该如何去解决和避免这些问题,没有去深究. 秉着"学而时习之"的态度(T_T自己太懒,厚着脸皮),这两天在鼓捣redis,至于redis的基本使用还是挺简单的,今天要说的是我在这个过程中看到网上博客一直提的关于缓

为UIAlertView添加block支持

系统自带的UIAlertView只能支持delegate方式. 如果你只有一个UIAlertView这种方式可能无关紧要. 但如果你有二个或多个UIAlertView, 你需要在委托方法中进行判断是哪个UIAlertView实例的产生的委托, 接着又要判断是响应哪个button. 如果你曾经这样做过, 想想这是多杂的代码. Objective-C是支持块代码的, 如果对UIAlertView添加块支持, 那岂不是一个美事. 这里推荐一个开源的实现: https://github.com/jiv

利用消息机制实现VC与Delphi之间的通讯(发送自定义消息)

摘要: 本文介绍了使用Windows消息机制实现由不同语言编制的程序之间的相互通讯.联系,并以当前较为流行的两种语言Microsoft Visual C++ 6.0和Borland delphi 5.0为对象,用这两种语言各编制一应用程序,并能很好的通过消息进行交互. 关键字:vc++.delphi.消息 一. 引言 编制较大型的程序往往需要将一个项目分割成若干个模块,由若干个开发小组共同完成.笔者曾参加过几个大型项目的研发工作,根据需要往往要将项目分为解为几大部分,分到三.四家科研单位共同完成

利用反射机制,获取类的字段、方法、并实现简单调用

这篇文章是为之后要介绍Android的ICO框架做预备的,所以,如果想最近学习Android的ICO框架的同学,可以稍微看一下. 首先,简单介绍一下Java里面的反射. JAVA反射机制是在运行状态中,对于任意一个类,都能够知道这个类的所有属性和方法:对于任意一个对象,都能够调用它的任意一个方法和属性:这种动态获取的信息以及动态调用对象的方法的功能称为java语言的反射机制. 不知道这样的一段解释,你能否看懂.如果更简单的说,反射就是能够根据你给出类名实例化出一个实实在在的对象.所以,对象的实例

java中利用反射机制绕开编译器对泛型的类型限制

首先看下面这个例子 public static void main(String[] args) { ArrayList<Integer> al1 = new ArrayList<Integer>(); al1.add(1); ArrayList<String> al2 = new ArrayList<String>(); al2.add("hello"); //int型链表和string型链表,结果为true System.out.pr

浅谈利用同步机制解决Java中的线程安全问题

我们知道大多数程序都不会是单线程程序,单线程程序的功能非常有限,我们假设一下所有的程序都是单线程程序,那么会带来怎样的结果呢?假如淘宝是单线程程序,一直都只能一个一个用户去访问,你要在网上买东西还得等着前面千百万人挑选购买,最后心仪的商品下架或者售空......假如饿了吗是单线程程序,那么一个用户得等前面全国千万个用户点完之后才能进行点餐,那饿了吗就该倒闭了不是吗?以上两个简单的例子,就说明一个程序能进行多线程并发访问的重要性,今天就让我们去了解一下Java中多线程并发访问这个方向吧. **第一

向PE文件中添加一个Section

背景 之前说过直接向类HelloWorld.exe的可执行文件添加一个MessageBox弹窗, 但有时候, 需要添加的内容太多了, 因为数据与代码一起插入, 以至于可执行文件本身没有足够的空闲空间存放这些内容时, 就需要添加一个Section. 确认节区头后面还有空间 用工具查看一下最后一个节区头后面是否还有多余的空间, 一般情况都会有的. 但若没有的话, 就要移动节区头后面的文件内容, 这个比较复杂, 在这里不说. 一般会结合PE View 和 WinHex 这两个工具, 如之前的Hello