iOS多线程总结(2)——GCD的使用

深入掌握iOS GCD多线程编程 本文还有配套的精品资源,点击获取 简介:GCD(Grand Central Dispatch)是iOS多线程编程的高效工具,简化了并发编程的复杂性,提供了灵活的异步执行机制。本教程详细介绍了GCD的基本概念、队列类型、任务提交方式以及工作模式,并通过示例讲解了如何创建队列、提交任务、延迟执行、实现栅栏任务以及使用小组件来跟踪任务。同时,提供了GCD最佳实践建议,如避免主线程... 阅读详情

本篇是多线程总结的第二篇,关于多线程的概念和NSThread的使用写在第一篇,查看请点击 《iOS多线程总结(1)——多线程相关概念及NSObject/NSThread的使用 》,本编主要讲解GCD的使用。

一. GCD简介

1. 什么是GCD

全称是Grand Central Dispatch,可译为“牛逼的中枢调度器”,纯C语言,提供了非常多非常强大的函数。

2. GCD的优势

GCD是苹果公司为多核的并行运算提出的解决方案,GCD会自动利用更多的CPU内核(比如双核、四核),GCD会自动管理线程的生命周期(创建线程、调度任务、销毁线程),程序员只需要告诉GCD想要执行什么任务,不需要编写任何线程管理代码。

3. GCD中两个核心概念

  • 任务:执行什么操作
  • 队列:用来存放任务

4. GCD的使用就三个步骤

  • 创建自定义队列或获取系统的全局队列。
  • 定制任务,即确定想做的事情。
  • 使用GCD提供的同步函数或异步函数将任务添加到队列中。
    即GCD底层会自动将队列中的任务取出,放到对应的线程中执行,任务的取出遵循队列的FIFO原则(先进先出,后进后出)。

5. GCD中执行任务的函数

  • 用同步的方式执行任务

    // queue:队列 block:任务
    dispatch_sync(dispatch_queue_t queue, dispatch_block_t block);

  • 用异步的方式执行任务

    // queue:队列 block:任务
    dispatch_async(dispatch_queue_t queue, dispatch_block_t block);

  • 同步和异步的区别

    • 同步:
      只能在当前线程中执行任务,不具备开启新线程的能力;
      在一个函数中添加任务,任务会顺序执行,且立马执行队列,执行完才返回来执行剩下的代码。

    • 异步:
      可以在新的线程中执行任务,具备开启新线程的能力;
      在一个函数中添加任务,这个函数执行完,才返回执行子线程中的队列。

6. GCD用于存放任务的队列

  • 并发队列(Concurrent Dispatch Queue),可以让多个任务并发(同时)执行(自动开启多个线程同时执行任务)并发功能只有在异步(dispatch_async)函数下才有效。

  • 串行队列(Serial Dispatch Queue),让任务一个接着一个地执行(一个任务执行完毕后,再执行下一个任务)。

(1). 并发队列

GCD默认已经提供了全局的并发队列,供整个应用使用,不需要手动创建,使用 dispatch_get_global_queue 函数可获得全局的并发队列

// 函数原型:
// priority:队列的优先级
// flags:此参数暂时无用,用0即可
dispatch_queue_t dispatch_get_global_queue(dispatch_queue_priority_t priority, unsigned long flags); 

// 使用举例
// 获得全局并发队列
dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0); 

// 全局并发队列的优先级
#define DISPATCH_QUEUE_PRIORITY_HIGH 2 // 高
#define DISPATCH_QUEUE_PRIORITY_DEFAULT 0 // 默认(中)
#define DISPATCH_QUEUE_PRIORITY_LOW (-2) // 低
#define DISPATCH_QUEUE_PRIORITY_BACKGROUND INT16_MIN // 后台

(2). 串行队列

GCD中获得串行有2种途径

  • 使用dispatch_queue_create函数创建串行队列
// 函数原型:
// label:队列名称
// attr:队列属性,一般用NULL即可
dispatch_queue_t dispatch_queue_create(const char *label, dispatch_queue_attr_t attr);

// 使用举例
// 创建名字为ss_thread的串行队列
dispatch_queue_t queue = dispatch_queue_create(“ss_thread”, NULL); 
// 非ARC需要释放手动创建的队列
dispatch_release(queue);
  • 使用主队列(跟主线程相关联的队列)

主队列是GCD自带的一种特殊的串行队列,放在主队列中的任务,都会放到主线程中执行,使用dispatch_get_main_queue()获得主队列。

//获得主队列
dispatch_queue_t queue = dispatch_get_main_queue();

7. 各种队列的执行效果

这里写图片描述

代码举例:

#import "ViewController.h"

@interface ViewController ()

@end

@implementation ViewController

