iOS Runtime的那些小事(持續更新中)

runtime是動態語言的一個特征,初學者在編寫程序的時候對這種運行時機制也是特別模糊。網上對這個東西的教程也是一大篇,所以具體的原理我在這里就不介紹了,我在這邊學了實際開發中用到的幾個小案例拿出來給童鞋找點感覺。

字典轉模型

字典轉模型是我們在實際開發中經常遇到的問題,我們可以自己一個一個把接口獲取下來的json數據賦值給model里的屬性,也可以采用網上流行的3方庫來進行裝換。
其實 實現這一個功能很簡單(大牛的3方庫,優化以及細節都考慮的很全面)。
我的基本思路是這樣:我首先將字典的keys取出來,之后將model里面的成員變量獲取到。對比key和成員變量的名稱,如果相同就將value 賦值給成員變量。
實際代碼:
聲明一個NSObject的類目:

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

@implementation NSObject(Extension)


+ (id)dicToModel:(NSDictionary *)dict {
    //創建調用對象
    id per = [self new];
    //要獲取model類的屬性
    unsigned int outCount;
    Ivar *vars = class_copyIvarList([self class], &outCount);
    //遍歷傳過來的數組
    for (id name1 in dict.allKeys) {
  
        NSString *name = [NSString stringWithFormat:@"_%@",name1];
        for (int i = 0; i < outCount; i++) {
            const char *varName = ivar_getName(vars[i]);
            NSString *runName = [NSString stringWithUTF8String:varName];
            if ([runName isEqualToString:name]) {
                //字典里的屬性和model里面的屬性 吻合則轉換
                //用這個方法的話,基本上屬性 得特殊處理
//              object_setIvar(per, vars[i], [dict objectForKey:name1]);
                [per setValue:[dict objectForKey:name1] forKey:runName];
            }
        }
    }
    
    return per;
    
}
@end

聲明一個Person類:

@interface Person : NSObject

@property (nonatomic, copy) NSString *name;
@property (nonatomic, assign) NSInteger *age;
@end

實際使用:

 //字典轉模型
    NSDictionary *dict = @{@"name":@"李雷",@"age":@18};
    Person *per = [Person dicToModel:dict];
    NSLog(@"%@=======%ld",per.name,per.age);

打印結果:

2016-03-07 16:10:07.068 DicToModel[3639:134187] 李雷=======18

使AlertView 像UIAlertViewController 一樣初始化只有直接處理點擊事件

我們在使用alertView的時候 如果想處理點擊方法 必須要實現代理方法,在代理方法里面處理點擊事件。如果在一個C上有多個alertView公用一個代理方法。這時候,對我們使用起來和閱讀都有一些麻煩。
而UIAlertViewController 可以初始化之后 設置點擊方法。我感覺這樣對代碼的閱讀直觀一些,就自己寫了一個類目來處理這些東西。

//使用
    UIAlertView *alert = [[UIAlertView alloc] initWithTitle:@"22" message:@"hah" delegate:nil cancelButtonTitle:@"cancel" otherButtonTitles:@"sure", nil];

    [alert handleSureAction:^(NSInteger index) {
        if (index == 1) {
            NSLog(@"確定被點擊");
        }else {
            NSLog(@"取消被點擊");
        }
    }];

    [alert show];

為alertView 新加的類目

//
//  UIAlertView+ActionBlock.h
//  classCluster
//
//  Created by DQ on 16/4/8.
//  Copyright ? 2016年 DQ. All rights reserved.
//

#import <UIKit/UIKit.h>
typedef void (^handleAction)(NSInteger index) ;

@interface UIAlertView (ActionBlock)<UIAlertViewDelegate>

- (void) handleSureAction:(handleAction) handle ;
@end
//
//  UIAlertView+ActionBlock.m
//  classCluster
//
//  Created by DQ on 16/4/8.
//  Copyright ? 2016年 DQ. All rights reserved.
//

#import "UIAlertView+ActionBlock.h"
#import <objc/runtime.h>
static void *KAlertViewActionKey = "alertViewKey";

@implementation UIAlertView (ActionBlock)

- (void)handleSureAction:(handleAction)handle {
    self.delegate = self;
    objc_setAssociatedObject(self, KAlertViewActionKey, handle, OBJC_ASSOCIATION_RETAIN_NONATOMIC);

}
- (void)alertView:(UIAlertView *)alertView clickedButtonAtIndex:(NSInteger)buttonIndex {
    handleAction handle = objc_getAssociatedObject(self, KAlertViewActionKey);
    handle(buttonIndex);
    
}
@end

引入這個類目之后,就可以愉快的處理點擊事件了。
======更新

http://www.lxweimin.com/p/b9fdd17c525e 原文 可以瞅瞅

method swizzling 全局修改字體

//
//  UILabel+ChangeFont.m
//  methodSwizzling-changeFont
//
//  Created by DQ on 16/4/28.
//  Copyright ? 2016年 DQ. All rights reserved.
//

#import "UILabel+ChangeFont.h"
#import <objc/runtime.h>

@implementation UILabel (ChangeFont)

+ (void)load {
    static dispatch_once_t onceToken;
    dispatch_once(&onceToken, ^{
        SEL oriSEL = @selector(willMoveToSuperview:);
        SEL swizzlingSEL = @selector(myMovetoSuperView:);
        Method oriMethod = class_getInstanceMethod([self class], oriSEL);
        Method swizzlingMethod = class_getInstanceMethod([self class], swizzlingSEL);
        BOOL isadd = class_addMethod([self class], oriSEL,method_getImplementation(swizzlingMethod), method_getTypeEncoding(swizzlingMethod));
        if (isadd) {
            class_replaceMethod([self class], swizzlingSEL, method_getImplementation(oriMethod), method_getTypeEncoding(oriMethod));
        }else {
            method_exchangeImplementations(oriMethod, swizzlingMethod);
        }
    });

}

- (void)myMovetoSuperView:(UIView *)superView  {
    [self myMovetoSuperView:superView];
    self.font = [UIFont fontWithName:@"Aladdin" size:30];


}

這邊有一個疑問就是,為什么不是直接交換方法,而是判斷有沒有添加?
百度南峰子博客下面評論有答案
1.didAddMethod一直為NO,是因為你當前類重載了父類的viewWillAppear方法。如果當前類沒有重寫父類的方法class_addMethod會把新的IMP替換掉父類的IMP,也就是重寫了父類方法,并且返回yes;如果當前類重寫了父類方法,就不在替換IMP,直接返回NO。
這是oc原話:

  • @note class_addMethod will add an override of a superclass's implementation,
  • but will not replace an existing implementation in this class.
  • To change an existing implementation, use method_setImplementation.
    如果說的有什么不對,還請提出,大家共同學習。最后感謝博主的無私分享。好人一生平安!
    2.添加是為了重寫父類的 viewWillAppear 方法,如果你自己已經手動重寫了 viewWillAppear 方法,class_addMethod() 就會添加失敗,返回 NO。

如果返回 NO ,說明已經重寫,就直接交換。

如果返回 YES, 說明添加成功,也就是你之前沒有重寫 viewWillAppear 方法。因為在添加方法里面是將 originalSelector 與 swizzledMethod 的 IMP 綁定在一起了,所以接下來只用將 swizzledSelector 與 originalMethod 的 IMP 綁定就可以了。也就實現了交換。

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。

推薦閱讀更多精彩內容