iOS開發(fā) runtime應(yīng)用

1.runtime的作用

  • 字典轉(zhuǎn)模型
  • 動(dòng)態(tài)修改成員變量
  • 方法交換
  • 給分類添加屬性

2.字典轉(zhuǎn)模型

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

@implementation NSObject (Json)

+ (instancetype)xwx_initWithDictionaryForModel:(NSDictionary *)dic{
    
    id myObj = [[self alloc] init];
    unsigned int outCount;
    //獲取類中的所有成員屬性
    objc_property_t *arrPropertys = class_copyPropertyList([self class], &outCount);
    
    for (NSInteger i = 0; i < outCount; i ++) {
        
        //獲取屬性名字符串
        objc_property_t property = arrPropertys[i];
        //model中的屬性名
        NSString *propertyName = [NSString stringWithUTF8String:property_getName(property)];
        id propertyValue = dic[propertyName];
        
        //處理服務(wù)器字段與本地model字段不匹配問題
        if ([propertyName isEqualToString:@"age3"]) {//處理本地與網(wǎng)絡(luò)字符不匹配的問題
            propertyValue = dic[@"age"];
        }
    
        if (propertyValue != nil) {
            [myObj setValue:propertyValue forKey:propertyName];
        }
    }
    //注意在runtime獲取屬性的時(shí)候淑蔚,并不是ARC Objective-C的對(duì)象所有需要釋放
    free(arrPropertys);
    return myObj;
}
@end

3.動(dòng)態(tài)修改成員變量

#import "UITextField+PlaceHolder.h"
#import <objc/runtime.h>

@implementation UITextField (PlaceHolder)

-(void)changePlaceHolderTextColor:(UIColor *)color{

//這塊就是為了打印出所有的成員變量,獲取成員變量的名
    unsigned int count;
    //獲取UITextField中的所有成員變量
    Ivar *ivars = class_copyIvarList([UITextField class], &count);
    for (int i = 0; i < count; i++) {
    // 取出i位置的成員變量
    Ivar ivar = ivars[i];
   // NSLog(@"成員變量%s", ivar_getName(ivar));
    }
    free(ivars);
     
    // iOS 13 通過 KVC 方式修改私有屬性,有 Crash 風(fēng)險(xiǎn),謹(jǐn)慎使用!并不是所有KVC都會(huì)Crash,要嘗試!
    if ([[UIDevice currentDevice].systemVersion floatValue] > 13.0) {
        //獲取一個(gè)成員變量,根據(jù)名稱獲取
        Ivar ivar =  class_getInstanceVariable([UITextField class], "_placeholderLabel");
        //object_getIvar獲取成員變量的值
        UILabel *placeholderLabel = object_getIvar(self, ivar);
        placeholderLabel.textColor = color;

    }else{
        //kvc賦值,iOS13之前用kvc賦值就可以哦
        [self setValue:color forKeyPath:@"_placeholderLabel.textColor"];
  }
}
@end
  • 需求二:給UITextView添加PlaceHolder
#import "UITextView+PlaceHolder.h"
#import <objc/runtime.h>

@implementation UITextView (PlaceHolder)



-(void)SetPlaceHolderTextColor:(UIColor *)color fontSize:(CGFloat)font textContent:(NSString *)text{
    
    
    UILabel *placeHolderLabel = [[UILabel alloc] initWithFrame:CGRectMake(0, 0, 100, 30)];
       placeHolderLabel.text = text;
       placeHolderLabel.numberOfLines = 0;
       placeHolderLabel.textColor = color;
    placeHolderLabel.font = [UIFont systemFontOfSize:font];
       [placeHolderLabel sizeToFit];
       
       if (self.text.length == 0) {
           [self addSubview:placeHolderLabel];//這句很重要不要忘了
       }
       
    [self setValue:placeHolderLabel forKey:@"_placeholderLabel"];

}

@end

