zoukankan      html  css  js  c++  java
  • Runloop和Runtime的简单介绍

    Runloop :

    一、特性

    • iOS中所有的事件监听全部是由Runloop负责监听的,main线程的Runloop在应用启动的时候就会自动创建,其他子线程需要自己启动,不会自己创建Runloop
    • 线程和Runloop之间是一一对用的,其关系是保存在一个全局的字典里面,线程刚创建时,并没有Runloop,不主动获取,那么它一直不会有,Runloop的创建发生在第一次获取时。
    • Runloop并不是线程安全,所以需要避免在其他线程上调用当前线程的Runloop
    • Runloop负责管理autorelease pools,负责处理消息事件,如输入源事件、计时器事件,网络请求等
    • 通Runloop机制实现省电,流畅、响应速度快。用户体验好

    二、Runloop Mode

    苹果文档中提到的 Mode 有五个:

    • NSDefaultRunLoopMode:App默认的Model,主线程是在这个Model下运行的
    • NSConnectionReplyMode:该模式用来监控NSConnection对象。你通常不需要在你的代码中使用该模式(ios9.0已经废弃NSConnection了,由NSURLSession替代,所以这个应该然并卵了吧)
    • NSModalPanelRunLoopMode: 使用该模式来标识用于modal panel(模态面板)的事件。
    • NSEventTrackingRunLoopMode:界面跟踪Mode,用于ScrollView追踪触摸滑动,保证界面滑动时不受其他Mode影响。 (当我们滑动ScrollView,TableView等继承于ScrollView的控件是, 系统会切换模式为: UITrackingRunLoopMode, 跟踪你的触摸事件, 当停止滚动的时候, 系统会切换模式为: kCFRunLoopDefaultMode),
    • NSRunLoopCommonModes 这是一组可配置的通用模式。将input sources与该模式关联则同时也将input sources与该组中的其它模式进行了关联。对于Cocoa应用,该模式缺省的包含了default,modal以及event tracking模式。

    iOS公开出来的只有两个:


    mode.png

    一个常见的问题就是,主线程中一个NSTimer添加在default mode中,当界面上有一些scroll view的滚动频繁发生导致run loop运行在UItraking mode中,从而这个timer没能如期望那般的运行。所以,我们就可以把这个timer加到NSRunLoopCommonModes中来解决
    举个栗子: 如果有TableView上有轮播图(NSTimer), 则在滚动TableView的时候,定时器是不好使的, 因为添加定时器默认是在kCFRunLoopDefaultMode下的

    三、Runloop 应用

    如果我们将定时器放到UITrackingRunLoopMode
    模式下, 则只有在拖动的时候,定时器才可以工作, 代码如下:

    // 调用了scheduledTimer返回的NSTimer的定时器对象,已经被自动添加到当前的runLoop中(一个线程对应一个runloop,如果在子线程中添加定时器..添加到子线程的runloop中),默认为NSDefaultRunLoopMode模式
            let timer = Timer.scheduledTimer(timeInterval: 3, target: self, selector: #selector(printAction), userInfo: nil, repeats: true)
            RunLoop.current.add(timer, forMode: .UITrackingRunLoopMode)
    
            // 如果需要更改模式, 直接这样就可以
            RunLoop.current.add(timer, forMode: .commonModes)

    iOS 10 加入新闭包形式的写法,

    // NSDefaultRunLoopMode:NSTimer只有在默认模式下(NSDefaultRunLoopMode)工作,切换到其他模式不再工作,比如拖拽了界面上的某个控件(会切换成UITrackingRunLoopMode)
     let timer = Timer.init(timeInterval: 1, repeats: true) { (timer) in
               print("新timer执行了")
            }
            RunLoop.current.add(timer, forMode: .defaultRunLoopMode)

    CADisplayLink如果NSTimer一样, 也是添加到模式中

    Runtime:

    一、简介

    • 运行时是一种面向对象的编程语言环境,类似于java的虚拟机
    • OC最主要的特点就是在程序运行的时候,以发送消息的方式调用方法
    • 运行时是OC的核心、底层,OC就是基于运行时的
    • 日常工作中,主要应用场景是关联对象、可以给分类动态的添加属性;动态的获取类的属性,用于字典转模型
    • 交叉方法,在无法修改系统的或者第三方框架的方式时,利用交叉方法,先执行自己的方法,在交换执行三方框架的方法,

    二、功能实现

    • Runtime提供获取类信息的方法如下:

      获取类的方法.png

    通过以上的函数,可以获取类的属性、协议、成员变量、和方法

    • 关联对象

    利用关联对象,可以不用每次都调用运行时方法遍历获取属性列表,提高程序效率
    void objc_setAssociatedObject(id object, const void *key, id value, objc_AssociationPolicy policy)
    此方法用于动态创建属性,记录属性数组

    id objc_getAssociatedObject(id object, const void *key)
    调用运行性的方法,判断对象属性是否已经获取,如果获取直接返回

    1.动态获取类的属性列表

    /**
     获取类的属性列表数组
    
     @return 类的属性列表数组
     */
    + (NSArray *)yw_objPropertyArr {
    
        //从关联对象 中获取对象属性,如果有,直接返回
        /**
         参数:
         1 对象 self
         2 动态属性的key
         返回值 id 动态添加的 属性值
         */
        NSArray *ptyList = objc_getAssociatedObject(self, KPropertyListKey);
        if (ptyList) {
            return ptyList;
        }
    
        /**
         获取属性列表
         1.要获取的类
         2.类属性的个数指针
         返回值:所有的属性数组 C语言中,数组的名字,就是指向第一个元素的地址 
        在OC 中使用C的时候晕倒 retain/create/copy 等 需要release
         */
        unsigned int count = 0;
        //C语言数组 需要 * 符号
        objc_property_t *proArr = class_copyPropertyList([self class], &count);
        NSLog(@"属性的数量%d",count);
        //创建数组
        NSMutableArray *MArr = [NSMutableArray array];
        //遍历所有的属性
        for (unsigned int i = 0; i < count; i++) {
            //从数组中取得属性
            // C语言结构体指针,不要 *
            objc_property_t pty = proArr[i];
            //从pty 中获取属性的名称
            const char *cName = property_getName(pty);
            NSString *name = [NSString stringWithCString:cName encoding:NSUTF8StringEncoding];
            //添加到数组
            [MArr addObject:name];
        }
        //释放数组
        free(proArr);
    
        //到此为止,对象的属性数组获取完毕,利用关联对象,动态的添加属性
        /**
         1.对象 self
         2.动态添加属性的key,获取值的时候使用
         3.动态添加属性值
         4.对象的引用关系
         */
        objc_setAssociatedObject(self, KPropertyListKey, MArr.copy, OBJC_ASSOCIATION_RETAIN_NONATOMIC);
        return MArr.copy;
    }

    2.字典转模型

    /**
     给定一个字典,创建self类 对用的对象
     @param dic 字典
     @return 对象
     */
    + (instancetype)yw_objWithDic:(NSDictionary *)dic {
        //实例化对象
        id object = [[self alloc] init];
        //使用字典使用对象信息
        //获取self 是属性列表
        NSArray *proArr = [self yw_objPropertyArr];
        //遍历字典的方法
        [dic enumerateKeysAndObjectsUsingBlock:^(id  _Nonnull key, id  _Nonnull obj, BOOL * _Nonnull stop) {
            NSLog(@"key %@, value %@",key,obj);
            //判断key 是否在proArr中
            if ([proArr containsObject:key]) {
                //属性存在KVC 赋值
                [object setValue:obj forKey:key];
            }
        }];
        return object;
    }

    3.交叉方法

    举个例子,在imageView setImage的时候,会根据imageView的大小对图片进行缩放,这样的性能不是好,特别是在表格滚动的时候,这时,就可以使用交叉的黑魔法,通过上下文绘制的方法,解决这一问题:

    /**
     类被加载到运行时,就会执行
     */
    + (void)load {
        Method originalMethod = class_getInstanceMethod([self class], @selector(setImage:));
        Method swizzledMethod = class_getInstanceMethod([self class], @selector(yw_setImage:));
        method_exchangeImplementations(originalMethod, swizzledMethod);
    }
    - (void)yw_setImage:(UIImage *)image {
        NSLog(@"%s",__func__);
        //根据imageView大小,重新调整 image 大小
        UIGraphicsBeginImageContextWithOptions(self.bounds.size, YES, 0);
        //绘制图像
        [image drawInRect:self.bounds];
        //取得结果
        UIImage *result = UIGraphicsGetImageFromCurrentImageContext();
        //关闭上下文
        UIGraphicsEndImageContext();
        //此处 setImage 和 yw_setImage方法已经被交换
        //调用系统原生的setImage方法
        [self yw_setImage:result];
    }

    4.编码解码(用于归档)

    ///归档
    - (void)encode:(NSCoder *)aCoder{
        unsigned int outCount = 0;
        Ivar *ivars = class_copyIvarList([self class], &outCount);
        for (unsigned int i = 0; i < outCount; i++)
        {
            Ivar ivar = ivars[i];
            NSString *key = [NSString stringWithUTF8String:ivar_getName(ivar)];
            if ([self.ignoredIvarNames containsObject:key])
            {
                continue;
            }
            id value = [self valueForKey:key];
            [aCoder encodeObject:value forKey:key];
        }
        free(ivars);
    }
    ///解档
    - (void)decode:(NSCoder *)aDecoder{
        unsigned int outCount = 0;
        Ivar *ivars = class_copyIvarList([self class], &outCount);
        for (unsigned int i = 0; i < outCount; i++)
        {
            Ivar ivar = ivars[i];
            NSString *key = [NSString stringWithUTF8String:ivar_getName(ivar)];
            if ([self.ignoredIvarNames containsObject:key])
            {
                continue;
            }
            id value = [aDecoder decodeObjectForKey:key];
            [self setValue:value forKey:key];
        }
        free(ivars);
    }
    ///忽略数组
    - (void)setIgnoredIvarNames:(NSArray *)ignoredIvarNames{
        objc_setAssociatedObject(self,
                                 @selector(ignoredIvarNames),
                                 ignoredIvarNames,
                                 OBJC_ASSOCIATION_RETAIN_NONATOMIC);
    }
    
    - (NSArray *)ignoredIvarNames{
        return objc_getAssociatedObject(self, _cmd);
    }

    githubDemo:runtime01

    转自:iOS魏

  • 相关阅读:
    Android 2.2 r1 API 中文文档系列(11) —— RadioButton
    Android API 中文 (15) —— GridView
    Android 中文 API (16) —— AnalogClock
    Android2.2 API 中文文档系列(7) —— ImageButton
    Android2.2 API 中文文档系列(6) —— ImageView
    Android 2.2 r1 API 中文文档系列(12) —— Button
    Android2.2 API 中文文档系列(8) —— QuickContactBadge
    [Android1.5]TextView跑马灯效果
    [Android1.5]ActivityManager: [1] Killed am start n
    Android API 中文(14) —— ViewStub
  • 原文地址:https://www.cnblogs.com/LoveStoryJX/p/6548294.html
Copyright © 2011-2022 走看看