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在应用的基本场景,防止忘记,记录一下
本文介绍了Objective-C运行时(runtime)的概念及其应用场景,包括方法交换、动态添加属性、字典转换为模型对象及归档解档操作。通过实例展示了如何利用runtime增强程序的灵活性。

7975

被折叠的 条评论
为什么被折叠?



