个人学习心得
关联是指把两个对象互相关联起来,使得其中的一个对象作为另外一个对象的一部分.我们可以在不修改类的定义的情况下为其对象增加存储空间.
关联对象类似于成员变量,是在运行时添加的.当我们给一个分类添加新的成员变量时,编译器会报错,这个问题可以通过添加全局变量来解决,但这些都不是Ivar,因为他们不会连接到一个单独的实例.关联就可以很方便的解决这个问题
- 运行时中关联的函数有三个
// 用一个给定的key给一个指定对象设置一个关联的值和关联策略
void objc_setAssociatedObject(id object, constvoid *key, id value, objc_AssociationPolicy policy)
object:源对象,即被关联的对象
key:关联的关键字,是一个void类型的指针.这个关键字建议是唯一的,如果使用同一个key来关联另外一个对象时,之前关联的对象会被自动释放.
value:关联的对象.当value为nil的时候,可以移除相应的关联
policy:关联策略,是个枚举值.用来表名关联的对象是copy, retain还是assign方式进行的,copy和retain有原子和非原子.和声明属性很类似.
// 从一个指定对象的给定key获取相关联的对象
id objc_getAssociatedObject(id object, const void*key)
object:被关联的对象
key:关联的关键字
// 断开所有的关联 通常情况下不建议使用这个函数,因为他会断开所有关联。只有在需要把对象恢复到“原始状态”的时候才会使用这个函数
void objc_removeAssociatedObjects(id object)
object:被关联的对象
- 具体的使用 demo(myruntime)
- 1.给一个分类添加属性(关联成员变量)
#import <Foundation/Foundation.h>
@interface NSObject (Runtime)
/** 添加属性 */
@property (nonatomic, copy) NSString*runtimeName;
@property (nonatomic, assign) float weight;
@end
#import "NSObject+Runtime.h"
#import <objc/runtime.h>
@implementation NSObject (Runtime)
static char *myKey;
#pragma mark - 设置setter和getter方法
- (void)setRuntimeName:(NSString*)runtimeName{
objc_setAssociatedObject(self, myKey, runtimeName, OBJC_ASSOCIATION_COPY);
}
- (NSString *)runtimeName{
return objc_getAssociatedObject(self,myKey);
}
- (void)setWeight:(float)weight{
objc_setAssociatedObject(self,@selector(weight), @(weight), OBJC_ASSOCIATION_ASSIGN);
}
- (float)weight{
return [objc_getAssociatedObject(self,_cmd) floatValue];
}
@end
- 2.动态的添加一个手势到UIView上
#import <UIKit/UIKit.h>
@interface UIView (Block)
// 添加点击事件,用block回调
- (void)tapActionWithBlock:(void(^)(void))block;
@end
#import "UIView+Block.h"
#import <objc/runtime.h>
@interface UIView ()
@end
// 关键字
static char blockKey;
static char tapKey;
@implementation UIView (Block)
#pragma mark - 添加点击事件
- (void)tapActionWithBlock:(void(^)(void))block{
UITapGestureRecognizer *tap =objc_getAssociatedObject(self, &tapKey);
if (!tap) {
tap = [[UITapGestureRecognizer alloc]initWithTarget:selfaction:@selector(handleActionForTap:)];
[self addGestureRecognizer:tap];
objc_setAssociatedObject(self, &tapKey, tap, OBJC_ASSOCIATION_RETAIN);
}
objc_setAssociatedObject(self, &blockKey, block, OBJC_ASSOCIATION_COPY);
}
// 点击事件回调
- (void)handleActionForTap:(UITapGestureRecognizer *)tap{
if (tap.state ==UIGestureRecognizerStateRecognized) {
void(^block)(void) =objc_getAssociatedObject(self, &blockKey);
if (block) {
block();
}
}
}
@end
- 3.给UIAlertView添加一个类方法
#import <UIKit/UIKit.h>
typedef void(^AlertViewCallBackBlock)(NSInteger buttonIndex);
@interface UIAlertView (runtime)
// 定义回调block
@property (nonatomic, copy)AlertViewCallBackBlock callBackBlock;
+ (void)alertViewCallBack:(AlertViewCallBackBlock)callBack WithTitle:(NSString *)title message:(NSString *)message cancelButtonTitle:(NSString *)cancel otherButtonTitles:(NSString *)otherTitle, ...;
@end
#import "UIAlertView+runtime.h"
#import <objc/runtime.h>
@implementation UIAlertView (runtime)
// 设置关联
- (void)setCallBackBlock:(AlertViewCallBackBlock)callBackBlock{
objc_setAssociatedObject(self,@selector(callBackBlock), callBackBlock,OBJC_ASSOCIATION_COPY_NONATOMIC);
}
- (AlertViewCallBackBlock)callBackBlock{
return objc_getAssociatedObject(self,_cmd);
}
+ (void)alertViewCallBack:(AlertViewCallBackBlock)callBack WithTitle:(NSString *)title message:(NSString *)message cancelButtonTitle:(NSString *)cancel otherButtonTitles:(NSString *)otherTitle, ...{
UIAlertView *alertView = [[UIAlertViewalloc] initWithTitle:title message:messagedelegate:nil cancelButtonTitle:cancelotherButtonTitles:otherTitle, nil];
NSString *other = nil;
va_list args;
if (otherTitle) {
va_start(args, otherTitle);
while ((other = va_arg(args, NSString*))) {
[alertViewaddButtonWithTitle:other];
}
va_end(args);
}
alertView.delegate = alertView;
[alertView show];
alertView.callBackBlock = callBack;
}
- (void)alertView:(UIAlertView *)alertView clickedButtonAtIndex:(NSInteger)buttonIndex{
if (self.callBackBlock) {
self.callBackBlock(buttonIndex);
}
}
@end
// 说明:
va_list, va_start, va_arg, va_end用于C语言的可变参数
va_list:用来定义一个指向参数的指针
va_start(ap, param):初始化va_list定义的变量.apap是va_list定义的变量,param是参数数组.初始化后,VA_LIST指针指向第二个参数
va_arg(ap, type):返回可变参数,ap是va_list定义的变量,type是要返回参数的类型
va_end(ap):结束可变参数的获取,ap是va_list定义的变量,
获取可变参数时用的是while循环,所以数组中最后一个参数需要传入nil作为结束遍历的标识.
- 4.给UIActionSheet添加一个类方法
#import <UIKit/UIKit.h>
typedef void(^ActionSheetCallBackBlock)(NSInteger buttonIndex);
@interface UIActionSheet (runtime)
// 定义回调block
@property (nonatomic, copy)ActionSheetCallBackBlock callBackBlock;
+ (void)actionSheetCallBlock:(ActionSheetCallBackBlock)callBackBlock withTitle:(NSString *)title message:(NSString*)message cancelButtonTitle:(NSString*)cancelButtonTitle otherButtonTitles:(NSString*)otherTitles, ...;
@end
#import "UIActionSheet+runtime.h"
#import <objc/runtime.h>
@implementation UIActionSheet (runtime)
- (void)setCallBackBlock:(ActionSheetCallBackBlock)callBackBlock{
objc_setAssociatedObject(self,@selector(callBackBlock), callBackBlock,OBJC_ASSOCIATION_COPY_NONATOMIC);
}
- (ActionSheetCallBackBlock)callBackBlock{
return objc_getAssociatedObject(self, _cmd);
}
+ (void)actionSheetCallBlock:(ActionSheetCallBackBlock)callBackBlock withTitle:(NSString *)title message:(NSString*)message cancelButtonTitle:(NSString*)cancelButtonTitle otherButtonTitles:(NSString*)otherTitles, ...{
UIActionSheet *actionSheet = [[UIActionSheetalloc] initWithTitle:title delegate:nilcancelButtonTitle:cancelButtonTitledestructiveButtonTitle:nilotherButtonTitles:otherTitles, nil];
NSString *other = nil;
va_list args;
if (otherTitles) {
va_start(args, otherTitles);
while ((other = va_arg(args, NSString*))) {
[actionSheetaddButtonWithTitle:other];
}
va_end(args);
}
actionSheet.callBackBlock = callBackBlock;
actionSheet.delegate = actionSheet;
[actionSheet showInView:[UIApplicationsharedApplication].keyWindow];
}
- (void)actionSheet:(UIActionSheet *)actionSheet clickedButtonAtIndex:(NSInteger)buttonIndex{
if (self.callBackBlock) {
self.callBackBlock(buttonIndex);
}
}
@end
- 5.对UIButton的点击事件也可以做一个封装,在开发中可以减少很多重复的代码
#import <UIKit/UIKit.h>
typedef void(^TouchUpInsideBlock)();
@interface UIButton (Runtime)
- (void)touchUpInsideBlock:(TouchUpInsideBlock)touchBlock;
@end
#import "UIButton+Runtime.h"
#import <objc/runtime.h>
const char *ggTouchBlock;
@implementation UIButton (Runtime)
- (void)touchUpInsideBlock:(TouchUpInsideBlock)touchBlock{
if (touchBlock) {
objc_setAssociatedObject(self,ggTouchBlock, touchBlock,OBJC_ASSOCIATION_COPY_NONATOMIC);
}
[self addTarget:selfaction:@selector(clickButton)forControlEvents:UIControlEventTouchUpInside];
}
- (void)clickButton{
TouchUpInsideBlock touchBlock =objc_getAssociatedObject(self, ggTouchBlock);
if (touchBlock) {
touchBlock();
}
}
@end