搜索
您的当前位置:首页正文

runtime在实际项目中的应用

来源:二三娱乐

平时觉得runtime都是大神用的,但是通过慢慢的学习,在实际项目中运用runtime,某些时候确实感觉挺牛逼的.
我就简单介绍下平时在我的项目中runtime的运用.

1.平时对于app账号信息的存储的时候,运用runtime实现归档和解档,也可以理解为字典转模型.

//解档
-(id)initWithCoder:(NSCoder *)decoder{
    self=[super init];
    if (self) {
        unsigned int count = 0;
        //获取类中所有成员变量名
        Ivar *ivar = class_copyIvarList([self class], &count);
        for (int i=0 ; i<count; i++) {
            Ivar iva=ivar[i];
            const char * name = ivar_getName(iva);
            NSString *strName = [NSString stringWithUTF8String:name];
            
            //进行解档取值
            id value = [decoder decodeObjectForKey:strName];
            
            //利用KVC对属性赋值
            [self setValue:value forKey:strName];
        }
        free(ivar);
    }
    return self;
}
//归档
-(void)encodeWithCoder:(NSCoder *)encoder{
    
    unsigned int count;
    Ivar *ivar=class_copyIvarList([self class], &count);
    for (int i=0; i<count; i++) {
        Ivar iva=ivar[i];
        const char *name = ivar_getName(iva);
        NSString *strName=[NSString stringWithUTF8String:name];
        //利用KVC取值
        id value =[self valueForKey:strName];
        [encoder encodeObject:value forKey:strName];
    }
    free(ivar);
}


继承与他的子类可以直接归档和解档

@interface LMAccount : LMArchiveModel
//账号信息
@property(nonatomic, copy)NSString *name;
@property(nonatomic, copy)NSString *mobile;
@property(nonatomic, copy)NSString *address;
@property(nonatomic, copy)NSString *email;
@property(nonatomic, copy)NSString *age;
@property(nonatomic, copy)NSString *userIcon;
@property(nonatomic, copy)NSString *signature;
@property(nonatomic, copy)NSString *userId;
@property(nonatomic, copy)NSString *state;
/**
 存储
 */
-(void)saveAccount;
/**
 取出账号信息

 @return 返回账号
 */
+(id)getAccount;


@implementation LMAccount
//存储
-(void)saveAccount{
    NSString *filePath=[[NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES) lastObject] stringByAppendingString:@"/LMAccount.data"];
    [NSKeyedArchiver archiveRootObject:self toFile:filePath];
    
}
//取出
+(id)getAccount{

    NSString *filePath=[[NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES) lastObject] stringByAppendingString:@"/LMAccount.data"];
    id account =[NSKeyedUnarchiver unarchiveObjectWithFile:filePath];
    return account;

}

2.在友盟统计页面停留时长的时候,我们可以runtime实现页面的统计,不用每个控制器都去写代码,解决了当页面较多时,需要修改的代码较多的问题

@implementation UIViewController (LMStatistics)
+(void)load{
    static dispatch_once_t onceToken;
    dispatch_once(&onceToken, ^{
        Method originalMethod1=class_getInstanceMethod([self class], @selector(viewWillAppear:));
        Method swizzledMethod1=class_getInstanceMethod([self class], @selector(lm_viewWillAppear:));
        
        Method originalMethod2=class_getInstanceMethod([self class], @selector(viewDidDisappear:));
        Method swizzledMethod2=class_getInstanceMethod([self class], @selector(lm_viewDidDisappear:));
        
        //交换实现
        method_exchangeImplementations(originalMethod1, swizzledMethod1);
        method_exchangeImplementations(originalMethod2, swizzledMethod2);

    });
}
-(void)lm_viewWillAppear:(BOOL)animated{
    ;
    NSLog(@"进入%@界面",NSStringFromClass([self class]));
    [self lm_viewWillAppear:animated];



}
-(void)lm_viewDidDisappear:(BOOL)animated{
    NSLog(@"离开%@界面",NSStringFromClass([self class]));
    [self lm_viewDidDisappear:animated];



}