值得注意的是哈误,在iOS13之后使用KVC給成員變量賦值有可能崩潰,要試

4. 方法交換

  • 需求1 NSMutableArray *array [array insertObject:nil atIndex:0]防止崩潰

參考鏈接 http://www.reibang.com/p/e0d46032b27f

//
//  NSMutableArray+Extension.m
//  runtime
//
//  Created by eport on 2020/12/13.


#import "NSMutableArray+Extension.h"
#import <objc/runtime.h>

//方法交換
@implementation NSMutableArray (Extension)


//類方法躏嚎,只在runtime調(diào)用一次,在APP運(yùn)行時(shí)調(diào)用.+ (void)load. 方法只要加入了工程種蜜自,進(jìn)行了編譯,且.m中實(shí)現(xiàn)了這個(gè)方法卢佣,都會(huì)調(diào)用一次重荠,值得注意的時(shí)沒實(shí)現(xiàn)的子類是不會(huì)調(diào)用的,就算父類實(shí)現(xiàn)了也不行
+ (void)load
{
    static dispatch_once_t onceToken;
    dispatch_once(&onceToken, ^{
        // 類簇:NSString珠漂、NSArray晚缩、NSDictionary,真實(shí)類型是其他類型
        Class cls = NSClassFromString(@"__NSArrayM");
        Method method1 = class_getInstanceMethod(cls, @selector(insertObject:atIndex:));
        Method method2 = class_getInstanceMethod(cls, @selector(jf_insertObject:atIndex:));
        method_exchangeImplementations(method1, method2);
    });
}

- (void)jf_insertObject:(id)anObject atIndex:(NSUInteger)index{

    if (anObject == nil) return;//如果是空對(duì)象媳危,就不添加荞彼,防止奔潰

    //攔截之后,再調(diào)用系統(tǒng)的實(shí)現(xiàn)待笑,由于不知道底層實(shí)現(xiàn)邏輯鸣皂,自己實(shí)現(xiàn)系統(tǒng)方法可能會(huì)出各種意想不到的錯(cuò)
    [self jf_insertObject:anObject atIndex:index];//這兒看似死循環(huán),細(xì)理邏輯暮蹂,方法實(shí)現(xiàn)已經(jīng)交換過了的
}

@end

  • 需求二 打印控制器的訪問次數(shù)

參考鏈接://http://www.reibang.com/p/6bcff1f9feee

#import "UIViewController+Logging.h"
#import <objc/runtime.h>

@implementation UIViewController (Logging)

+ (void)load
{
    swizzleMethod([self class], @selector(viewDidAppear:), @selector(swizzled_viewDidAppear:));
}

- (void)swizzled_viewDidAppear:(BOOL)animated
{
    // call original implementation
    [self swizzled_viewDidAppear:animated];
    
    // Logging
    NSLog(@"%@", NSStringFromClass([self class]));
}

void swizzleMethod(Class class, SEL originalSelector, SEL swizzledSelector)
{
    // the method might not exist in the class, but in its superclass
    Method originalMethod = class_getInstanceMethod(class, originalSelector);
    Method swizzledMethod = class_getInstanceMethod(class, swizzledSelector);
    
    // class_addMethod will fail if original method already exists
    BOOL didAddMethod = class_addMethod(class, originalSelector, method_getImplementation(swizzledMethod), method_getTypeEncoding(swizzledMethod));
    
    // the method doesn’t exist and we just added one
    if (didAddMethod) {
        class_replaceMethod(class, swizzledSelector, method_getImplementation(originalMethod), method_getTypeEncoding(originalMethod));
    }
    else {
        method_exchangeImplementations(originalMethod, swizzledMethod);
    }
    
}
@end

  • 需求3: 要求項(xiàng)目中的按鈕,不可以連續(xù)點(diǎn)擊多次
#import "UIButton+DelaySwizzling.h"
#import <objc/runtime.h>

@interface UIButton()

