Good articles to my personal technology blog: https://cainluo.github.io/15062229631553.html


Last article, we briefly talked about some Tips to use GCD, if you do not watch the friends, you can play iOS development: combat development GCD Tips (a) look.

This time, we’ll continue with the tips.

Reprint statement: if you need to reprint this article, please contact the author, and indicate the source, and can not modify this article without authorization.


Flexible use of queue groups

When we use queue groups to perform tasks, this is usually the case:

- (void)queueGroup {

    dispatch_queue_t queue = dispatch_queue_create("queue", DISPATCH_QUEUE_CONCURRENT);
    dispatch_group_t group = dispatch_group_create();
    
    dispatch_group_async(group, queue, ^{
        NSLog(@" Execute task, current thread :%@"[NSThread currentThread]);
    });
}
Copy the code

Printed results:

2017-09-24 11:34:44.766052+0800 CMD-tips [59653:3482075] 2017-09-24 11:34:44.766606+0800 CMD-tips [59653:3482075] <NSThread: 0x604000464980>{number = 3, name = (null)}Copy the code

There are other ways to use dispatch_group_enter and dispatch_group_leave. Note that both methods are present at the same time:

- (void)gourpEnterAndLeave {
    
    dispatch_group_t group = dispatch_group_create();
    
    dispatch_group_enter(group);

    [self urlRequestSuccess:^{
        
        NSLog(@" Network request successful");
        
        dispatch_group_leave(group);
    } failure:^{
        
        NSLog(@" Network request failed");

        dispatch_group_leave(group);
    }];
}

- (void)urlRequestSuccess:(void(^)())success
                  failure:(void(^)())failure {
    
    success();
// failure();
}
Copy the code

Printed results:

2017-09-24 11:46:16.054410+0800 GCD-Tips[60002:3501228[enter into operation2017-09-24 11:46:16.054721+0800 GCD-Tips[60002:3501228The network request succeededCopy the code

This way, we can wrap up the network request, but be careful not to call both dispatch_group_leave at the same time, otherwise it will hang.

If we want to add an end task, we can do it in two ways:

  • dispatch_group_notify
    • It is called after the current queue group task has finished executingdispatch_group_notifyTo let you know the mission is over.
  • dispatch_group_wait
    • anddispatch_group_notifySimilar, except that you can add the delay end time, but there is a point to note here,dispatch_group_waitBlocks the current thread, so do not use theThe main threadOr it blocks the main thread.

Dispatch_barrier_ (a) Sync usage notes

We all know that dispatch_barrier_(a)sync is a barrier method that inserts a block into a queue and waits until that block is complete before executing another queue.

- (void)queueBarrier {
    
    dispatch_queue_t queue = dispatch_queue_create("queueBarrier", DISPATCH_QUEUE_CONCURRENT);
    
    dispatch_async(queue, ^{
        
        NSLog("Execute one, current thread :%@"[NSThread currentThread]);
    });
    
    dispatch_barrier_async(queue, ^{
        
        NSLog(@" Big guy is coming, current thread :%@"[NSThread currentThread]);
    });

    dispatch_async(queue, ^{
        
        NSLog(@" Execute two, current thread :%@"[NSThread currentThread]);
    });
}
Copy the code

Printed results:

2017-09-24 12:44:36.151126+0800 CMD-tips [61121:3585236] 2017-09-24 12:44:36.151579+0800 CMD-Tips [61121:3585334] <NSThread: 0x600000462E40 >{number = 3, name = (null)} 2017-09-24 12:44:36.152335+0800 CGC-TIPS [61121:3585334] Current thread :<NSThread: 0x600000462E40 >{Number = 3, name = (null)} 2017-09-24 12:44:36.154241+0800 CD/Tips[61121:3585334] 0x600000462e40>{number = 3, name = (null)}Copy the code

PS: dispatch_barrier_(a)syncIt’s only valid if it’s on a self-created concurrent queue, if it’s on a global concurrent queue, serial queue,dispatch_(a)syncThe effect is the same, in this case, it is easy to cause thread deadlock, so be careful here.


Dispatch_set_context and dispatch_set_finalizer_f

Two things to add here, dispatch_set_context and dispatch_set_finalizer_f.

  • Dispatch_set_context: Context data can be added to the queue
  • Dispatch_set_finalizer_f: transfers memory management rights

Note that dispatch_set_context accepts the context parameter as a C language parameter.

typedef struct _Info {
    int age;
} Info;

void cleanStaff(void *context) {
    
    NSLog(@"In clean, context age: %d", ((Info *)context)->age);
    
    // If the object is new, use delete
    free(context);
}

- (void)setContext {
    
    dispatch_queue_t queue = dispatch_queue_create("contextQueue", DISPATCH_QUEUE_SERIAL);
    
    // Initializes the Data object and sets the initialization value
    Info *myData = malloc(sizeof(Info));
    myData->age = 100;
    
    / / binding Context
    dispatch_set_context(queue, myData);
    
    // Set the finalizer function to release the context memory after the queue execution is complete
    dispatch_set_finalizer_f(queue, cleanStaff);

    dispatch_async(queue, ^{
        
        // Get the queue context data
        Info *data = dispatch_get_context(queue);
        / / print
        NSLog(@"1: context age: %d", data->age);
        // Modify the data saved by the context
        data->age = 20;
    });
}
Copy the code

Print the result:

2017-09-24 14:24:10.394129+0800 GCD-Tips[61881:3652088[enter into operation2017-09-24 14:24:10.394547+0800 GCD-Tips[61881:3652274] 1: context age: 100
2017-09-24 14:24:10.394738+0800 GCD-Tips[61881:3652274] In clean, context age: 20
Copy the code

PS: We diddispatch_set_contextBe sure to release it, or you’ll cause a memory leak.

In addition to that, we can do things with Core Foundation, so how do we do that?


NSObject and dispatch_set_context

Here we create a Model class that inherits NSObject:

@interface GCDModel : NSObject

@property (nonatomic.assign) NSInteger age;

@end

@implementation GCDModel

- (void)dealloc {
    
    NSLog(@"%@ released".NSStringFromClass([self class]));
}

@end
Copy the code

In this class, we simply operate with a single attribute and a dealloc method:

void cleanObjectStaff(void *context) {
    
    GCDModel *model = (__bridge GCDModel *)context;
    
    NSLog(@"In clean, context age: %ld", model.age);
    
    // Free memory
    CFRelease(context);
}

- (void)objectAndContext {
    
    dispatch_queue_t queue = dispatch_queue_create("objectQueue", DISPATCH_QUEUE_SERIAL);
    
    // Initializes the Data object and sets the initialization value
    GCDModel *model = [[GCDModel alloc] init];
    model.age = 20;
    
    // Bind Context, where the __bridge key is used
    dispatch_set_context(queue, (__bridge_retained void *)(model));
    
    // Set the finalizer function to release the context memory after the queue execution is complete
    dispatch_set_finalizer_f(queue, cleanObjectStaff);
    
    dispatch_async(queue, ^{
        
        // Get the queue context data
        GCDModel *model = (__bridge GCDModel *)(dispatch_get_context(queue));
        / / print
        NSLog(@"1: context age: %ld", model.age);
        // Modify the data saved by the context
        model.age = 120;
    });
}
Copy the code

Print the result:

2017-09-24 14:40:34.024509+0800 GCD-Tips[62448:3676807[enter into operation2017-09-24 14:40:34.024915+0800 GCD-Tips[62448:3676887] 1: context age: 20
2017-09-24 14:40:34.025236+0800 GCD-Tips[62448:3676887] In clean, context age: 120
2017-09-24 14:40:34.025706+0800 GCD-Tips[62448:3676887The GCDModel is releasedCopy the code

Here we explain the __bridge keyword:

  • __bridge: only type conversion, no memory management permission modification.
  • __bridge_retained: memory translation(CFBridgingRetain)And change the memory management permissions fromARCTake it to yourself and use it for final releaseCFReleaseTo release the object.
  • __bridge_transfer:Core FoundationConverted toObjective-Cobject(CFBridgingRelease)And give the memory management authorityARC.

Why take memory management into your own hands and not ARC?

The truth is simple, if ARC is in charge, your object will be released as soon as it detects that it is done.

You can’t add the Context to the queue, it will give you a wild pointer error, so we need to do it ourselves to make sure that ARC doesn’t release it.

The above code is also easy to interpret:

  • indispatch_set_contextWhen using__bridge_retainedTo convert, willContextThe memory management rights to our own hands for management.
  • In the case of queued tasks, we usedispatch_get_contextIn order to getcontextUse keywords__bridgeMake the switch, so that it can be maintainedcontextThe memory management rights remain unchanged, preventing out of scopeContextThey will be released.
  • In the end,CFReleaseTo releasecontextThis ensures that the memory is released without causing memory leaks.

conclusion

Ok, the extra GCD tips are almost here, if there are more tips will continue to update, welcome friends and I share other tips ~~

Here are a few recommended articles:

Grand Central Dispatch (GCD) Reference Concurrency Programming Guide Toll-Free Bridged Types


The project address

The address of the project: https://github.com/CainRun/iOS-Project-Example/tree/master/GCD-Tips/GCD-Tips-Two


The last