+load vs. +initialize

Swizzling应该只在load方法中使用

oc会在运行时自动调用每个类的两个方法,+load 会在类初始化加载的时候调用;+initialize方法会在程序调用类的第一个实例或者类方法的时候调用。这两个方法都是可选的,只会在实现的时候才去调用。由于method swizzling会影响到全局的状态,因此最小化竞争条件的出现变得很重要,+load方法能够确保在类的初始化时候调用,这能够保证改变应用行为的一致性,而+initialize在执行时并不提供这种保证,实际上,如果没有直接给这个类发送消息,该方法可能都不会调用到。

dispatch_once

Swizzling应该只在dispatch_once中完成

如上,由于swizzling会改变全局状态,所以我们需要在运行时采取一些预防措施。原子性就是其中的一种预防措施,因为它能保证不管有多少个线程,代码只会执行一次。GCD的dispatch_once 能够满足这种需求,因此在method swizzling应该将其作为最佳的实践方式。

3.在项目中有很多的button是不可以重复点击的,用runtime可以很好的解决这个问题

@implementation UIButton (LMRetouch)
+(void)load{
    static dispatch_once_t onceToken;
    dispatch_once(&onceToken, ^{

        Method method1= class_getInstanceMethod([self class], @selector(sendAction:to:forEvent:));
        Method method2= class_getInstanceMethod([self class], @selector(customSendAction:to:forEvent:));
      
         method_exchangeImplementations(method1, method2);
    });
}
-(NSTimeInterval)timeInterval{
    return [objc_getAssociatedObject(self, _cmd) doubleValue];
    
    
}
-(void)setTimeInterval:(NSTimeInterval)timeInterval{
    objc_setAssociatedObject(self, @selector(timeInterval), @(timeInterval), OBJC_ASSOCIATION_RETAIN_NONATOMIC);
}
-(void)customSendAction:(SEL)action to:(id)target forEvent:(UIEvent *)event{
    if (self.isIgnore) {//设为YES,执行系统的方法
        [self customSendAction:action to:target forEvent:event];
        return;
    }
    if ([NSStringFromClass(self.class) isEqualToString:@"UIButton"]) {
        self.timeInterval=self.timeInterval==0? defaultTimeInterval:self.timeInterval;
        
        if (self.isIgnoreEvent) {//是否忽视此次事件
            return;
        }else if(self.timeInterval>0){
            [self performSelector:@selector(resetState) withObject:nil afterDelay:self.timeInterval];
        }
        self.isIgnoreEvent=YES;
        [self customSendAction:action to:target forEvent:event];
        
    }
}
//重置
-(void)resetState{
    
    
    self.isIgnoreEvent=NO;
    
    
}
#pragma mark  - 动态添加属性
-(void)setIsIgnoreEvent:(BOOL)isIgnoreEvent{
    objc_setAssociatedObject(self, @selector(isIgnoreEvent), @(isIgnoreEvent), OBJC_ASSOCIATION_RETAIN_NONATOMIC);
}
-(BOOL)isIgnoreEvent{
    
    return [objc_getAssociatedObject(self, _cmd) boolValue];
}
-(void)setIsIgnore:(BOOL)isIgnore{
    
    objc_setAssociatedObject(self, @selector(isIgnore), @(isIgnore), OBJC_ASSOCIATION_RETAIN_NONATOMIC);
    
    
    
}
-(BOOL)isIgnore{
    return [objc_getAssociatedObject(self, _cmd) boolValue];
}

4.项目中有的时候一个buttion的响应区域太小,交互不是很好,这个时候就需要增加button的响应区域

@interface UIButton (LMEnlargeTouchArea)
/**
 设置响应边距
 
 @param size 增加的边距
 */
