GitHub:https://github.com/quzhongyeluo/Runtime
1.給Category分類添加屬性
objc_setAssociatedObject來把一個(gè)對(duì)象與另外一個(gè)對(duì)象進(jìn)行關(guān)聯(lián)。該函數(shù)需要四個(gè)參數(shù):源對(duì)象,關(guān)鍵字,關(guān)聯(lián)的對(duì)象和一個(gè)關(guān)聯(lián)策略。
OBJC_EXPORT void objc_setAssociatedObject(id object, const void *key, id value, objc_AssociationPolicy policy)
關(guān)鍵字是一個(gè)void類型的指針。每一個(gè)關(guān)聯(lián)的關(guān)鍵字必須是唯一的。通常都是會(huì)采用靜態(tài)變量來作為關(guān)鍵字。
關(guān)聯(lián)策略表明了相關(guān)的對(duì)象是通過賦值,保留引用還是復(fù)制的方式進(jìn)行關(guān)聯(lián)的;還有這種關(guān)聯(lián)是原子的還是非原子的。
這里的關(guān)聯(lián)策略和聲明屬性時(shí)的很類似。這種關(guān)聯(lián)策略是通過使用預(yù)先定義好的常量來表示的。
eg:1、為UIImageView添加點(diǎn)擊事件
UIImageView+QZ_ClickBlock.h
typedef void(^ClickBlcok)();
@interface UIImageView (ClickBlock)
@property (nonatomic, copy ) ClickBlcok clickBlock;
@end
UIImageView+QZ_ClickBlock.m
@interface UIImageView ()
@property (nonatomic,strong) UITapGestureRecognizer *tag;
@end
@implementation UIImageView (ClickBlock)
static const void *qz_clickKey = @"qz_clickKey";
- (void)setClickBlock:(ClickBlcok)clickBlock{
objc_setAssociatedObject(self, qz_clickKey, clickBlock, OBJC_ASSOCIATION_COPY_NONATOMIC);
self.userInteractionEnabled = true;
if (self.tag) {
[self removeGestureRecognizer:self.tag];
}
if (clickBlock) {
UITapGestureRecognizer *tag = [[UITapGestureRecognizer alloc] initWithTarget:self action:@selector(clickAction)];
[self addGestureRecognizer:tag];
}
}
- (ClickBlcok)clickBlock{
return objc_getAssociatedObject(self, qz_clickKey);
}
- (void)clickAction{
if (self.clickBlock) {
self.clickBlock();
}
}
@end
eg:2、解決重復(fù)點(diǎn)擊的問題 :代碼注入 , 把系統(tǒng)方法替換成自己的方法
hook思想
#import <UIKit/UIKit.h>
/**
解決重復(fù)點(diǎn)擊的問題 :代碼注入 , 把系統(tǒng)方法替換成自己的方法
hook:思想
*/
@interface UIButton (QZ_RepeatClick)
/**
重復(fù)點(diǎn)擊的間隔
*/
@property (nonatomic, assign) NSTimeInterval interval;
@end
#import "UIButton+QZ_RepeatClick.h"
#import <objc/message.h>
@interface UIButton ()
/**
上次點(diǎn)擊時(shí)間
*/
@property (nonatomic, assign) NSTimeInterval preInterval;
@end
@implementation UIButton (QZ_RepeatClick)
static const char *UIControl_Interval = "UIControl_Interval";
static const char *UIControl_PreInterval = "UIControl_PreInterval";
- (NSTimeInterval)interval {
return [objc_getAssociatedObject(self, UIControl_Interval) doubleValue];
}
- (void)setInterval:(NSTimeInterval)interval{
objc_setAssociatedObject(self, UIControl_Interval, @(interval), OBJC_ASSOCIATION_RETAIN_NONATOMIC);
}
- (NSTimeInterval)preInterval {
return [objc_getAssociatedObject(self, UIControl_PreInterval) doubleValue];
}
- (void)setPreInterval:(NSTimeInterval)preInterval{
objc_setAssociatedObject(self, UIControl_PreInterval, @(preInterval), OBJC_ASSOCIATION_RETAIN_NONATOMIC);
}
// 在load時(shí)執(zhí)行hook : 替換系統(tǒng)方法
+ (void)load {
Method before = class_getInstanceMethod(self, @selector(sendAction:to:forEvent:));
Method after = class_getInstanceMethod(self, @selector(qz_sendAction:to:forEvent:));
method_exchangeImplementations(before, after);
}
- (void)qz_sendAction:(SEL)action to:(id)target forEvent:(UIEvent *)event {
if ([NSDate date].timeIntervalSince1970 - self.preInterval < self.interval) {
return;
}
if (self.interval > 0) {
self.preInterval = [NSDate date].timeIntervalSince1970;
}
[self qz_sendAction:action to:target forEvent:event];
}
@end
2.給Category分類動(dòng)態(tài)添加方法
eg:
為UIButton添加設(shè)置 在不同狀態(tài)下背景色
存在問題:無法根據(jù)狀態(tài)改變文字 (不知道如何解決 - 麻煩知道的告知下)
#import <UIKit/UIKit.h>
/**
為UIButton添加設(shè)置 在不同狀態(tài)下背景色
缺點(diǎn):無法根據(jù)狀態(tài)改變文字 (不知道如何解決 - 麻煩知道的告知下)
*/
@interface UIButton (QZ_BackgroundColor)
- (void)setBackgroundColor:(UIColor *)backgroundColor forState:(UIControlState)state;
@end
#import "UIButton+QZ_BackgroundColor.h"
#import <objc/runtime.h>
@interface UIButton ()
@property (nonatomic, strong) NSMutableDictionary *backgroundColorDict;
@end
@implementation UIButton (QZ_BackgroundColor)
static const void *qz_key_backgroundColor = @"qz_key_backgroundColor";
static NSString *qz_forUIControlStateNormal = @"qz_forUIControlStateNormal";
static NSString *qz_forUIControlStateHighlighted = @"qz_forUIControlStateHighlighted";
static NSString *qz_forUIControlStateDisabled = @"qz_forUIControlStateDisabled";
static NSString *qz_forUIControlStateSelected = @"qz_forUIControlStateSelected";
- (void)setBackgroundColor:(UIColor *)backgroundColor forState:(UIControlState)state{
if (!self.backgroundColorDict) {
self.backgroundColorDict = [NSMutableDictionary dictionary];
}
[self.backgroundColorDict setObject:backgroundColor forKey:[self qz_forUIControlState:state]];
}
- (NSString *)qz_forUIControlState:(UIControlState)state {
NSString *string;
switch (state) {
case UIControlStateNormal:
string = qz_forUIControlStateNormal;
break;
case UIControlStateHighlighted:
string = qz_forUIControlStateHighlighted;
break;
case UIControlStateDisabled:
string = qz_forUIControlStateDisabled;
break;
case UIControlStateSelected:
string = qz_forUIControlStateSelected;
break;
default:
string = qz_forUIControlStateNormal;
break;
}
return string;
}
- (void)setBackgroundColorDict:(NSMutableDictionary *)backgroundColorDict{
objc_setAssociatedObject(self, qz_key_backgroundColor, backgroundColorDict, OBJC_ASSOCIATION_RETAIN_NONATOMIC);
}
- (NSMutableDictionary *)backgroundColorDict{
return objc_getAssociatedObject(self, qz_key_backgroundColor);
}
- (void)setHighlighted:(BOOL)highlighted{
[super setHighlighted:highlighted];
if (highlighted) {
self.backgroundColor = (UIColor *)[self.backgroundColorDict objectForKey:qz_forUIControlStateHighlighted];
}else{
self.backgroundColor = (UIColor *)[self.backgroundColorDict objectForKey:qz_forUIControlStateNormal];
}
}
- (void)setEnabled:(BOOL)enabled{
[super setEnabled:enabled];
if (enabled) {
self.backgroundColor = (UIColor *)[self.backgroundColorDict objectForKey:qz_forUIControlStateNormal];
}else{
self.backgroundColor = (UIColor *)[self.backgroundColorDict objectForKey:qz_forUIControlStateDisabled];
}
}
- (void)setSelected:(BOOL)selected{
[super setSelected:selected];
if (selected) {
self.backgroundColor = (UIColor *)[self.backgroundColorDict objectForKey:qz_forUIControlStateSelected];
}else{
self.backgroundColor = (UIColor *)[self.backgroundColorDict objectForKey:qz_forUIControlStateNormal];
}
}
@end
3.字典與模型互轉(zhuǎn)
#import <Foundation/Foundation.h>
@interface NSObject (QZ_KeyValues)
- (id)initWithQZ_Dictionary:(NSDictionary *)dictionary;
- (NSDictionary *)qz_ObjectToDictionary;
@end
#import "NSObject+QZ_KeyValues.h"
#import <objc/message.h>
@implementation NSObject (QZ_KeyValues)
/**
字典轉(zhuǎn)模型
@param dictionary 字典
@return 模型
*/
-(id)initWithQZ_Dictionary:(NSDictionary *)dictionary{
id objc = [self init];
for (NSString *key in dictionary.allKeys) {
id value = dictionary[key];
// 判斷當(dāng)前屬性是否為model
objc_property_t property = class_getProperty([self class], key.UTF8String);
unsigned int outCount = 0;
objc_property_attribute_t *attributeList = property_copyAttributeList(property, &outCount);
objc_property_attribute_t attribute = attributeList[0];
NSString *typeString = [NSString stringWithUTF8String:attribute.value];
NSString *className = NSStringFromClass([self class]);
if ([typeString isEqualToString:className]) {
value = [self initWithQZ_Dictionary:value];
}
// 當(dāng)前屬性是否為model
NSString *methodName = [NSString stringWithFormat:@"set%@%@:",[key substringToIndex:1].uppercaseString,[key substringFromIndex:1]];
SEL setter = sel_registerName(methodName.UTF8String);
if ([objc respondsToSelector:setter]) {
((void (*) (id,SEL,id)) objc_msgSend) (objc,setter,value);
}
free(attributeList);
}
return objc;
}
/**
模型轉(zhuǎn)字典
@return 字典
*/
- (NSDictionary *)qz_ObjectToDictionary{
unsigned int outCount = 0;
objc_property_t *propertyList = class_copyPropertyList([self class], &outCount);
NSMutableDictionary *dict = [NSMutableDictionary dictionary];
for (int i = 0; i < outCount; i ++) {
objc_property_t property = propertyList[i];
//生成getter方法,并用objc_msgSend調(diào)用
const char *propertyName = property_getName(property);
SEL getter = sel_registerName(propertyName);
if ([self respondsToSelector:getter]) {
id value = ((id (*) (id,SEL)) objc_msgSend) (self,getter);
// 判斷當(dāng)前屬性是不是Model
if ([value isKindOfClass:[self class]] && value) {
value = [value qz_ObjectToDictionary];
}
if (value) {
NSString *key = [NSString stringWithUTF8String:propertyName];
[dict setObject:value forKey:key];
}
}
}
free(propertyList);
return dict;
}
@end
4.自動(dòng)歸檔解檔
#import <Foundation/Foundation.h>
@interface User : NSObject <NSCoding>
@property (nonatomic,copy) NSString *name;
@property (nonatomic,copy) NSNumber *age;
@property (nonatomic,copy) NSNumber *phone;
@property (nonatomic,copy) NSString *userid;
@end
#import "User.h"
#import <objc/message.h>
@implementation User
- (instancetype)initWithCoder:(NSCoder *)aDecoder{
if (self = [super init]) {
unsigned int outCount = 0;
// 獲取所有成員變量
Ivar *vars = class_copyIvarList([self class], &outCount);
for (int i = 0; i < outCount; i++) {
Ivar var = vars[i];
// 獲取成員變量名稱
const char *name = ivar_getName(var);
NSString *key = [NSString stringWithUTF8String:name];
// 注意kvc的特性是,如果能找到key這個(gè)屬性的setter方法,則調(diào)用setter方法
// 如果找不到setter方法,則查找成員變量key或者成員變量_key,并且為其賦值
// 所以這里不需要再另外處理成員變量名稱的“_”前綴
id value = [aDecoder decodeObjectForKey:key];
[self setValue:value forKey:key];
}
}
return self;
}
- (void)encodeWithCoder:(NSCoder *)aCoder{
unsigned int outCount = 0;
Ivar *vars = class_copyIvarList([self class], &outCount);
for (int i = 0; i < outCount; i++) {
Ivar var = vars[i];
const char *name = ivar_getName(var);
NSString *key = [NSString stringWithUTF8String:name];
id value = [self valueForKey:key];
[aCoder encodeObject:value forKey:key];
}
}
@end