ios advanced tutorial 2-large map loading of runloop optimization

Keywords: github

1 why to optimize large image loading

When rendering large pictures, it takes a lot of time
We use RUNLOOP to optimize:

Ideas: 1. Every time RUNLOOP loads only one small object, put the picture task into the array, and load from the array loop. This can make the project smooth
2. Load only the picture tasks in the current view
3. In order not to let runloop sleep, we need to use a timer area to hold the runloop or notify to register the runloop event, so that we can wake up when we are about to sleep

Paste code directly

#import "DWURunLoopWorkDistribution.h"
#import <objc/runtime.h>

#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{
    //Add the task corresponding to the task and taskkeys array to facilitate processing in the callback of the registration method
    [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;


    //Wake up when kcfrnloopbeforewaying runloop enters sleep
    _registerObserver(kCFRunLoopBeforeWaiting, defaultModeObserver, NSIntegerMax - 999, kCFRunLoopDefaultMode, (__bridge void *)runLoopWorkDistribution, &_defaultModeRunLoopWorkDistributionCallback);
    /*
     1. kCFRunLoopDefaultMode: The default Mode, in which the main thread usually runs.

     2. UITrackingRunLoopMode: Track mode to ensure that Scrollview slides smoothly and is not affected by other modes.

     3. UIInitializationRunLoopMode: After starting the program, the transition mode will not be used after starting.

     4: GSEventReceiveRunLoopMode: Graphic The mode of the related event, which is usually not used.

     5: kCFRunLoopCommonModes: Placeholder mode, used as the flags DefaultMode and CommonMode.

  */
}

// Verify what this is to call the method 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(@ "about to enter runloop");
//            break;
//        case kCFRunLoopBeforeTimers:
//            NSLog(@ "about to process timer");
//            break;
//        case kCFRunLoopBeforeSources:
//            NSLog(@ "about to process input Sources");
//            break;
//        case kCFRunLoopBeforeWaiting:
//            NSLog(@ "about to sleep");
//            break;
//        case kCFRunLoopAfterWaiting:
//            NSLog(@ "wake up from sleep, before processing the wake-up source");
//            break;
//        case kCFRunLoopExit:
//            NSLog(@ "exit");
//            break;
//    }
    observer = CFRunLoopObserverCreate(     NULL,
                                            activities,
                                            YES,
                                            order,
                                            callback,
                                            &context);
    CFRunLoopAddObserver(runLoop, observer, mode);

   //Create a runloop observer
   /*
    CFAllocatorRef allocator:memory allocation
    CFOptionFlags activities :Wakeup flag
    Boolean repeats:Is it repeated?
    CFIndex order :priority
    CFRunLoopObserverCallBack callout :Callback binding
    CFRunLoopObserverContext *context  :transmit
    */
    CFRelease(observer);
}





//typedef void (*CFRunLoopObserverCallBack)(CFRunLoopObserverRef observer, CFRunLoopActivity activity, void *info);
static void _runLoopWorkDistributionCallback(CFRunLoopObserverRef observer, CFRunLoopActivity activity, void *info)
{  //                             __bridge type implements id type and void * type


    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 Wake up callback current state CFRunLoopActivity[%lu]",activity);
    //CFRunLoopObserverRef observer
    //Cfrunloopactivity runloop status kcfrnloop before waiting
    // Next, execute "runLoopWorkDistributionCallback"
    _runLoopWorkDistributionCallback(observer, activity, info);


//    Kcfrnloop entry = (1ul < < 0), / / about to enter the Loop
//    Kcfrnloopbeforetimers = (1ul < 1), / / Timer to be processed
//    Kcfrnloopbeforesources = (1ul < 2), / / Source to be processed
//    Kcfrnloopbeforewaiting = (1ul < 5), / / going to sleep
//    Kcfrnloop after waiting = (1ul < 6), / / just woke up from sleep
//    Kcfrnloopexit = (1ul < < 7), / / about to exit 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);
    //The following four parameters are respectively: source object, keyword, associated object and an associated policy.
}

.h file

#import <UIKit/UIKit.h>

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

Code analysis comes from:
Recommend a third party RunLoopWorkDistribution, address https://github.com/diwu/RunLoopWorkDistribution

We can add a loop where observers listen to RUNLOOP
//Add a listener

- (void)addObserver {

    // 1. Create listener
    /**
     *  Create listener
     *
     *  @param allocator#>  Allocate storage space
     *  @param activities#> Status to monitor
     *  @param repeats#>    Whether to monitor continuously
     *  @param order#>      Priority, default is 0
     *  @param observer     Observer
     *  @param activity     Listen for the current status of the callback
     */
    CFRunLoopObserverRef observer = CFRunLoopObserverCreateWithHandler(kCFAllocatorDefault, kCFRunLoopAllActivities, YES, 0, ^(CFRunLoopObserverRef observer, CFRunLoopActivity activity) {

        /*
         kCFRunLoopEntry = (1UL << 0),          Entry into work
         kCFRunLoopBeforeTimers = (1UL << 1),   About to process Timers event
         kCFRunLoopBeforeSources = (1UL << 2),  About to process Source event
         kCFRunLoopBeforeWaiting = (1UL << 5),  About to sleep
         kCFRunLoopAfterWaiting = (1UL << 6),   Wake up
         kCFRunLoopExit = (1UL << 7),           Exit RunLoop
         kCFRunLoopAllActivities = 0x0FFFFFFFU  Listen to all events
         */
        //Nslog (@ '% @', [nsrunloop currentrunloop]. Currentmode); / / view the current RunLoop running status
        switch (activity) {
            case kCFRunLoopEntry:
                NSLog(@"RunLoop--Get into");
                break;
            case kCFRunLoopBeforeTimers:
                NSLog(@"RunLoop--Timer Event");
                break;
            case kCFRunLoopBeforeSources:
                NSLog(@"RunLoop--Source Event");
                break;
            case kCFRunLoopBeforeWaiting:
                NSLog(@"RunLoop--dormancy");
                break;
            case kCFRunLoopAfterWaiting:
                NSLog(@"RunLoop--awaken");
                break;
            case kCFRunLoopExit:
                NSLog(@"RunLoop--Sign out");
                break;
            default:
                break;
        }
    });

    // 2. Add listener
    /**
     *  Add listener to the specified RunLoop
     *
     *  @param rl#>       To add a listener's RunLoop
     *  @param observer#> Listener object
     *  @param mode#>     RunLoop Fill in the default mode
     */
    CFRunLoopAddObserver(CFRunLoopGetCurrent(), observer, kCFRunLoopDefaultMode);
}
- (void)viewDidLoad {
    [super viewDidLoad];
    [self addObserver]; // Add listener

}

Look at log
Normal RUNLOOP is the end of a cycle of sleep wake-up

We don't sleep. We wake up when we are about to sleep

Posted by uproa on Fri, 20 Mar 2020 09:35:17 -0700