/** 触摸屏幕执行 */
- (void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event {

    [self syncConcurrent];
}

/**
 * 同步函数 + 并发队列:不会创建线程,会在“当前线程”执行任务,任务是串行的,执行完一个任务,再执行下一个任务。
 */
- (void)syncConcurrent {

    // 1. 创建并发队列
//    dispatch_queue_t queue = dispatch_queue_create("ss_Concurrent", DISPATCH_QUEUE_CONCURRENT);

    // 1. 获取全局并发队列,DISPATCH_QUEUE_PRIORITY_DEFAULT:默认优先级
    dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);

    // 2. 通过同步函数添加任务到并发队列
    // 任务1
    dispatch_sync(queue, ^{

        for (int i = 0; i < 10; i++) {
            NSLog(@"%d----%@",i,[NSThread currentThread]);
        }
    });
    // 任务2
    dispatch_sync(queue, ^{

        for (int i = 0; i < 10; i++) {
            NSLog(@"%d----%@",i,[NSThread currentThread]);
        }
    });
    // 任务3
    dispatch_sync(queue, ^{

        for (int i = 0; i < 10; i++) {
            NSLog(@"%d----%@",i,[NSThread currentThread]);
        }
    });

}

/**
 * 同步函数 + 串行队列(主队列)
 */
- (void)syncMainSerial {

    // 注意:
    // 1. 如果当前线程是主线程,通过同步函数将任务添加到主队列,主线程就会陷入假堵塞状态,线程就会卡住不会往下执行。
    // 2. 如果当前线程不是主线程,通过同步函数将任务添加到主队列,主线程会按顺序执行任务。

    NSThread *thread = [[NSThread alloc] initWithTarget:self selector:@selector(run) object:nil];

    [thread start];

}

// 线程任务
- (void)run {

    // 1. 获取主队列
    dispatch_queue_t queue = dispatch_get_main_queue();

    // 2. 通过同步函数添加任务到主队列
    // 任务1
    dispatch_sync(queue, ^{

        for (int i = 0; i < 10; i++) {
            NSLog(@"%d----%@",i,[NSThread currentThread]);
        }
    });
    // 任务2
    dispatch_sync(queue, ^{

        for (int i = 0; i < 10; i++) {
            NSLog(@"%d----%@",i,[NSThread currentThread]);
        }
    });
    // 任务3
    dispatch_sync(queue, ^{

        for (int i = 0; i < 10; i++) {
            NSLog(@"%d----%@",i,[NSThread currentThread]);
        }
    });

}

/**
 * 同步函数 + 自定义串行队列:不会开启新的线程,在“当前线程”执行任务,任务是串行的,执行完一个任务,再执行下一个任务。
 */
- (void)syncCustomSerial {

    // 1. 创建串行队列
    dispatch_queue_t queue = dispatch_queue_create("ss_serial", DISPATCH_QUEUE_SERIAL);

    // 2. 通过同步函数添加任务到串行队列
    // 任务1
    dispatch_sync(queue, ^{

        for (int i = 0; i < 10; i++) {
            NSLog(@"%d----%@",i,[NSThread currentThread]);
        }
    });
    // 任务2
    dispatch_sync(queue, ^{

        for (int i = 0; i < 10; i++) {
            NSLog(@"%d----%@",i,[NSThread currentThread]);
        }
    });
    // 任务3
    dispatch_sync(queue, ^{

        for (int i = 0; i < 10; i++) {
            NSLog(@"%d----%@",i,[NSThread currentThread]);
        }
    });

}

/**
 * 异步函数 + 串行队列(主队列):不会开新线程,在主线程按任务的先后顺序执行。
 */
- (void)asyncMainSerial {

    // 1. 获取主队列
    dispatch_queue_t queue = dispatch_get_main_queue();

    // 2. 使用异步函数将任务添加到主队列
    // 任务1
    dispatch_async(queue, ^{

        for (int i = 0; i < 10; i++) {
            NSLog(@"%d----%@",i,[NSThread currentThread]);
        }

    });
    // 任务2
    dispatch_async(queue, ^{

        for (int i = 0; i < 10; i++) {
            NSLog(@"%d----%@",i,[NSThread currentThread]);
        }

    });
    // 任务3
    dispatch_async(queue, ^{

        for (int i = 0; i < 10; i++) {
            NSLog(@"%d----%@",i,[NSThread currentThread]);
        }

    });

}

/**
 * 异步函数 + 自定义串行队列:会开启新的线程,但只开一条新线程,任务是一个执行完再到下一个执行,先进先出后进后出原则。
 */
