1. 程式人生 > >iOSruntime執行時的四種基本應用場景

iOSruntime執行時的四種基本應用場景

1:什麼是執行時(runtime)?

因為Objective-C是一門動態型語言,所以會把一些決定工作本來在編譯期完成的,放在執行的時候去做。這樣做的目的極大的增加的系統的靈活性。所以編譯器是不夠的,我們還需要一個執行時系統 (runtime system) 來執行在執行的時候需要執行的程式碼。這就是 Objective-C Runtime 系統存在的意義,它是整個OC執行框架的一塊基石。在這裡不做執行時底層的實現做說明,只總結一下執行時在應用中的4種基本場景。

2:在什麼場合使用執行時(runtime)?

(1):交換方法

      1:匯入標頭檔案

#import <objc/runtime.h>
      2:實現自定義的方法
+(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在應用的基本場景,防止忘記,記錄一下