iOS runtime應(yīng)用整理

頁面統(tǒng)計(jì)埋點(diǎn)

#import <objc/runtime.h>
@implementation UIViewController (Stastistics)
+ (void)load{
    static dispatch_once_t onceToken;
    dispatch_once(&onceToken, ^{
        //頁面進(jìn)入
        Method orginWillAppear = class_getInstanceMethod([self class], @selector(viewWillAppear:));
        Method swizWillAppear = class_getInstanceMethod([self class], @selector(stastistics_viewWillAppear:));
        bool isAddWillAppear = class_addMethod([self class], @selector(viewWillAppear:), method_getImplementation(swizWillAppear), method_getTypeEncoding(swizWillAppear));
        if (isAddWillAppear) {
            class_replaceMethod([self class], @selector(stastistics_viewWillAppear:), method_getImplementation(orginWillAppear), method_getTypeEncoding(orginWillAppear));
        } else {
            method_exchangeImplementations(orginWillAppear, swizWillAppear);
        }
        //頁面離開
        Method orginWillDisAppear = class_getInstanceMethod([self class], @selector(viewWillDisappear:));
        Method swizWillDisAppear = class_getInstanceMethod([self class], @selector(stastistics_viewWillDisappear:));
        bool isAddWillDisAppear = class_addMethod([self class], @selector(viewWillDisappear:), method_getImplementation(swizWillDisAppear), method_getTypeEncoding(swizWillDisAppear));
        if (isAddWillDisAppear) {
            class_replaceMethod([self class], @selector(stastistics_viewWillDisappear:), method_getImplementation(orginWillDisAppear), method_getTypeEncoding(orginWillDisAppear));
        } else {
            method_exchangeImplementations(orginWillDisAppear, swizWillDisAppear);
        }
    });
}
- (void)stastistics_viewWillAppear:(BOOL)animated{
    //執(zhí)行頁面進(jìn)入埋點(diǎn)
    [self stastistics_viewWillAppear:animated];
}
- (void)stastistics_viewWillDisappear:(BOOL)animated{
    //執(zhí)行頁面離開埋點(diǎn)
    [self stastistics_viewWillDisappear:animated];
}
  • Swizzling應(yīng)該總在+load中執(zhí)行
    Objective-C在運(yùn)行時(shí)會自動(dòng)調(diào)用類的兩個(gè)方法+load和+initialize咳促。+load會在類初始加載時(shí)調(diào)用人乓, +initialize方法是以懶加載的方式被調(diào)用的,如果程序一直沒有給某個(gè)類或它的子類發(fā)送消息央勒,那么這個(gè)類的 +initialize方法是永遠(yuǎn)不會被調(diào)用的。
  • Swizzling應(yīng)該總是在dispatch_once中執(zhí)行以及在+load中執(zhí)行時(shí),不應(yīng)調(diào)用[super load]
    Swizzling會改變?nèi)譅顟B(tài)艇搀,所以在運(yùn)行時(shí)采取一些預(yù)防措施臀蛛,使用dispatch_once就能夠確保代碼不管有多少線程都只被執(zhí)行一次以及調(diào)用父類可能造成重復(fù)調(diào)用

實(shí)現(xiàn)數(shù)組越界異常保護(hù)

#import "objc/runtime.h"
@implementation NSArray (Swizzling)
+ (void)load {
    Method fromMethod = class_getInstanceMethod(objc_getClass("__NSArrayI"), @selector(objectAtIndex:));
    Method toMethod = class_getInstanceMethod(objc_getClass("__NSArrayI"), @selector(swizzling_objectAtIndex:));
    method_exchangeImplementations(fromMethod, toMethod);
}
- (id)swizzling_objectAtIndex:(NSUInteger)index {
    if (self.count-1 < index) {
        // 異常處理
        @try {
            return [self swizzling_objectAtIndex:index];
        }
        @catch (NSException *exception) {
            // 打印崩潰信息
            NSLog(@"---------- %s Crash Because Method %s  ----------\n", class_getName(self.class), __func__);
            NSLog(@"%@", [exception callStackSymbols]);
            return nil;
        }
        @finally {}
    } else {
        return [self swizzling_objectAtIndex:index];
    }
}
  • NSArray其實(shí)在Runtime中對應(yīng)著__NSArrayI亲桦,NSMutableArray對應(yīng)著__NSArrayM,NSDictionary對應(yīng)著__NSDictionaryI浊仆,NSMutableDictionary對應(yīng)著__NSDictionaryM客峭。
  • 對NSArray增加分類實(shí)現(xiàn)異常保護(hù)后,NSMutableArray仍然會出現(xiàn)數(shù)組越界抡柿。