- (void)asyncCustomSerial {

    // 1. 创建一个串行队列
    dispatch_queue_t queue = dispatch_queue_create("ss_serial", DISPATCH_QUEUE_SERIAL);

    // 2. 使用异步函数将任务添加到串行队列
    // 任务1
    dispatch_async(queue, ^{

        for (int i = 0; i < 10; i++) {
            NSLog(@"%d----%@",i,[NSThread currentThread]);
        }

    });
    // 任务2
    dispatch_async(queue, ^{

        for (int i = 0; i < 10; i++) {
            NSLog(@"%d----%@",i,[NSThread currentThread]);
        }

    });
    // 任务3
    dispatch_async(queue, ^{

        for (int i = 0; i < 10; i++) {
            NSLog(@"%d----%@",i,[NSThread currentThread]);
        }
    });

}

/**
 * 异步函数 + 并发队列:可以开启多条线程,并发执行。
 */
- (void)asyncConcurrent {

    // 1. 创建一个并发队列
//    dispatch_queue_t queue = dispatch_queue_create("ss_queue", DISPATCH_QUEUE_CONCURRENT);

    // 1. 获取全局并发队列,DISPATCH_QUEUE_PRIORITY_DEFAULT:默认优先级
    dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);

    // 2. 使用异步函数将任务添加到并发队列
    // 任务1
    dispatch_async(queue, ^{

        for (int i = 0; i < 10; i++) {
            NSLog(@"%d----%@",i,[NSThread currentThread]);
        }

    });
    // 任务2
    dispatch_async(queue, ^{

        for (int i = 0; i < 10; i++) {
            NSLog(@"%d----%@",i,[NSThread currentThread]);
        }

    });
    // 任务3
    dispatch_async(queue, ^{

        for (int i = 0; i < 10; i++) {
            NSLog(@"%d----%@",i,[NSThread currentThread]);
        }

    });

}

@end

二. 线程通信

1. 从子线程回到主线程

dispatch_async(
dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
    // 执行耗时的异步操作...
      dispatch_async(dispatch_get_main_queue(), ^{
        // 回到主线程,执行UI刷新操作
        });
});

2. 从子线程传到子线程再传到主线程

- (void)communication {
    // 1. 创建一个串行队列
    dispatch_queue_t queue1 = dispatch_queue_create("serial_one", DISPATCH_QUEUE_SERIAL);
    // 2. 使用异步函数把任务添加到队列
    dispatch_async(queue1, ^{

        // 1. 创建url
        NSURL *url = [NSURL URLWithString:@"http://img.51ztzj.com/upload/image/20140917/sj201409171024_279x419.jpg"];

        // 2. 下载图片数据
        NSData *data = [NSData dataWithContentsOfURL:url];

        // 3. 生成图片
        UIImage *imageOne = [UIImage imageWithData:data];

        NSLog(@"%@",[NSThread currentThread]);


        // 3. 线程通信,子线程传给子线程
        // 3.1 创建一个串行队列
        dispatch_queue_t queue2 = dispatch_queue_create("serial_two", DISPATCH_QUEUE_SERIAL);
        // 3.2 使用异步函数把任务添加到队列
        dispatch_async(queue2, ^{

            UIImage *imageTwo = imageOne;
            NSLog(@"%@",[NSThread currentThread]);


            // 3.3 线程通信,把图片传回主线程
            dispatch_async(dispatch_get_main_queue(), ^{

                self.imageView.image = imageTwo;
                NSLog(@"%@",[NSThread currentThread]);
            });

        });

    });
}

三. 延时执行/线程栅栏

注意:

  • 延迟执行不等于线程堵塞,不能通过sleepUntilDate:方法和sleepForTimeInterval:方法设置延迟。
  • 线程栅栏的使用必须用在自定义的队列中才起作用,在全局队列中不起作用。

具体代码:

#import "ViewController.h"

@interface ViewController ()

// 开始时间
@property (nonatomic, assign) double begin;
// 结束时间
@property (nonatomic, assign) __block double end;

@end

@implementation ViewController

