runtime.runloop

iOS开发 runtime应用

2020-12-14  本文已影响0人  喜剧收尾_XWX

1.runtime的作用

2.字典转模型

#import "NSObject+Json.h"
#import <objc/runtime.h>
#import <objc/message.h>

@implementation NSObject (Json)

+ (instancetype)xwx_initWithDictionaryForModel:(NSDictionary *)dic{
    
    id myObj = [[self alloc] init];
    unsigned int outCount;
    //获取类中的所有成员属性
    objc_property_t *arrPropertys = class_copyPropertyList([self class], &outCount);
    
    for (NSInteger i = 0; i < outCount; i ++) {
        
        //获取属性名字符串
        objc_property_t property = arrPropertys[i];
        //model中的属性名
        NSString *propertyName = [NSString stringWithUTF8String:property_getName(property)];
        id propertyValue = dic[propertyName];
        
        //处理服务器字段与本地model字段不匹配问题
        if ([propertyName isEqualToString:@"age3"]) {//处理本地与网络字符不匹配的问题
            propertyValue = dic[@"age"];
        }
    
        if (propertyValue != nil) {
            [myObj setValue:propertyValue forKey:propertyName];
        }
    }
    //注意在runtime获取属性的时候,并不是ARC Objective-C的对象所有需要释放
    free(arrPropertys);
    return myObj;
}
@end

3.动态修改成员变量

#import "UITextField+PlaceHolder.h"
#import <objc/runtime.h>

@implementation UITextField (PlaceHolder)

-(void)changePlaceHolderTextColor:(UIColor *)color{

//这块就是为了打印出所有的成员变量,获取成员变量的名
    unsigned int count;
    //获取UITextField中的所有成员变量
    Ivar *ivars = class_copyIvarList([UITextField class], &count);
    for (int i = 0; i < count; i++) {
    // 取出i位置的成员变量
    Ivar ivar = ivars[i];
   // NSLog(@"成员变量%s", ivar_getName(ivar));
    }
    free(ivars);
     
    // iOS 13 通过 KVC 方式修改私有属性,有 Crash 风险,谨慎使用!并不是所有KVC都会Crash,要尝试!
    if ([[UIDevice currentDevice].systemVersion floatValue] > 13.0) {
        //获取一个成员变量,根据名称获取
        Ivar ivar =  class_getInstanceVariable([UITextField class], "_placeholderLabel");
        //object_getIvar获取成员变量的值
        UILabel *placeholderLabel = object_getIvar(self, ivar);
        placeholderLabel.textColor = color;

    }else{
        //kvc赋值,iOS13之前用kvc赋值就可以哦
        [self setValue:color forKeyPath:@"_placeholderLabel.textColor"];
  }
}
@end
#import "UITextView+PlaceHolder.h"
#import <objc/runtime.h>

@implementation UITextView (PlaceHolder)



-(void)SetPlaceHolderTextColor:(UIColor *)color fontSize:(CGFloat)font textContent:(NSString *)text{
    
    
    UILabel *placeHolderLabel = [[UILabel alloc] initWithFrame:CGRectMake(0, 0, 100, 30)];
       placeHolderLabel.text = text;
       placeHolderLabel.numberOfLines = 0;
       placeHolderLabel.textColor = color;
    placeHolderLabel.font = [UIFont systemFontOfSize:font];
       [placeHolderLabel sizeToFit];
       
       if (self.text.length == 0) {
           [self addSubview:placeHolderLabel];//这句很重要不要忘了
       }
       
    [self setValue:placeHolderLabel forKey:@"_placeholderLabel"];

}

@end

值得注意的是,在iOS13之后使用KVC给成员变量赋值有可能崩溃,要试

4. 方法交换

参考链接 https://www.jianshu.com/p/e0d46032b27f

//
//  NSMutableArray+Extension.m
//  runtime
//
//  Created by eport on 2020/12/13.


#import "NSMutableArray+Extension.h"
#import <objc/runtime.h>

//方法交换
@implementation NSMutableArray (Extension)