NSCoding的自動(dòng)歸檔和自動(dòng)解檔

#import <objc/runtime.h>

@implementation NSObject (Extension)

- (void)decode:(NSCoder *)aDecoder {
    // 一層層父類往上查找舔琅,對父類的屬性執(zhí)行歸解檔方法
    Class c = self.class;
    while (c &&c != [NSObject class]) {

        unsigned int outCount = 0;
        Ivar *ivars = class_copyIvarList(c, &outCount);
        for (int i = 0; i < outCount; i++) {
            Ivar ivar = ivars[i];
            NSString *key = [NSString stringWithUTF8String:ivar_getName(ivar)];

            // 如果有實(shí)現(xiàn)該方法再去調(diào)用
            if ([self respondsToSelector:@selector(ignoredNames)]) {
                if ([[self ignoredNames] containsObject:key]) continue;
            }

            id value = [aDecoder decodeObjectForKey:key];
            [self setValue:value forKey:key];
        }
        free(ivars);
        c = [c superclass];
    }

}

- (void)encode:(NSCoder *)aCoder {
    // 一層層父類往上查找,對父類的屬性執(zhí)行歸解檔方法
    Class c = self.class;
    while (c &&c != [NSObject class]) {

        unsigned int outCount = 0;
        Ivar *ivars = class_copyIvarList([self class], &outCount);
        for (int i = 0; i < outCount; i++) {
            Ivar ivar = ivars[i];
            NSString *key = [NSString stringWithUTF8String:ivar_getName(ivar)];

            // 如果有實(shí)現(xiàn)該方法再去調(diào)用
            if ([self respondsToSelector:@selector(ignoredNames)]) {
                if ([[self ignoredNames] containsObject:key]) continue;
            }

            id value = [self valueForKeyPath:key];
            [aCoder encodeObject:value forKey:key];
        }
        free(ivars);
        c = [c superclass];
    }
}

在需要?dú)w解檔的對象中實(shí)現(xiàn)下面方法即可

// 設(shè)置需要忽略的屬性
- (NSArray *)ignoredNames {
    return @[@"bone"];
}

// 在系統(tǒng)方法內(nèi)來調(diào)用我們的方法
- (instancetype)initWithCoder:(NSCoder *)aDecoder {
    if (self = [super init]) {
        [self decode:aDecoder];
    }
    return self;
}

- (void)encodeWithCoder:(NSCoder *)aCoder {
    [self encode:aCoder];
}

利用runtime 進(jìn)行字典轉(zhuǎn)模型

@implementation NSObject (JSONExtension)

- (void)setDict:(NSDictionary *)dict {

    Class c = self.class;
    while (c &&c != [NSObject class]) {

        unsigned int outCount = 0;
        Ivar *ivars = class_copyIvarList(c, &outCount);
        for (int i = 0; i < outCount; i++) {
            Ivar ivar = ivars[i];
            NSString *key = [NSString stringWithUTF8String:ivar_getName(ivar)];

            // 成員變量名轉(zhuǎn)為屬性名(去掉下劃線 _ )
            key = [key substringFromIndex:1];
            // 取出字典的值
            id value = dict[key];

            // 如果模型屬性數(shù)量大于字典鍵值對數(shù)理洲劣,模型屬性會被賦值為nil而報(bào)錯(cuò)
            if (value == nil) continue;

            // 獲得成員變量的類型
            NSString *type = [NSString stringWithUTF8String:ivar_getTypeEncoding(ivar)];

            // 如果屬性是對象類型
            NSRange range = [type rangeOfString:@"@"];
            if (range.location != NSNotFound) {
                // 那么截取對象的名字(比如@"Dog"备蚓,截取為Dog)
                type = [type substringWithRange:NSMakeRange(2, type.length - 3)];
                // 排除系統(tǒng)的對象類型
                if (![type hasPrefix:@"NS"]) {
                    // 將對象名轉(zhuǎn)換為對象的類型,將新的對象字典轉(zhuǎn)模型(遞歸)
                    Class class = NSClassFromString(type);
                    value = [class objectWithDict:value];

                }else if ([type isEqualToString:@"NSArray"]) {

                    // 如果是數(shù)組類型囱稽,將數(shù)組中的每個(gè)模型進(jìn)行字典轉(zhuǎn)模型郊尝,先創(chuàng)建一個(gè)臨時(shí)數(shù)組存放模型
                    NSArray *array = (NSArray *)value;
                    NSMutableArray *mArray = [NSMutableArray array];

                    // 獲取到每個(gè)模型的類型
                    id class ;
                    if ([self respondsToSelector:@selector(arrayObjectClass)]) {

                        NSString *classStr = [self arrayObjectClass];
                        class = NSClassFromString(classStr);
                    }
                    // 將數(shù)組中的所有模型進(jìn)行字典轉(zhuǎn)模型
                    for (int i = 0; i < array.count; i++) {
                        [mArray addObject:[class objectWithDict:value[i]]];
                    }

                    value = mArray;
                }
            }

            // 將字典中的值設(shè)置到模型上
            [self setValue:value forKeyPath:key];
        }
        free(ivars);
        c = [c superclass];
    }
}