- (void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event {

    _begin = [NSDate timeIntervalSinceReferenceDate];

    // 延迟执行方式1
    // 延迟3秒执行
    dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(3 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{

        _end = [NSDate timeIntervalSinceReferenceDate];
        NSLog(@"时间间隔为%f秒",_end - _begin);
    });

    // 延迟执行方式2
    // 延迟2.5秒执行
    // TimeInterval:延迟执行时间
    // repeats:是否重复执行
//    [NSTimer scheduledTimerWithTimeInterval:2.5 target:self selector:@selector(run) userInfo:nil repeats:NO];

    // 延迟执行方式3
    // 延迟5秒执行
    // afterDelay:延迟执行时间
    [self performSelector:@selector(run) withObject:nil afterDelay:0];

    // 提示:performSelector:withObject:afterDelay:和NSTimer的延迟都是通过NSRunLoop实现的。
    // 注意:延迟执行不等于线程堵塞,不同通过sleepUntilDate:方法和sleepForTimeInterval:方法设置延迟。

}

- (void)run {

    _end = [NSDate timeIntervalSinceReferenceDate];
    NSLog(@"时间间隔为%f秒",_end - _begin);


    // 1. 创建一个并发队列
//    dispatch_queue_t queue = dispatch_queue_create("ss_concurrent", DISPATCH_QUEUE_CONCURRENT);
    // 1. 获取全局并发队列
    dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);

    // 2. 使用异步函数把任务添加到并发队列
    // 任务1
    dispatch_async(queue, ^{

        for (int i = 0; i < 10; i++) {
            NSLog(@"%d----%@",i,[NSThread currentThread]);
        }
    });
    // 任务2
    dispatch_async(queue, ^{

        for (int i = 0; i < 10; i++) {
            NSLog(@"%d----%@",i,[NSThread currentThread]);
        }
    });


    // 线程栅栏
    // 注意:线程栅栏的使用必须用在自定义的队列中才起作用,在全局队列中不起作用。
    dispatch_barrier_async(queue, ^{

        NSLog(@"------------------------------------");
    });


    // 任务3
    dispatch_async(queue, ^{

        for (int i = 0; i < 10; i++) {
            NSLog(@"%d----%@",i,[NSThread currentThread]);
        }
    });
    // 任务4
    dispatch_async(queue, ^{

        for (int i = 0; i < 10; i++) {
            NSLog(@"%d----%@",i,[NSThread currentThread]);
        }
    });

}

@end

四. 队列组

注意:
一定要注意函数的使用,必须是关于组队列的函数配合使用,例如:dispatch_group_async()函数和dispatch_group_notify()函数的配合,不能是是dispatch_async()函数和dispatch_group_notify()函数的配合。

具体代码:图片合成

#import "ViewController.h"

@interface ViewController ()

@property (weak, nonatomic) IBOutlet UIImageView *imageView;

@property (nonatomic, strong) UIImage *image1;

@property (nonatomic, strong) UIImage *image2;

@end

@implementation ViewController

- (void)viewDidLoad {
    [super viewDidLoad];

    // 1. 创建一个并发队列
    dispatch_queue_t queue = dispatch_queue_create("queue", DISPATCH_QUEUE_CONCURRENT);
    // 2. 创建一个组队列
    dispatch_group_t group = dispatch_group_create();

    // 3. 下载第一张相片
    // 通过异步函数把任务添加到并发队列
    dispatch_group_async(group, queue, ^{

        NSURL *url = [NSURL URLWithString:@"http://www.bz55.com/uploads/allimg/150429/140-1504291Z502-50.jpg"];
        NSData *data = [NSData dataWithContentsOfURL:url];
        _image1 = [UIImage imageWithData:data];
    });

    // 4. 下载第二张相片
    // 通过异步函数把任务添加到并发队列
    dispatch_group_async(group, queue, ^{

        NSURL *url = [NSURL URLWithString:@"http://img.taopic.com/uploads/allimg/110914/8879-11091422541844.jpg"];
        NSData *data = [NSData dataWithContentsOfURL:url];
        _image2 = [UIImage imageWithData:data];
    });


    // 5. 合成相片
    dispatch_group_notify(group, queue, ^{

        CGSize size = self.view.bounds.size;

        // 开启新的图像上下文
        UIGraphicsBeginImageContext(size);

        // 画图
        [_image1 drawInRect:CGRectMake(0, 0, size.width, size.height * 0.5)];
        [_image2 drawInRect:CGRectMake(0, size.height * 0.5, size.width, size.height * 0.5)];

        // 获取图片
        UIImage *image = UIGraphicsGetImageFromCurrentImageContext();

        // 结束图像上下文
        UIGraphicsEndImageContext();

        // 回到主线程显示图片
        // 注意,不要再子线程设置UI界面,可能会出错
        dispatch_async(dispatch_get_main_queue(), ^{
            // 设置图片
            _imageView.image = image;
        });

    });

}

@end

五. 快速遍历

  • 使用注意:遍历的对象必须是互不相干的,没有联系的对象。

  • 函数原型:

// iterations:遍历总数
// queue:队列
// size_t:索引的类型,需要索引,可以在使用的时候添加一个变量:如^(size_t index){}
void dispatch_apply(size_t iterations, dispatch_queue_t queue, void (^block)(size_t));
  • 具体使用:文件剪切
#import "ViewController.h"

@interface ViewController ()

@end

@implementation ViewController