// 重復(fù)點(diǎn)擊間隔
@property (nonatomic, assign) NSTimeInterval xxx_acceptEventInterval;
// 上一次點(diǎn)擊時(shí)間戳
@property (nonatomic, assign) NSTimeInterval xxx_acceptEventTime;

@end


@implementation UIButton (DelaySwizzling)

+ (void)load {
    static dispatch_once_t onceToken;
    dispatch_once(&onceToken, ^{
        Class class = [self class];

        SEL originalSelector = @selector(sendAction:to:forEvent:);
        SEL swizzledSelector = @selector(xxx_sendAction:to:forEvent:);

        Method originalMethod = class_getInstanceMethod(class, originalSelector);
        Method swizzledMethod = class_getInstanceMethod(class, swizzledSelector);

        BOOL didAddMethod = class_addMethod(class,
                                            originalSelector,
                                            method_getImplementation(swizzledMethod),
                                            method_getTypeEncoding(swizzledMethod));

        if (didAddMethod) {
            class_replaceMethod(class,
                                swizzledSelector,
                                method_getImplementation(originalMethod),
                                method_getTypeEncoding(originalMethod));
        } else {
            method_exchangeImplementations(originalMethod, swizzledMethod);
        }
    });
}

- (void)xxx_sendAction:(SEL)action to:(id)target forEvent:(UIEvent *)event {
    
    // 如果想要設(shè)置統(tǒng)一的間隔時(shí)間寞缝,可以在此處加上以下幾句
    if (self.xxx_acceptEventInterval <= 0) {
        // 如果沒有自定義時(shí)間間隔,則默認(rèn)為 0.4 秒
        self.xxx_acceptEventInterval = 0.4;
    }
    
    // 是否小于設(shè)定的時(shí)間間隔
    BOOL needSendAction = (NSDate.date.timeIntervalSince1970 - self.xxx_acceptEventTime >= self.xxx_acceptEventInterval);
    
    // 更新上一次點(diǎn)擊時(shí)間戳
    if (self.xxx_acceptEventInterval > 0) {
        self.xxx_acceptEventTime = NSDate.date.timeIntervalSince1970;
    }
    
    // 兩次點(diǎn)擊的時(shí)間間隔小于設(shè)定的時(shí)間間隔時(shí)仰泻,才執(zhí)行響應(yīng)事件
    if (needSendAction) {
        [self xxx_sendAction:action to:target forEvent:event];
    }
}

- (NSTimeInterval )xxx_acceptEventInterval{
    return [objc_getAssociatedObject(self, "UIControl_acceptEventInterval") doubleValue];
}

- (void)setXxx_acceptEventInterval:(NSTimeInterval)xxx_acceptEventInterval{
    objc_setAssociatedObject(self, "UIControl_acceptEventInterval", @(xxx_acceptEventInterval), OBJC_ASSOCIATION_RETAIN_NONATOMIC);
}

- (NSTimeInterval )xxx_acceptEventTime{
    return [objc_getAssociatedObject(self, "UIControl_acceptEventTime") doubleValue];
}

- (void)setXxx_acceptEventTime:(NSTimeInterval)xxx_acceptEventTime{
    objc_setAssociatedObject(self, "UIControl_acceptEventTime", @(xxx_acceptEventTime), OBJC_ASSOCIATION_RETAIN_NONATOMIC);
}

@end

5.給分類添加屬性

  • 需求1 :給Catagory 添加屬性

文章參考 http://www.reibang.com/p/916aef6f7ab1

//.h文件
#import <Foundation/Foundation.h>

NS_ASSUME_NONNULL_BEGIN

@interface NSObject (AssociatedObject)

@property(nonatomic,copy)NSString *associatedObject;

@end

NS_ASSUME_NONNULL_END
//.m文件
import "NSObject+AssociatedObject.h"
#import <objc/runtime.h>

@implementation NSObject (AssociatedObject)

