iOS进阶教程2-RUNLOOP优化大图加载

1 为什么要优化大图加载

大图片在渲染的时候,比较耗费时间
我们利用RUNLOOP 来优化:

思路:1.每一次RUNLOOP,都只加载一个小任物,把图片任务放到数组,从数组循环来加载.这样可以是项目达到流畅.
2.只加载当前视图内的图片任务
3.为了不让runloop休眠.我们要用一个timer区持有这个runloop 或者 通知注册runloop事件,让快要睡眠的时候去执行唤醒.

直接贴代码

#import "DWURunLoopWorkDistribution.h"
#import 

#define DWURunLoopWorkDistribution_DEBUG 1

@interface DWURunLoopWorkDistribution ()

@property (nonatomic, strong) NSMutableArray *tasks;

@property (nonatomic, strong) NSMutableArray *tasksKeys;

@property (nonatomic, strong) NSTimer *timer;

@end

@implementation DWURunLoopWorkDistribution

- (void)removeAllTasks {
    [self.tasks removeAllObjects];
    [self.tasksKeys removeAllObjects];
}

- (void)addTask:(DWURunLoopWorkDistributionUnit)unit withKey:(id)key{
    //将任务对应的添加到task 和 taskkeys数组数组中方便在注册方法中的callback中以待处理
    [self.tasks addObject:unit];
    [self.tasksKeys addObject:key];
   // NSLog(@"%@%@",unit,key);
    if (self.tasks.count > self.maximumQueueLength) {
        [self.tasks removeObjectAtIndex:0];
        [self.tasksKeys removeObjectAtIndex:0];
    }
}

- (void)_timerFiredMethod:(NSTimer *)timer {
    //We do nothing here
}

- (instancetype)init
{
    if ((self = [super init])) {
        _maximumQueueLength = 30;
        _tasks = [NSMutableArray array];
        _tasksKeys = [NSMutableArray array];
      //  _timer = [NSTimer scheduledTimerWithTimeInterval:0.1 target:self selector:@selector(_timerFiredMethod:) userInfo:nil repeats:YES];

    }
    return self;
}

+ (instancetype)sharedRunLoopWorkDistribution {

    static DWURunLoopWorkDistribution *singleton;
    static dispatch_once_t once;
    dispatch_once(&once, ^{
        singleton = [[DWURunLoopWorkDistribution alloc] init];
        [self _registerRunLoopWorkDistributionAsMainRunloopObserver:singleton];
    });
    return singleton;
}


+ (void)_registerRunLoopWorkDistributionAsMainRunloopObserver:(DWURunLoopWorkDistribution *)runLoopWorkDistribution {
    static CFRunLoopObserverRef defaultModeObserver;


    //kCFRunLoopBeforeWaiting  RUNLOOP 进入休眠的时候唤醒
    _registerObserver(kCFRunLoopBeforeWaiting, defaultModeObserver, NSIntegerMax - 999, kCFRunLoopDefaultMode, (__bridge void *)runLoopWorkDistribution, &_defaultModeRunLoopWorkDistributionCallback);
    /*
     1. kCFRunLoopDefaultMode: 默认 mode,通常主线程在这个 Mode 下运行。

     2. UITrackingRunLoopMode: 追踪mode,保证Scrollview滑动顺畅不受其他 mode 影响。

     3. UIInitializationRunLoopMode: 启动程序后的过渡mode,启动完成后就不再使用。

     4: GSEventReceiveRunLoopMode: Graphic相关事件的mode,通常用不到。

     5: kCFRunLoopCommonModes: 占位mode,作为标记DefaultMode和CommonMode用。

  */
}