- (void)viewDidLoad {
    [super viewDidLoad];

    // 获取一个全局并发队列
    dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);

    // 文件夹路径
    NSString *from = @"/Users/SJM/Desktop/From";
    NSString *to = @"/Users/SJM/Desktop/To";

    // 获取文件管理员
    NSFileManager *manager = [NSFileManager defaultManager];
    // 获取所有文件夹中的文件路径
    NSArray *pathArr = [manager subpathsAtPath:from];

    // 快速遍历
    dispatch_apply(pathArr.count, queue, ^(size_t index) {

        // 获取文件路径
        NSString *path = pathArr[index];

        // 拼接路径
        NSString *fromPath = [from stringByAppendingPathComponent:path];
        NSString *toPath = [to stringByAppendingPathComponent:path];

        // 剪切
        [manager moveItemAtPath:fromPath toPath:toPath error:nil];

        NSLog(@"%@----%@",[NSThread currentThread],path);
    });
}

@end

六. 一次性代码

使用dispatch_once函数能保证某段代码在程序运行过程中只被执行1次

使用举例:

static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
    // 只执行1次的代码(这里面默认是线程安全的)
});

函数解析:

  // dispatch_once_t实质是long的宏定义,定义的变量用于检查该代码块是否已经被调度,用于dispatch_once()函数
  static dispatch_once_t onceToken;

  // 声明一个Block变量,用于存储只执行1次的代码
  void (^block)() = ^{
    // 只执行1次的代码(这里面默认是线程安全的)
  };

  // dispatch_once()函数接收一个检查代码块是否已执行的变量和一个希望在应用的生命周期内仅被调度一次的代码块;
 // dispatch_once不仅意味着代码仅会被运行一次,而且还是线程安全的,如果被多个线程调用,该函数会同步等待直至代码块完成。
  dispatch_once(&onceToken,block);

七. 定时器

GCD的定时器与NSRunLoop的运行模式没有关系,即NSRunLoop的运行模式不管是NSDefaultRunLoopMode或者是UITrackingRunLoopMode,GCD定时器都会执行。

具体使用:

#import "ViewController.h"

@interface ViewController ()

@property (nonatomic, strong) dispatch_source_t timer;

@end

@implementation ViewController

- (void)viewDidLoad {
    [super viewDidLoad];

    // 创建定时器,(dispatch_source_t本质是OC对象)
    self.timer = dispatch_source_create(DISPATCH_SOURCE_TYPE_TIMER, 0, 0, dispatch_get_main_queue());

    // 设置定时器的实践参数,时间参数一般是纳秒(1秒 == 10的9次方纳秒)为单位
    // 何时开始
    dispatch_time_t start = dispatch_time(DISPATCH_TIME_NOW, (ino64_t)(1.0 * NSEC_PER_SEC));
    // 时间间隔
    uint64_t interval = (uint64_t)(2.0 * NSEC_PER_SEC);
    // 设置参数
    dispatch_source_set_timer(self.timer, start, interval, 0);

    // 设置回调,即设置需要定时器定时执行的操作
    dispatch_source_set_event_handler(self.timer, ^{

        NSLog(@"------");

    });

    // 启动定时器
    dispatch_resume(self.timer);

}