- (void)setAssociatedObject:(id)associatedObject
{
    //注意最后一個(gè)參數(shù)的類型
    objc_setAssociatedObject(self, @selector(associatedObject), associatedObject, OBJC_ASSOCIATION_COPY);
}

- (id)associatedObject
{
    return objc_getAssociatedObject(self, _cmd);
}



@end

需求二: 給UIButton添加一個(gè)block處理點(diǎn)擊事件
(1)寫法1

#import <UIKit/UIKit.h>
#import <objc/runtime.h>    // 導(dǎo)入頭文件

// 聲明一個(gè)button點(diǎn)擊事件的回調(diào)block
typedef void(^ButtonClickCallBack)(UIButton *button);

@interface UIButton (Handle)

// 為UIButton增加的回調(diào)方法
- (void)handleClickCallBack:(ButtonClickCallBack)callBack;

@end

#import "UIButton+Handle.h"

// 聲明一個(gè)靜態(tài)的索引key荆陆,用于獲取被關(guān)聯(lián)對(duì)象的值
static char *buttonClickKey;

@implementation UIButton (Handle)

- (void)handleClickCallBack:(ButtonClickCallBack)callBack {
    // 將button的實(shí)例與回調(diào)的block通過索引key進(jìn)行關(guān)聯(lián):
    objc_setAssociatedObject(self, &buttonClickKey, callBack, OBJC_ASSOCIATION_RETAIN_NONATOMIC);
    
    // 設(shè)置button執(zhí)行的方法
    [self addTarget:self action:@selector(buttonClicked) forControlEvents:UIControlEventTouchUpInside];
}

- (void)buttonClicked {
    // 通過靜態(tài)的索引key,獲取被關(guān)聯(lián)對(duì)象(這里就是回調(diào)的block)
    ButtonClickCallBack callBack = objc_getAssociatedObject(self, &buttonClickKey);
    
    if (callBack) {
        callBack(self);
    }
}

@end

(2)寫法二

#import <UIKit/UIKit.h>
#import <objc/runtime.h>  

typedef void(^ButtonClickCallBack)(UIButton * _Nullable button);

NS_ASSUME_NONNULL_BEGIN

@interface UIButton (Handler)

@property(nonatomic,copy)ButtonClickCallBack  bottonCallBack;

@end

NS_ASSUME_NONNULL_END
#import "UIButton+Handler.h"


@implementation UIButton (Handler)

- (void)setCallBlock:(ButtonClickCallBack)bottonCallBack
{
    objc_setAssociatedObject(self, @selector(callBlock), bottonCallBack, OBJC_ASSOCIATION_COPY_NONATOMIC);
}

- (ButtonClickCallBack )callBlock
{
    return objc_getAssociatedObject(self, _cmd);
    //    return objc_getAssociatedObject(self, @selector(callBlock));
}