//类方法,只在runtime调用一次,在APP运行时调用.+ (void)load. 方法只要加入了工程种,进行了编译,且.m中实现了这个方法,都会调用一次,值得注意的时没实现的子类是不会调用的,就算父类实现了也不行
+ (void)load
{
    static dispatch_once_t onceToken;
    dispatch_once(&onceToken, ^{
        // 类簇:NSString、NSArray、NSDictionary,真实类型是其他类型
        Class cls = NSClassFromString(@"__NSArrayM");
        Method method1 = class_getInstanceMethod(cls, @selector(insertObject:atIndex:));
        Method method2 = class_getInstanceMethod(cls, @selector(jf_insertObject:atIndex:));
        method_exchangeImplementations(method1, method2);
    });
}

- (void)jf_insertObject:(id)anObject atIndex:(NSUInteger)index{

    if (anObject == nil) return;//如果是空对象,就不添加,防止奔溃

    //拦截之后,再调用系统的实现,由于不知道底层实现逻辑,自己实现系统方法可能会出各种意想不到的错
    [self jf_insertObject:anObject atIndex:index];//这儿看似死循环,细理逻辑,方法实现已经交换过了的
}

@end

参考链接://https://www.jianshu.com/p/6bcff1f9feee

#import "UIViewController+Logging.h"
#import <objc/runtime.h>

@implementation UIViewController (Logging)

+ (void)load
{
    swizzleMethod([self class], @selector(viewDidAppear:), @selector(swizzled_viewDidAppear:));
}

- (void)swizzled_viewDidAppear:(BOOL)animated
{
    // call original implementation
    [self swizzled_viewDidAppear:animated];
    
    // Logging
    NSLog(@"%@", NSStringFromClass([self class]));
}

void swizzleMethod(Class class, SEL originalSelector, SEL swizzledSelector)
{
    // the method might not exist in the class, but in its superclass
    Method originalMethod = class_getInstanceMethod(class, originalSelector);
    Method swizzledMethod = class_getInstanceMethod(class, swizzledSelector);
    
    // class_addMethod will fail if original method already exists
    BOOL didAddMethod = class_addMethod(class, originalSelector, method_getImplementation(swizzledMethod), method_getTypeEncoding(swizzledMethod));
    
    // the method doesn’t exist and we just added one
    if (didAddMethod) {
        class_replaceMethod(class, swizzledSelector, method_getImplementation(originalMethod), method_getTypeEncoding(originalMethod));
    }
    else {
        method_exchangeImplementations(originalMethod, swizzledMethod);
    }
    
}
@end

#import "UIButton+DelaySwizzling.h"
#import <objc/runtime.h>

@interface UIButton()

// 重复点击间隔
@property (nonatomic, assign) NSTimeInterval xxx_acceptEventInterval;
// 上一次点击时间戳
@property (nonatomic, assign) NSTimeInterval xxx_acceptEventTime;

@end


@implementation UIButton (DelaySwizzling)

+ (void)load {
    static dispatch_once_t onceToken;
    dispatch_once(&onceToken, ^{
        Class class = [self class];

        SEL originalSelector = @selector(sendAction:to:forEvent:);
        SEL swizzledSelector = @selector(xxx_sendAction:to:forEvent:);

        Method originalMethod = class_getInstanceMethod(class, originalSelector);
        Method swizzledMethod = class_getInstanceMethod(class, swizzledSelector);

        BOOL didAddMethod = class_addMethod(class,
                                            originalSelector,
                                            method_getImplementation(swizzledMethod),
                                            method_getTypeEncoding(swizzledMethod));

        if (didAddMethod) {
            class_replaceMethod(class,
                                swizzledSelector,
                                method_getImplementation(originalMethod),
                                method_getTypeEncoding(originalMethod));
        } else {
            method_exchangeImplementations(originalMethod, swizzledMethod);
        }
    });
}

- (void)xxx_sendAction:(SEL)action to:(id)target forEvent:(UIEvent *)event {
    
    // 如果想要设置统一的间隔时间,可以在此处加上以下几句
    if (self.xxx_acceptEventInterval <= 0) {
        // 如果没有自定义时间间隔,则默认为 0.4 秒
        self.xxx_acceptEventInterval = 0.4;
    }
    
    // 是否小于设定的时间间隔
    BOOL needSendAction = (NSDate.date.timeIntervalSince1970 - self.xxx_acceptEventTime >= self.xxx_acceptEventInterval);
    
    // 更新上一次点击时间戳
    if (self.xxx_acceptEventInterval > 0) {
        self.xxx_acceptEventTime = NSDate.date.timeIntervalSince1970;
    }
    
    // 两次点击的时间间隔小于设定的时间间隔时,才执行响应事件
    if (needSendAction) {
        [self xxx_sendAction:action to:target forEvent:event];
    }
}