// 验证下 这是什么调用方法  static
static void _registerObserver(CFOptionFlags activities, CFRunLoopObserverRef observer, CFIndex order, CFStringRef mode, void *info, CFRunLoopObserverCallBack callback) {
    CFRunLoopRef runLoop = CFRunLoopGetCurrent();
    CFRunLoopObserverContext context = {
        0,
        info,
        &CFRetain,
        &CFRelease,
        NULL
    };
//    switch (activities) {
//        case kCFRunLoopEntry:
//            NSLog(@"即将进入runloop");
//            break;
//        case kCFRunLoopBeforeTimers:
//            NSLog(@"即将处理timer");
//            break;
//        case kCFRunLoopBeforeSources:
//            NSLog(@"即将处理input Sources");
//            break;
//        case kCFRunLoopBeforeWaiting:
//            NSLog(@"即将睡眠");
//            break;
//        case kCFRunLoopAfterWaiting:
//            NSLog(@"从睡眠中唤醒,处理完唤醒源之前");
//            break;
//        case kCFRunLoopExit:
//            NSLog(@"退出");
//            break;
//    }
    observer = CFRunLoopObserverCreate(     NULL,
                                            activities,
                                            YES,
                                            order,
                                            callback,
                                            &context);
    CFRunLoopAddObserver(runLoop, observer, mode);

   //创建一个runloop观察者
   /*
    CFAllocatorRef allocator:内存分配
    CFOptionFlags activities :唤醒标志
    Boolean repeats:是否重复
    CFIndex order :优先级
    CFRunLoopObserverCallBack callout :回调 绑定
    CFRunLoopObserverContext *context  :传递
    */
    CFRelease(observer);
}





//typedef void (*CFRunLoopObserverCallBack)(CFRunLoopObserverRef observer, CFRunLoopActivity activity, void *info);
static void _runLoopWorkDistributionCallback(CFRunLoopObserverRef observer, CFRunLoopActivity activity, void *info)
{  //                             __bridge 类型 实现id类型与void*类型


    DWURunLoopWorkDistribution *runLoopWorkDistribution = (__bridge DWURunLoopWorkDistribution *)info;
    if (runLoopWorkDistribution.tasks.count == 0) {
        return;
    }
    BOOL result = NO;
    while (result == NO && runLoopWorkDistribution.tasks.count) {
        DWURunLoopWorkDistributionUnit unit  = runLoopWorkDistribution.tasks.firstObject;
        result = unit();
        [runLoopWorkDistribution.tasks removeObjectAtIndex:0];
        [runLoopWorkDistribution.tasksKeys removeObjectAtIndex:0];
    }
}


static void _defaultModeRunLoopWorkDistributionCallback(CFRunLoopObserverRef observer, CFRunLoopActivity activity, void *info) {
    NSLog(@"---------> Callback唤醒回调 当前状态CFRunLoopActivity[%lu]",activity);
    //CFRunLoopObserverRef 观察者
    //CFRunLoopActivity    RUNLOOP状态  kCFRunLoopBeforeWaiting
    // 接下来执行 _runLoopWorkDistributionCallback
    _runLoopWorkDistributionCallback(observer, activity, info);


//    kCFRunLoopEntry         = (1UL << 0), // 即将进入Loop
//    kCFRunLoopBeforeTimers  = (1UL << 1), // 即将处理 Timer
//    kCFRunLoopBeforeSources = (1UL << 2), // 即将处理 Source
//    kCFRunLoopBeforeWaiting = (1UL << 5), // 即将进入休眠
//    kCFRunLoopAfterWaiting  = (1UL << 6), // 刚从休眠中唤醒
//    kCFRunLoopExit          = (1UL << 7), // 即将退出Loop

}
@end

@implementation UITableViewCell (DWURunLoopWorkDistribution)

@dynamic currentIndexPath;

- (NSIndexPath *)currentIndexPath {
    NSIndexPath *indexPath = objc_getAssociatedObject(self, @selector(currentIndexPath));
    return indexPath;
}