@end

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末集侯,一起剝皮案震驚了整個(gè)濱河市被啼,隨后出現(xiàn)的幾起案子帜消,更是在濱河造成了極大的恐慌,老刑警劉巖浓体,帶你破解...
    沈念sama閱讀 218,122評(píng)論 6 505
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件泡挺,死亡現(xiàn)場(chǎng)離奇詭異,居然都是意外死亡命浴,警方通過查閱死者的電腦和手機(jī)娄猫,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,070評(píng)論 3 395
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來生闲,“玉大人媳溺,你說我怎么就攤上這事」蚋梗” “怎么了褂删?”我有些...
    開封第一講書人閱讀 164,491評(píng)論 0 354
  • 文/不壞的土叔 我叫張陵,是天一觀的道長(zhǎng)冲茸。 經(jīng)常有香客問我,道長(zhǎng)缅帘,這世上最難降的妖魔是什么轴术? 我笑而不...
    開封第一講書人閱讀 58,636評(píng)論 1 293
  • 正文 為了忘掉前任,我火速辦了婚禮钦无,結(jié)果婚禮上逗栽,老公的妹妹穿的比我還像新娘。我一直安慰自己失暂,他們只是感情好彼宠,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,676評(píng)論 6 392
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著弟塞,像睡著了一般凭峡。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上决记,一...
    開封第一講書人閱讀 51,541評(píng)論 1 305
  • 那天摧冀,我揣著相機(jī)與錄音,去河邊找鬼系宫。 笑死索昂,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的扩借。 我是一名探鬼主播椒惨,決...
    沈念sama閱讀 40,292評(píng)論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼,長(zhǎng)吁一口氣:“原來是場(chǎng)噩夢(mèng)啊……” “哼潮罪!你這毒婦竟也來了康谆?” 一聲冷哼從身側(cè)響起凄杯,我...
    開封第一講書人閱讀 39,211評(píng)論 0 276
  • 序言:老撾萬榮一對(duì)情侶失蹤,失蹤者是張志新(化名)和其女友劉穎秉宿,沒想到半個(gè)月后戒突,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 45,655評(píng)論 1 314
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡描睦,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,846評(píng)論 3 336
  • 正文 我和宋清朗相戀三年膊存,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片忱叭。...
    茶點(diǎn)故事閱讀 39,965評(píng)論 1 348
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡隔崎,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出韵丑,到底是詐尸還是另有隱情爵卒,我是刑警寧澤,帶...
    沈念sama閱讀 35,684評(píng)論 5 347
  • 正文 年R本政府宣布撵彻,位于F島的核電站钓株,受9級(jí)特大地震影響,放射性物質(zhì)發(fā)生泄漏陌僵。R本人自食惡果不足惜轴合,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,295評(píng)論 3 329
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望碗短。 院中可真熱鬧受葛,春花似錦、人聲如沸偎谁。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,894評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽巡雨。三九已至闰渔,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間鸯隅,已是汗流浹背澜建。 一陣腳步聲響...
    開封第一講書人閱讀 33,012評(píng)論 1 269
  • 我被黑心中介騙來泰國(guó)打工, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留蝌以,地道東北人炕舵。 一個(gè)月前我還...
    沈念sama閱讀 48,126評(píng)論 3 370
  • 正文 我出身青樓,卻偏偏與公主長(zhǎng)得像跟畅,于是被迫代替她去往敵國(guó)和親咽筋。 傳聞我的和親對(duì)象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,914評(píng)論 2 355

推薦閱讀更多精彩內(nèi)容

  • 久違的晴天徊件,家長(zhǎng)會(huì)奸攻。 家長(zhǎng)大會(huì)開好到教室時(shí)蒜危,離放學(xué)已經(jīng)沒多少時(shí)間了。班主任說已經(jīng)安排了三個(gè)家長(zhǎng)分享經(jīng)驗(yàn)睹耐。 放學(xué)鈴聲...
    飄雪兒5閱讀 7,523評(píng)論 16 22
  • 今天感恩節(jié)哎辐赞,感謝一直在我身邊的親朋好友。感恩相遇硝训!感恩不離不棄响委。 中午開了第一次的黨會(huì),身份的轉(zhuǎn)變要...
    迷月閃星情閱讀 10,566評(píng)論 0 11
  • 可愛進(jìn)取窖梁,孤獨(dú)成精赘风。努力飛翔,天堂翱翔纵刘。戰(zhàn)爭(zhēng)美好邀窃,孤獨(dú)進(jìn)取。膽大飛翔假哎,成就輝煌瞬捕。努力進(jìn)取,遙望位谋,和諧家園山析。可愛游走...
    趙原野閱讀 2,727評(píng)論 1 1
  • 在妖界我有個(gè)名頭叫胡百曉掏父,無論是何事,只要找到胡百曉即可有解決的辦法秆剪。因?yàn)槭侵缓偞蠹乙杂瀭饔灲形摇皟A城百曉”赊淑,...
    貓九0110閱讀 3,261評(píng)論 7 3