- (NSTimeInterval )xxx_acceptEventInterval{
    return [objc_getAssociatedObject(self, "UIControl_acceptEventInterval") doubleValue];
}

- (void)setXxx_acceptEventInterval:(NSTimeInterval)xxx_acceptEventInterval{
    objc_setAssociatedObject(self, "UIControl_acceptEventInterval", @(xxx_acceptEventInterval), OBJC_ASSOCIATION_RETAIN_NONATOMIC);
}

- (NSTimeInterval )xxx_acceptEventTime{
    return [objc_getAssociatedObject(self, "UIControl_acceptEventTime") doubleValue];
}

- (void)setXxx_acceptEventTime:(NSTimeInterval)xxx_acceptEventTime{
    objc_setAssociatedObject(self, "UIControl_acceptEventTime", @(xxx_acceptEventTime), OBJC_ASSOCIATION_RETAIN_NONATOMIC);
}

@end

5.给分类添加属性

文章参考 https://www.jianshu.com/p/916aef6f7ab1

//.h文件
#import <Foundation/Foundation.h>

NS_ASSUME_NONNULL_BEGIN

@interface NSObject (AssociatedObject)

@property(nonatomic,copy)NSString *associatedObject;

@end

NS_ASSUME_NONNULL_END
//.m文件
import "NSObject+AssociatedObject.h"
#import <objc/runtime.h>

@implementation NSObject (AssociatedObject)

- (void)setAssociatedObject:(id)associatedObject
{
    //注意最后一个参数的类型
    objc_setAssociatedObject(self, @selector(associatedObject), associatedObject, OBJC_ASSOCIATION_COPY);
}

- (id)associatedObject
{
    return objc_getAssociatedObject(self, _cmd);
}



@end

需求二: 给UIButton添加一个block处理点击事件
(1)写法1

#import <UIKit/UIKit.h>
#import <objc/runtime.h>    // 导入头文件

// 声明一个button点击事件的回调block
typedef void(^ButtonClickCallBack)(UIButton *button);

@interface UIButton (Handle)

// 为UIButton增加的回调方法
- (void)handleClickCallBack:(ButtonClickCallBack)callBack;

@end

#import "UIButton+Handle.h"

// 声明一个静态的索引key,用于获取被关联对象的值
static char *buttonClickKey;

@implementation UIButton (Handle)

- (void)handleClickCallBack:(ButtonClickCallBack)callBack {
    // 将button的实例与回调的block通过索引key进行关联:
    objc_setAssociatedObject(self, &buttonClickKey, callBack, OBJC_ASSOCIATION_RETAIN_NONATOMIC);
    
    // 设置button执行的方法
    [self addTarget:self action:@selector(buttonClicked) forControlEvents:UIControlEventTouchUpInside];
}

- (void)buttonClicked {
    // 通过静态的索引key,获取被关联对象(这里就是回调的block)
    ButtonClickCallBack callBack = objc_getAssociatedObject(self, &buttonClickKey);
    
    if (callBack) {
        callBack(self);
    }
}

@end

(2)写法二

#import <UIKit/UIKit.h>
#import <objc/runtime.h>  

typedef void(^ButtonClickCallBack)(UIButton * _Nullable button);

NS_ASSUME_NONNULL_BEGIN

@interface UIButton (Handler)

@property(nonatomic,copy)ButtonClickCallBack  bottonCallBack;

@end

NS_ASSUME_NONNULL_END
#import "UIButton+Handler.h"


@implementation UIButton (Handler)

- (void)setCallBlock:(ButtonClickCallBack)bottonCallBack
{
    objc_setAssociatedObject(self, @selector(callBlock), bottonCallBack, OBJC_ASSOCIATION_COPY_NONATOMIC);
}

- (ButtonClickCallBack )callBlock
{
    return objc_getAssociatedObject(self, _cmd);
    //    return objc_getAssociatedObject(self, @selector(callBlock));
}



@end

上一篇 下一篇

猜你喜欢

热点阅读