頁面統(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)