個人學(xué)習(xí)心得
關(guān)聯(lián)是指把兩個對象互相關(guān)聯(lián)起來,使得其中的一個對象作為另外一個對象的一部分.我們可以在不修改類的定義的情況下為其對象增加存儲空間.
關(guān)聯(lián)對象類似于成員變量,是在運(yùn)行時(shí)添加的.當(dāng)我們給一個分類添加新的成員變量時(shí),編譯器會報(bào)錯,這個問題可以通過添加全局變量來解決,但這些都不是Ivar,因?yàn)樗麄儾粫B接到一個單獨(dú)的實(shí)例.關(guān)聯(lián)就可以很方便的解決這個問題
- 運(yùn)行時(shí)中關(guān)聯(lián)的函數(shù)有三個
// 用一個給定的key給一個指定對象設(shè)置一個關(guān)聯(lián)的值和關(guān)聯(lián)策略
void objc_setAssociatedObject(id object, constvoid *key, id value, objc_AssociationPolicy policy)
object:源對象,即被關(guān)聯(lián)的對象
key:關(guān)聯(lián)的關(guān)鍵字,是一個void類型的指針.這個關(guān)鍵字建議是唯一的,如果使用同一個key來關(guān)聯(lián)另外一個對象時(shí),之前關(guān)聯(lián)的對象會被自動釋放.
value:關(guān)聯(lián)的對象.當(dāng)value為nil的時(shí)候,可以移除相應(yīng)的關(guān)聯(lián)
policy:關(guān)聯(lián)策略,是個枚舉值.用來表名關(guān)聯(lián)的對象是copy, retain還是assign方式進(jìn)行的,copy和retain有原子和非原子.和聲明屬性很類似.
// 從一個指定對象的給定key獲取相關(guān)聯(lián)的對象
id objc_getAssociatedObject(id object, const void*key)
object:被關(guān)聯(lián)的對象
key:關(guān)聯(lián)的關(guān)鍵字
// 斷開所有的關(guān)聯(lián) 通常情況下不建議使用這個函數(shù)违寿,因?yàn)樗麜嚅_所有關(guān)聯(lián)胳施。只有在需要把對象恢復(fù)到“原始狀態(tài)”的時(shí)候才會使用這個函數(shù)
void objc_removeAssociatedObjects(id object)
object:被關(guān)聯(lián)的對象
- 具體的使用 demo(myruntime)
- 1.給一個分類添加屬性(關(guān)聯(lián)成員變量)
#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 - 設(shè)置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.動態(tài)的添加一個手勢到UIView上
#import <UIKit/UIKit.h>
@interface UIView (Block)
// 添加點(diǎn)擊事件,用block回調(diào)
- (void)tapActionWithBlock:(void(^)(void))block;
@end
#import "UIView+Block.h"
#import <objc/runtime.h>
@interface UIView ()
@end
// 關(guān)鍵字
static char blockKey;
static char tapKey;
@implementation UIView (Block)
#pragma mark - 添加點(diǎn)擊事件
- (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);
}
// 點(diǎn)擊事件回調(diào)
- (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)
// 定義回調(diào)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)
// 設(shè)置關(guān)聯(lián)
- (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語言的可變參數(shù)
va_list:用來定義一個指向參數(shù)的指針
va_start(ap, param):初始化va_list定義的變量.apap是va_list定義的變量,param是參數(shù)數(shù)組.初始化后,VA_LIST指針指向第二個參數(shù)
va_arg(ap, type):返回可變參數(shù),ap是va_list定義的變量,type是要返回參數(shù)的類型
va_end(ap):結(jié)束可變參數(shù)的獲取,ap是va_list定義的變量,
獲取可變參數(shù)時(shí)用的是while循環(huán),所以數(shù)組中最后一個參數(shù)需要傳入nil作為結(jié)束遍歷的標(biāo)識.
- 4.給UIActionSheet添加一個類方法
#import <UIKit/UIKit.h>
typedef void(^ActionSheetCallBackBlock)(NSInteger buttonIndex);
@interface UIActionSheet (runtime)
// 定義回調(diào)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的點(diǎn)擊事件也可以做一個封裝,在開發(fā)中可以減少很多重復(fù)的代碼
#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