-(void)setEnlargeEdge:(CGFloat)size;

/**
 设置响应边距(增加)
 
 @param top 上边距
 @param right 右边距
 @param bottom 下边距
 @param left 左边距
 */
-(void)setEnlargeEdgeWithTop:(CGFloat)top right:(CGFloat)right bottom:(CGFloat)bottom left:(CGFloat)left;
@implementation UIButton (LMEnlargeTouchArea)
static char topNameKey;
static char rightNameKey;
static char bottomNameKey;
static char leftNameKey;

//设置边距
#pragma mark  - setter
-(void)setEnlargeEdge:(CGFloat)size{
    
    objc_setAssociatedObject(self, &topNameKey, [NSNumber numberWithFloat:size], OBJC_ASSOCIATION_COPY_NONATOMIC);
    
    objc_setAssociatedObject(self, &bottomNameKey, [NSNumber numberWithFloat:size], OBJC_ASSOCIATION_COPY_NONATOMIC);
    
    objc_setAssociatedObject(self, &leftNameKey, [NSNumber numberWithFloat:size], OBJC_ASSOCIATION_COPY_NONATOMIC);
    
    objc_setAssociatedObject(self, &rightNameKey, [NSNumber numberWithFloat:size], OBJC_ASSOCIATION_COPY_NONATOMIC);
}

#pragma mark  - setter
-(void)setEnlargeEdgeWithTop:(CGFloat)top right:(CGFloat)right bottom:(CGFloat)bottom left:(CGFloat)left{
    //  需要添加关联的对象
    //  添加的唯一标识符
    //  关联的对象
    //  关联的策略,是个枚举
    
    objc_setAssociatedObject(self, &topNameKey, [NSNumber numberWithFloat:top], OBJC_ASSOCIATION_COPY_NONATOMIC);
    
    objc_setAssociatedObject(self, &bottomNameKey, [NSNumber numberWithFloat:bottom], OBJC_ASSOCIATION_COPY_NONATOMIC);
    
    objc_setAssociatedObject(self, &leftNameKey, [NSNumber numberWithFloat:left], OBJC_ASSOCIATION_COPY_NONATOMIC);
    
    objc_setAssociatedObject(self, &rightNameKey, [NSNumber numberWithFloat:right], OBJC_ASSOCIATION_COPY_NONATOMIC);
}
//getter
//获取新的rect
-(CGRect)enlargedRect{
    // 添加过关联的对象
    // 添加的唯一标识符
    NSNumber *topEdge =objc_getAssociatedObject(self, &topNameKey);
    NSNumber *bottomEdge =objc_getAssociatedObject(self, &bottomNameKey);
    NSNumber *leftEdge =objc_getAssociatedObject(self, &leftNameKey);
    NSNumber *rightEdge =objc_getAssociatedObject(self, &rightNameKey);
    
    if (topEdge && bottomEdge &&leftEdge && rightEdge) {
        
        
        return CGRectMake(self.bounds.origin.x-leftEdge.floatValue,
                          self.bounds.origin.y-topEdge.floatValue,
                          self.bounds.size.width+leftEdge.floatValue+rightEdge.floatValue,
                          self.bounds.size.height+topEdge.floatValue+bottomEdge.floatValue);
        
    }else{
        
        return self.bounds;
    }
    
}
#pragma mark  -  override
-(UIView *)hitTest:(CGPoint)point withEvent:(UIEvent *)event{
    CGRect rect=[self enlargedRect];
    if (CGRectEqualToRect(rect, self.bounds)) {
        return [super hitTest:point withEvent:event];
    }
    return CGRectContainsPoint(rect, point)? self:nil;
    
}

当然平时对于runtime的运用不止这些,很多耦合性比较高,就不写出来了,以后有增加的地方会补充的.

这个demo的github地址:

参考文档:

最后项目中的一些东西参考了github上部分项目,由于时间太久,都不知道在哪里了,就不说了

Top