- (void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event {

    // 取消定时器
    dispatch_cancel(self.timer);
    self.timer = nil;
}

@end
三维空间刚体运动4-3:四元数线性插值方法:Squad 摘要:四元数插值三个方法Slerp、Squad和Spring既复杂又很重要,为了详细阐述,故每个方法独立成一篇博文讲解,没有插值的四元数是没有灵魂的,插值的重要性不言而喻。Slerp是经典的两点间一阶连续可导插值方法,Squad方法在Slerp的基础上实现多点间的一阶连续可导,而Spring则可以实现多点间的二阶连续可导,更适合复杂的曲线,此系列博文也是国内首篇介绍Spring的中文资料。 阅读详情

相关推荐

银河麒麟高级服务器操作系统V10SP2(X86)PXE无人值守批量部署实战指南

本文详细介绍了银河麒麟高级服务器操作系统V10SP2(X86)PXE无人值守批量部署的实战指南。通过PXE网络引导技术,实现全自动化、高效率的系统部署,特别适用于企业级数据中心和国产化替代项目。文章涵盖基础环境准备、关键服务配置、Kickstart高级定制及部署测试等核心内容,帮助用户快速掌握批量部署技巧。

weixin_42525637的博客 382

iOS列表性能优化之异步绘制

Python实战社群Java实战社群长按识别下方二维码,按需求添加扫码关注添加客服进Python社群▲扫码关注添加客服进Java社群▲作者 | Mkit来源 | 掘金https://j...

iOS_开发 464

告别卡顿!用LVGL+DMA刷新STM32F407的SPI屏幕,性能提升实战

本文详细介绍了如何通过LVGL+DMA技术优化STM32F407驱动SPI屏幕的性能,实现接近60FPS的流畅刷新。从硬件瓶颈分析到SPI与DMA的精细化配置,再到LVGL的双缓冲机制与局部刷新优化,提供了一套完整的性能提升方案。特别适合嵌入式GUI开发中面临卡顿问题的工程师参考。

weixin_42531588的博客 378

iOS绘图教程

本文是《Programming iOS5》中Drawing一章的翻译,考虑到主题完整性,翻译版本中加入了一些书中未涉及到的内容。希望本文能够对你有所帮助。(本文由海水的味道翻译整理,转载请注明译者和出处,请勿用于商业用途!原文)   Core Graphics Framework是一套基于C的API框架,使用了Quartz作为绘图引擎。它提供了低级别、轻量级、高保真度的2D渲染。该框架

南宫的专栏 709

JS逆向-快手__NS_hxfalcon参数分析

现在快手H5接口都需要__NS_hxfalcon这个参数,这篇文章就是逆向这个参数

ckcookies的博客 4073

CAD制图

CAD制图可以提高设计效率和准确性,并方便文档的存档和交流,是现代工程设计中不可或缺的一部分。·CAD三维开发是指使用计算机程序和工具来创建、编辑和操作三维模型、图形和设计的过程。它在工程、建筑、制造和游戏等领域广泛应用。CAD三维开发可能涉及到复杂的图形算法、性能优化、并行计算等方面的内容,因此需要深入学习和实践,以提高开发的效率和质量。

RZer的博客 1646

多线程

概念: //什么是进程? //1.进程是指在系统中正在运行的一个应用程序 //2.每个进程之间是独立的,每个进程均运行在其专用且受保护的内存空间内 //什么是线程? //1.1个进程想要执行任务,必须得有线程(每个进程至少要有1条线程) //2.线程是进程的基本执行单元,一个进程(程序)的所有任务都在线程中执行 //什么是多线程 //1个进程中可以开启多条线程,每条

zhongmin_li的博客 734

3GPP协议解读_NTN系列(一)_38.811_非地面网络(NTN)的背景、应用场景和信道建模

非地面网络覆盖广,对物理攻击和自然灾害有一定的抵抗性。本文根据3GPP协议对NTN网络的背景、应用场景和信道模型进行了整理。

JCMLSY的博客 4925

iOS多线程--下(GCD)

1 GCD它是一种纯C语言,它是为多核并列运算设计的。可以自动管理线程的生命周期。 GCD 是面向任务和队列的,不是面向线程的。他有两个关键字“任务”“队列”。 使用 GCD 的步骤主要是: 1 定制任务 2 任务添加到队列中,队列支持 FIFO 原则#基本形式如下dispath_queue_t queue = dispath_get_global_queue(DISPATCH_QUEUE_

smallyou的专栏 1676

Win11下Python脚本不执行?5分钟搞定PATH环境变量配置(VSCode终端实测)

本文针对Win11系统下VSCode终端中Python脚本执行无响应的问题,提供了详细的诊断与解决方案。核心在于正确配置PATH环境变量,通过图形化界面调整Python路径优先级,并分享了VSCode终端环境变量的刷新技巧,帮助开发者在5分钟内快速解决环境配置难题。

weixin_28358083的博客 384

【学习系列】SAP RAP 4:通过注解来完成一些常规需求

本文介绍了RAP应用中常用注解的实现方式,包括搜索帮助、文本描述绑定、联动搜索、字段控制等功能。通过@Consumption、@UI等注解可以实现搜索帮助定义、多字段联动、必输校验、默认排序和默认值设置。管理字段可通过@Semantics注解自动记录创建/修改信息,长文本使用@UI.multiLineText实现多行显示。这些注解可优化界面交互和数据管理,为后续行为定义功能开发奠定基础。文章结构清晰,针对不同需求提供了具体的实现方案和代码示例。

DeveloperMrMeng的博客 694

多线程 04---超级牛逼的GCD

忘记NSThread中关于线程的所有概念吧,现在只需要知道两样东西,就能解决问题:任务,队列1.基本概念:1.什么是GCD: 全称是Grand Central Dispatch,可译为“牛逼的中枢调度器” 纯C语言,提供了非常多强大的函数 2.GCD的优势: GCD是苹果公司为多核的并行运算提出的解决方案 GCD会自动利用更多的CPU内核(比如双核、四核) GCD会自动管理线程的生命周期(创建线程、

人的梦想是不会终结的 1612

YOLO系列专栏(五):YOLO26 模型部署实战 | 从训练完成到落地应用(CPU/GPU/移动端)

训练好的YOLO26模型,本质上是一个权重文件(.pt格式),无法直接在实际场景中使用——不同应用场景需要不同格式的模型(如ONNX、TensorRT、TFLite),不同设备(CPU/GPU/移动端)的部署方式也截然不同。在保证模型精度基本不变的前提下,最大化提升推理速度,适配不同硬件设备。如果部署不当,会出现以下问题:❌ 推理速度慢:CPU部署单帧推理耗时超过100ms,无法满足实时检测需求(如监控、自动驾驶);❌ 模型格式不兼容:直接使用.pt权重文件,无法在移动端、嵌入式设备上运行;

qq_36130719的博客 631

iOS-GCD多线程编程详解2

ios GCD多线程编程之dispatch_apply使用详解

980404514_凡凡的专栏 725

NTscan扫描工具

ntscan是一个扫描器,可以扫描没有密码和弱密码的共享,先到配置文件中填写猜测的用户名和密码,扫描过程中右侧出现的是扫描日志。

C# WinForm 基于DirectShow.dll编写的程序,可边预览边保存视频,音频和图片

基于微软的DirectShow.dll的二次开发1.自动识别连接的摄像头跟麦克风且可以对其进行选择(默认选中第一个)2.可录制wmv和avi两种格式的视频并且都带有音频,avi可以录制的分辨率和帧数进行选择,wmv默认进行了淸晰度优化如不喜欢可去除代码中有相关提示3.可在预览点开之后进行拍照,也可边录制视频边拍照4.添加了视频和音频的压缩功能(酌情使用)这次二次开发发现C#对DirectShow的相关信息太少,这次做完也不敢藏私共享出来,还望大家指正

IOS多线程之GCD(2)

一、主队列介绍 主队列:是和主线程相关联的队列,主队列是GCD自带的一种特殊的串行队列,放在主队列中得任务,都会放到主线程中执行。 提示:如果把任务放到主队列中进行处理,那么不论处理函数是异步的还是同步的都不会开启新的线程。 获取主队列的方式:  dispatch_queue_t queue=dispatch_get_main_queue(); (1)使用异步函数执行

tianzhipo5929的专栏 517

STM32F407中文手册

STM32f407中文手册 本参考手册面向应用开发人员,提供有关使用 STM32F405xx/07xx、STM32F415xx/17xx、 STM32F42xxx 和 STM32F43xxx 微控制器存储器与外设的完整信息。STM32F405xx/07xx、STM32F415xx/17xx、STM32F42xxx 和 STM32F43xxx 构成一个微 控制器系列,各产品具有不同的存储器大小、封装和外设。有关订购信息以及器件的机械与电气特性,请参见数据手册。有关 ARM Cortex™-M4F 内核的信息,请参见《Cortex™-M4F 技术参考手册》。

Xcode7.2NSThread、GCD多线程创建及简单使用

NSThread、GCD多线程创建及简单使用 【PS】:NSOpretion创建多线程的方式不常使用,本篇不做介绍。 一、NSThread创建多线程 1、线程创建方法 方法一: -(void)nsThreadCreat_Method1 { // NSLog(@"-----第一种:NSThread线程------"); NSThread *thread = [

Mlgbo的博客 1278

嵌入式系统考前速成:STM32环境搭建与高频考点全攻略

嵌入式系统是软硬件可裁剪、资源受限、强调实时可靠的专用计算机系统,广泛应用于物联网、工业控制和智能硬件领域。STM32作为基于Cortex-M3/M4内核的主流微控制器,凭借丰富的外设与成熟的开发生态,成为高校教学和工程实践的核心平台。理解存储器映射、时钟树、GPIO、中断、定时器与串口等基础原理,是掌握嵌入式开发的关键。对于面临期末考、补考、专升本或考研复试的备考者,高效搭建Keil开发环境、配置芯片支持包与ST-Link调试工具,能显著提升复习效率。本文以5小时速成路线为主线,梳理高频考点与可复用代码模

weixin_30399155的博客 314

iOS多线程--多线程之GCD2

线程、任务和队列的概念异步、同步 &amp; 并行、串行的特点一条重要的准则一般来说,我们使用GCD的最大目的是在新的线程中同时执行多个任务,这意味着我们需要两项条件:能开启新的线程任务可以同时执行结合以上两个条件,也就等价“开启新线程的能力 + 任务同步执行的权利”,只有在满足能力与权利这两个条件的前提下,我们才可以在同时执行多个任务。所有组合的特点(一)异步执行 + 并行队列实现代码:1234...

雨夜枫起的博客 650

[iOS 多线程 & 网络 - 1.2] - 多线程GCD

A.GCD基本使用 1.GCD的概念 什么是GCD全称是Grand Central Dispatch,可译为“牛逼的中枢调度器”纯C语言,提供了非常多强大的函数GCD的优势GCD是苹果公司为多核的并行运算提出的解决方案GCD会自动利用更多的CPU内核(比如双核、四核)GCD会自动管理线程的生命周期(创建线程、调度任务、销毁线程)程序员只需要告诉GCD想要执行什么任务,不需要编写任何线程...

daxiguan0304的博客 156

IOS线程数据篇2之多线程:NSThread NSOperation GCD(一)

栈空间是非常宝贵的,主线程只能开辟1M。子线程只有512K。android的栈空间好像只有2M。android的堆空间有16M、32M、64M的。 线程运行完之后,ARC下内存自动回收。线程不能被杀死,只能暂停。 优点:将耗时的任务分配到其他线程执行,由主线程负责统一更新界面会使应用程序更加流畅,用户体验更好 缺点:新建线程会消耗内存空间和CPU时间,线程太多会降低系统的运行性能。 多线程

关注IOS、Android网络、音视频编解码、特效、Neon算法优化,DSP等嵌入式驱动开发算法移植 1009

iOS开发之多线程(2)—— Thread

版本 Xcode 11.5 Swift 5.2.2 简介 一个Thread即为一个线程. 方法属性 OC中的属性方法(Swift方法名类似): #pragma mark - 属性 // 可以使用返回的字典来保存线程的特定数据. (这只是一个普通的字典, 用来保存所有开发者感兴趣的数据.) @property (readonly, retain) NSMutableDictionary *threadDictionary; // 线程优先级 @property double threadPriority A

看影成痴 427

iOS开发之多线程(4)—— Operation

目录版本简介操作同步 & 异步队列串行 & 并行基本用法1. 不使用队列2. 使用队列2.1 四种添加操作(任务)方法2.2 其他属性方法2.3 线程间通信 版本 Xcode 11.5 Swift 5.2.2 简介 我们先来讨论相关理论知识点, 代码部分统一放到后文. Operation基于GCD封装, 完全面向对象. 对应于GCD, Operation也有任务和队列的概念, 只不过在Operation中任务(block)被称为操作(operation). Operation支持以下关键功能

看影成痴 959

iOS多线程开发

1:首先简单介绍什么叫线程可并发执行的,拥有最小系统资源,共享进程资源的基本调度单位。共用堆,自有栈(官方资料说明iOS主线程栈大小为1M,其它线程为512K)。并发执行进度不可控,对非原子操作易造成状态不一致,加锁控制又有死锁的风险。2:IOS中的线程iOS主线程(UI线程),我们的大部分业务逻辑代码运行于主线程中。没有特殊需求,不应引入线程增加程序复杂度。应用场景:逻辑执行时间过长,严重影响交

zhenghaikun的专栏 444

iOS的三种多线程技术NSThread/NSOperation/GCD

1.iOS的三种多线程技术 1.NSThread每个NSThread对象对应一个线程,量级较轻(真正的多线程) 2.以下两点是苹果专门开发的“并发”技术,使得程序员可以不再去关心线程的具体使用问题 NSOperation/NSOperationQueue面向对象的线程技术 GCD —— Grand Central Dispatch(派发) 是基于C语言的框架,可以充分利用多核,是...

weixin_30877755的博客 125

iOS面试:4.多线程GCD

进程是指在系统中正在运行的一个应用程序。对于电脑而已,你打开一个软件,就相当于开启了一个进程。对于手机而已,你打开了一个APP,就相当于开启了一个进程。线程是进程的基本执行单位。一个进程中至少会有一条线程,当然也可能会有多条线程。比如你使用QQ音乐听歌,系统会创建一条线程去播放音乐。使用QQ音乐下载歌曲,系统会创建一条线程去下载歌曲。这两个操作是可以同时进行的,也就说一个进程中可以同时运行多条线程。

长沙火山 3062

iOS - 多线程-GCD

GNUstep是GNU计划的项目之一,它将Cocoa的OC库重新开源实现了一遍。虽然GNUstep不是苹果官方源码,但还是具有一定的参考价值。队列(Concurrent Dispatch Queue)看似是在没有延迟的情况下执行,实际上因为该方法是基于。队列(Serial Dispatch Queue)添加一个定时器,但是因为此时我们是在。这时候都有打印,只不过打印顺序。主要影响:能不能开启新的线程。主要影响:任务的执行方式。上面的例子中,主要是考察。,这次打印顺序是正常的。

sharp521的博客 1231

iOS GCD的基本使用

简而言之,就是 创建任务--创建对列-->把任务放到对列里.同步执行(sync)和异步执行(async)。串行对列,并行对列(注意:并行对列并不是说有多个对列并行,不管串行对列还是并行对列,都只有一个对列,对列里放的任务是串行的,叫串行对列,对列里放的任务是并行的,叫并行对列)所以理论上任务和对列有4种组合方案:1.串行对列里添加同步任务;2.串行对列里添加异步任务;3.并行对列里添加同步任务;

wyz670083956的博客 1831
上一篇: iOS多线程总结(1)——多线程相关概念及NSObject/NSThread的使用
下一篇: iOS多线程总结(3)——NSOperation与NSOperationQueue的使用
活在生活里
博客等级 码龄11年 94粉丝 82原创
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

当前余额3.43元 前往充值 >
需支付:10.00元
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付元
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值