iOSruntime執行時的四種基本應用場景
阿新 • • 發佈:2019-02-12
1:什麼是執行時(runtime)?
因為Objective-C是一門動態型語言,所以會把一些決定工作本來在編譯期完成的,放在執行的時候去做。這樣做的目的極大的增加的系統的靈活性。所以編譯器是不夠的,我們還需要一個執行時系統 (runtime system) 來執行在執行的時候需要執行的程式碼。這就是 Objective-C Runtime 系統存在的意義,它是整個OC執行框架的一塊基石。在這裡不做執行時底層的實現做說明,只總結一下執行時在應用中的4種基本場景。
2:在什麼場合使用執行時(runtime)?
(1):交換方法
1:匯入標頭檔案
2:實現自定義的方法#import <objc/runtime.h>
+(void)methodSwizzlingWithOriginalSelector:(SEL)originalSelector bySwizzledSelector:(SEL)swizzledSelector{
Class class = [self class];
//原始方法
Method originalMethod = class_getInstanceMethod(class, originalSelector);
//替換方法
Method swizzledMethod = class_getInstanceMethod(class, swizzledSelector);
//引數1:新增方法的類
//引數2:已經被新增的方法
//引數3:將要新增的新方法,該方法必須有兩個引數和_cmd;
//引數4:描述方法型別的字元型別
//如果被新增成功就會返回successful,如果這個類已經有相同函式名的方法,並且已經被實現,那麼就會返回NO
//如果想要改變這個已經存在的方法,就使用method_setImplementation方法
BOOL didAddMethod = class_addMethod(class, originalSelector, method_getImplementation(swizzledMethod), method_getTypeEncoding(swizzledMethod));
/**
* Replaces the implementation of a method for a given class.
*
* @param cls The class you want to modify.
* @param name A selector that identifies the method whose implementation you want to replace.
* @param imp The new implementation for the method identified by name for the class identified by cls.
* @param types An array of characters that describe the types of the arguments to the method.
* Since the function must take at least two arguments—self and _cmd, the second and third characters
* must be “@:” (the first character is the return type).
*
* @return The previous implementation of the method identified by \e name for the class identified by \e cls.
*
* @note This function behaves in two different ways:
* - If the method identified by \e name does not yet exist, it is added as if \c class_addMethod were called.
* The type encoding specified by \e types is used as given.
* - If the method identified by \e name does exist, its \c IMP is replaced as if \c method_setImplementation were called.
* The type encoding specified by \e types is ignored.
*/
if (didAddMethod) {
class_replaceMethod(class, swizzledSelector, method_getImplementation(originalMethod), method_getTypeEncoding(originalMethod));
}else{
method_exchangeImplementations(originalMethod, swizzledMethod);
}
}
3:應用
(1:匯入上面的標頭檔案
#import "NSObject+RuntimeMethodSwizzling.h"
(2: 重寫父類方法
+(void)load{ static dispatch_once_t onceToken; dispatch_once(&onceToken, ^{ [UIViewController methodSwizzlingWithOriginalSelector:@selector(viewWillAppear:) bySwizzledSelector:@selector(my_ViewWillAppear:)]; }); }
(3: 實現替換的方法
-(void)my_ViewWillAppear:(BOOL)animation{
[self my_ViewWillAppear:animation];
NSLog(@"父類交換了方法");
//注意:新的方法應該總是在+load方法中
//在OC中 ,RunTime 會在類初始化載入的時候呼叫+laod方法,在類的第一次被呼叫的時候實現
//initialize方法,由於Method Swizzling 會影響到類的全域性狀態,所以要儘量避免在併發處理
//競爭情況 ,+load方法能保證在類的初始化的過程中被載入,並保證這種改變應用級別的一致性
//要使用安全單利進行交換
//
}
(2):新增屬性
(1:為UIImageView 增加一個例項變數
#import <UIKit/UIKit.h>
@interface UIImageView (AddProperty)
@property (nonatomic,copy)NSString * downUrl;
@end
(2:重寫setter和getter方法
#import <objc/runtime.h>
//新增屬性
-(void)setDownUrl:(NSString *)downUrl{
objc_setAssociatedObject(self, @selector(downUrl), downUrl, OBJC_ASSOCIATION_RETAIN_NONATOMIC);
}
-(NSString *)downUrl{
return objc_getAssociatedObject(self, @selector(downUrl));
}
(3:引用新增的屬性
UIImageView * imageview = [[UIImageView alloc] init];
//新增一個屬性
imageview.downUrl = @"www.baidu.com";
imageview.frame = CGRectMake(100, 200, 100, 100);
imageview.backgroundColor = [UIColor redColor];
[self.view addSubview:imageview];
(3) 字典轉模型
#import <Foundation/Foundation.h>
@interface RunTime : NSObject
@property (nonatomic,copy)NSString * year;
@property (nonatomic,copy)NSString * month;
@property (nonatomic,copy)NSString * day;
+(id)createModelWith:(NSDictionary *)dictionary;
@end
(1:實現方法
#import "objc/runtime.h"
#import "objc/message.h"
+(id)createModelWith:(NSDictionary *)dictionary{
id objc = [[self alloc] init];
unsigned int count = 0;
if (objc) {
Ivar * ivars = class_copyIvarList([self class], &count);
for (int i = 0; i<count; i++) {
Ivar ivar = ivars[i];
//獲取變數名稱
const char * name = ivar_getName(ivar);
//獲取變數名
NSString * key = [NSString stringWithUTF8String:name];
//下面的幾步是用來生成setter方法
NSString * keyvalue = [key substringFromIndex:1];
key = keyvalue.capitalizedString;
//拼接seter方法
key = [NSString stringWithFormat:@"set%@:",key];
SEL func = NSSelectorFromString(key);
//如果自己響應例項變數的setter方法
if ([objc respondsToSelector:func]) {
id value = @"";
if ([dictionary objectForKey: keyvalue] != nil) {
value = [dictionary objectForKey:keyvalue];
}
//然後在主執行緒中執行方法
[objc performSelectorOnMainThread:func withObject:value waitUntilDone:[NSThread isMainThread]];
}
}
free(ivars);
}
return objc;
}
(2:呼叫
//仿網路請求的資料
NSDictionary * modeldict = @{@"year":@"2020",@"month":@"11",@"day":@"11"};
RunTime * model = [RunTime createModelWith:modeldict];
NSLog(@"解析資料:年:%@||月:%@||日:%@",model.year,model.month,model.day);
(4)解檔歸檔
(1:例項變數
#import <Foundation/Foundation.h>
@interface EncodeAndUnEncode : NSObject<NSCoding>
@property (nonatomic,copy)NSString * title; //電影名
@property (nonatomic,copy)NSString * genres; //電影的種類
@property (nonatomic,copy)NSString * imageUrl;//電影的圖片
//歸檔
-(void)encodeWithCoder:(NSCoder *)aCoder;
//解檔
-(instancetype)initWithCoder:(NSCoder *)aDecoder;
@end
(2:方法的實現
#import "EncodeAndUnEncode.h"
#import <objc/runtime.h>
#import <objc/message.h>
@implementation EncodeAndUnEncode
//解
-(instancetype)initWithCoder:(NSCoder *)aDecoder{
if (self = [super init]) {
unsigned int count = 0;
Ivar * ivarlist = class_copyIvarList([self class], &count);
for (int i = 0; i<count; i++) {
Ivar alvar = ivarlist[i];
const char * name = ivar_getName(alvar);
id value = [aDecoder decodeObjectForKey:[NSString stringWithUTF8String:name]];
if (!value) {
}else{
[self setValue:value forKey:[NSString stringWithUTF8String:name]];
}
}
free(ivarlist);
}
return self;
}
//歸
-(void)encodeWithCoder:(NSCoder *)aCoder{
//獲取某個類的成員變數
unsigned int count = 0;
Ivar * ivarlist = class_copyIvarList([self class], &count);
for (int i = 0; i < count; i ++) {
Ivar alvar = ivarlist[i];
//獲取成員變數的名稱
const char * ivarname = ivar_getName(alvar);
id value = [self valueForKey:[NSString stringWithUTF8String:ivarname]];
if (!value) {
}else{
[aCoder encodeObject:value forKey:[NSString stringWithUTF8String:ivarname]];
}
}
free(ivarlist);
}
(2:呼叫
//歸檔的路徑
//如果不存在
NSString* docPatn = [NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES) lastObject];
NSString* path = [docPatn stringByAppendingPathComponent:@"moive.archiver"];
//自定義物件存到檔案中
[NSKeyedArchiver archiveRootObject:moive toFile:path];
//解檔
EncodeAndUnEncode * unmoive = [NSKeyedUnarchiver unarchiveObjectWithFile:path];
NSLog(@"獲取電影的資訊:電影:%@||電影類別:%@||電影地址:%@",unmoive.title,unmoive.genres,unmoive.imageUrl);
以上就是runtime在應用的基本場景,防止忘記,記錄一下