+ (instancetype )objectWithDict:(NSDictionary *)dict {
    NSObject *obj = [[self alloc]init];
    [obj setDict:dict];
    return obj;
}

Associated Object 關(guān)聯(lián)對象

static const char imgIdKey ;
@implementation UIImage (Category)
- (void)setImgId:(NSString *)imgId{
    objc_setAssociatedObject(self, &imgIdKey, imgId, OBJC_ASSOCIATION_COPY_NONATOMIC);
}
- (NSString *)imgId{
    return objc_getAssociatedObject(self, &imgIdKey);
}
  • const void *key 區(qū)分不同的關(guān)聯(lián)對象的 key。有3種寫法战惊。

使用 &AssociatedObjectKey 作為key值

   static char AssociatedObjectKey = "AssociatedKey";

使用AssociatedKey 作為key值

  static const void *AssociatedKey = "AssociatedKey";

使用@selector

  @selector(associatedKey)
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末流昏,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌况凉,老刑警劉巖谚鄙,帶你破解...
    沈念sama閱讀 217,406評論 6 503
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異刁绒,居然都是意外死亡闷营,警方通過查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,732評論 3 393
  • 文/潘曉璐 我一進(jìn)店門知市,熙熙樓的掌柜王于貴愁眉苦臉地迎上來傻盟,“玉大人,你說我怎么就攤上這事初狰∧荆” “怎么了?”我有些...
    開封第一講書人閱讀 163,711評論 0 353
  • 文/不壞的土叔 我叫張陵奢入,是天一觀的道長筝闹。 經(jīng)常有香客問我,道長腥光,這世上最難降的妖魔是什么关顷? 我笑而不...
    開封第一講書人閱讀 58,380評論 1 293
  • 正文 為了忘掉前任,我火速辦了婚禮武福,結(jié)果婚禮上议双,老公的妹妹穿的比我還像新娘。我一直安慰自己捉片,他們只是感情好平痰,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,432評論 6 392
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著伍纫,像睡著了一般宗雇。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上莹规,一...
    開封第一講書人閱讀 51,301評論 1 301
  • 那天赔蒲,我揣著相機(jī)與錄音,去河邊找鬼良漱。 笑死舞虱,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的母市。 我是一名探鬼主播矾兜,決...
    沈念sama閱讀 40,145評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼患久!你這毒婦竟也來了椅寺?” 一聲冷哼從身側(cè)響起舶沿,我...
    開封第一講書人閱讀 39,008評論 0 276
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎配并,沒想到半個(gè)月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體高镐,經(jīng)...
    沈念sama閱讀 45,443評論 1 314
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡溉旋,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,649評論 3 334
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了嫉髓。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片观腊。...
    茶點(diǎn)故事閱讀 39,795評論 1 347
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖算行,靈堂內(nèi)的尸體忽然破棺而出梧油,到底是詐尸還是另有隱情,我是刑警寧澤州邢,帶...
    沈念sama閱讀 35,501評論 5 345
  • 正文 年R本政府宣布,位于F島的核電站,受9級特大地震影響肛鹏,放射性物質(zhì)發(fā)生泄漏飞涂。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,119評論 3 328
  • 文/蒙蒙 一呀枢、第九天 我趴在偏房一處隱蔽的房頂上張望胚股。 院中可真熱鬧,春花似錦裙秋、人聲如沸琅拌。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,731評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽进宝。三九已至,卻和暖如春泣侮,著一層夾襖步出監(jiān)牢的瞬間即彪,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 32,865評論 1 269
  • 我被黑心中介騙來泰國打工活尊, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留隶校,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 47,899評論 2 370
  • 正文 我出身青樓蛹锰,卻偏偏與公主長得像深胳,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個(gè)殘疾皇子铜犬,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,724評論 2 354

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