- (void)setCurrentIndexPath:(NSIndexPath *)currentIndexPath {
    objc_setAssociatedObject(self, @selector(currentIndexPath), currentIndexPath, OBJC_ASSOCIATION_RETAIN_NONATOMIC);
    //这四个后面的参数分别表示:源对象,关键字,关联的对象和一个关联策略。
}

.h 文件

#import 

typedef BOOL(^DWURunLoopWorkDistributionUnit)(void);

@interface DWURunLoopWorkDistribution : NSObject

@property (nonatomic, assign) NSUInteger maximumQueueLength;

+ (instancetype)sharedRunLoopWorkDistribution;
- (void)addTask:(DWURunLoopWorkDistributionUnit)unit withKey:(id)key;

- (void)removeAllTasks;

@end

@interface UITableViewCell (DWURunLoopWorkDistribution)

@property (nonatomic, strong) NSIndexPath *currentIndexPath;

@end

代码分析来源于:
推荐一个第三方RunLoopWorkDistribution,地址https://github.com/diwu/RunLoopWorkDistribution

我们可以添加观察者 监听RUNLOOP的循环
// 添加一个监听者

- (void)addObserver {

    // 1. 创建监听者
    /**
     *  创建监听者
     *
     *  @param allocator#>  分配存储空间
     *  @param activities#> 要监听的状态
     *  @param repeats#>    是否持续监听
     *  @param order#>      优先级, 默认为0
     *  @param observer     观察者
     *  @param activity     监听回调的当前状态
     */
    CFRunLoopObserverRef observer = CFRunLoopObserverCreateWithHandler(kCFAllocatorDefault, kCFRunLoopAllActivities, YES, 0, ^(CFRunLoopObserverRef observer, CFRunLoopActivity activity) {

        /*
         kCFRunLoopEntry = (1UL << 0),          进入工作
         kCFRunLoopBeforeTimers = (1UL << 1),   即将处理Timers事件
         kCFRunLoopBeforeSources = (1UL << 2),  即将处理Source事件
         kCFRunLoopBeforeWaiting = (1UL << 5),  即将休眠
         kCFRunLoopAfterWaiting = (1UL << 6),   被唤醒
         kCFRunLoopExit = (1UL << 7),           退出RunLoop
         kCFRunLoopAllActivities = 0x0FFFFFFFU  监听所有事件
         */
        //NSLog(@"%@", [NSRunLoop currentRunLoop].currentMode); // 查看当前的RunLoop运行状态
        switch (activity) {
            case kCFRunLoopEntry:
                NSLog(@"RunLoop--进入");
                break;
            case kCFRunLoopBeforeTimers:
                NSLog(@"RunLoop--Timer事件");
                break;
            case kCFRunLoopBeforeSources:
                NSLog(@"RunLoop--Source事件");
                break;
            case kCFRunLoopBeforeWaiting:
                NSLog(@"RunLoop--休眠");
                break;
            case kCFRunLoopAfterWaiting:
                NSLog(@"RunLoop--唤醒");
                break;
            case kCFRunLoopExit:
                NSLog(@"RunLoop--退出");
                break;
            default:
                break;
        }
    });

    // 2. 添加监听者
    /**
     *  给指定的RunLoop添加监听者
     *
     *  @param rl#>       要添加监听者的RunLoop
     *  @param observer#> 监听者对象
     *  @param mode#>     RunLoop的运行模式, 填写默认模式即可
     */
    CFRunLoopAddObserver(CFRunLoopGetCurrent(), observer, kCFRunLoopDefaultMode);
}
- (void)viewDidLoad {
    [super viewDidLoad];
    [self addObserver]; // 添加监听者

}

看log
正常的RUNLOOP 是 睡眠 唤醒 一个循环结束
iOS进阶教程2-RUNLOOP优化大图加载_第1张图片

我们他RUNLOOP不休眠 一直在即将睡眠的时候唤醒
iOS进阶教程2-RUNLOOP优化大图加载_第2张图片

你可能感兴趣的:(iOS进阶教程)