多线程 线程安全

2023-05-16

多线程的安全隐患

我们用多线程有很多好处,但是也存在安全隐患
资源共享
1块资源可能会被多个线程共享,也就是多个线程可能会访问同一块资源
比如多个线程访问同一个对象、同一个变量、同一个文件
当多个线程访问同一块资源时,很容易引发数据错乱和数据安全问题

例子多线程卖票的

- (void)saleTickets
{
    self.tickets = 100;
    dispatch_async(dispatch_get_global_queue(0, 0), ^
    {
        for (int i = 0; i < 5; i++)
        {
            [self sale];
        }
    });
    dispatch_async(dispatch_get_global_queue(0, 0), ^
    {
        for (int i = 0; i < 5; i++)
        {
            [self sale];
        }
    });
    dispatch_async(dispatch_get_global_queue(0, 0), ^
    {
        for (int i = 0; i < 5; i++)
        {
            [self sale];
        }
    });
}

- (void)sale
{
    int oldTickets = self.tickets;
    sleep(.2);
    oldTickets--;
    self.tickets = oldTickets;
    NSLog(@"还剩余%i张票", self.tickets);
}

产生的原因

隐患产生的原因

线程同步技术,就是线程访问同一个资源,需要按照特定的次序
解决方案:使用线程同步技术(同步,就是协同步调,按预定的先后次序进行)
常见的线程同步技术是:加锁

加锁

11OSSpinLock01

自旋锁,所有的线程用同一把锁,才能达到加锁的目的等待锁的线程会处于忙等(busy-wait)状态,一直占用着CPU资源
目前已经不再安全,可能会出现优先级反转问题
如果等待锁的线程优先级较高,它会一直占用着CPU资源,优先级低的线程就无法释放锁

需要导入头文件

import <libkern/OSAtomic.h>

@property (nonatomic, assign)  OSSpinLock *lock;
    self.lock = OS_SPINLOCK_INIT;
- (void)sale
{
    OSSpinLockLock(&_lock);
    int oldTickets = self.tickets;
    sleep(.2);
    oldTickets--;
    self.tickets = oldTickets;
    NSLog(@"还剩余%i", oldTickets);
    OSSpinLockUnlock(&_lock);
}

存钱和取钱也是共用一把锁
IOS10之后就过期了

12OSSpinLock02

让线程阻塞,1让线程睡觉,2忙等状态
尝试加锁

尝试加锁

14答疑问

用不用加锁 要看是不是多条线程同时访问同一个资源,也要看访问了是干嘛,如果没有修改的话是不需要的

15 os_unfair_lock

用于取代不安全的OSSpinLock ,从iOS10开始才支持
从底层调用看,等待os_unfair_lock锁的线程会处于休眠状态,并非忙等
需要导入头文件#import <os/lock.h>
如果忘记解锁了,线程会一直在那里睡眠,其他的线程拿不到锁,这就是一个死锁。

//初始化锁
       self.moneyLock = OS_UNFAIR_LOCK_INIT;
    //尝试加锁
   os_unfair_lock_trylock(&_ticketLock);
    //加锁
    os_unfair_lock_lock(&_ticketLock);
    //解锁
    os_unfair_lock_unlock(&_ticketLock);

16pthread_mutex01

pthread开头的一半都是通用的
mutex叫做”互斥锁”,等待锁的线程会处于休眠状态
需要导入头文#import <pthread.h>

//创建一个锁的属性
    pthread_mutexattr_t attr;
    //初始化属性
    pthread_mutexattr_init(&attr);
    //设置属性类型
    pthread_mutexattr_settype(&attr, PTHREAD_MUTEX_DEFAULT);
    //出初始化锁
    pthread_mutex_t Lock;
    pthread_mutex_init(&lock, &attr);
//加锁
 pthread_mutex_lock(&_moneyLock);
    //解锁
    pthread_mutex_unlock(&_moneyLock);
//销毁相关资源
    pthread_mutex_destroy(&_ticketLock);
    pthread_mutexattr_destroy(&attr);

//锁的类型
/*
 * Mutex type attributes
 */
#define PTHREAD_MUTEX_NORMAL        0
#define PTHREAD_MUTEX_ERRORCHECK    1
#define PTHREAD_MUTEX_RECURSIVE     2
#define PTHREAD_MUTEX_DEFAULT       PTHREAD_MUTEX_NORMAL

17-pthread_mutex02-递归锁

pthread_mutex这种锁我们用完要记得手动销毁

实验一下在这个锁里面再加一个自己的锁,会出现什么情况?
死锁,
或者递归里面有这个锁,都会造成死锁
第一个情况,只需要换一把锁就可以解决
如果是递归,无法换锁,这种情况可以换成递归锁,只需要将这把锁的属性换成PTHREAD_MUTEX_RECURSIVE,他就可以重复加锁。
如果不痛的线程都来访问这个锁的疑问,递归锁是允许同一个线程,对一个锁重复加锁。 如果其他的线程来访问的时候已经加锁了,就不行再加锁,只能等当前线程解锁之后才可以加锁或者访问

//创建一个递归锁
/创建一个锁的属性
    pthread_mutexattr_t attr;
    //初始化属性
    pthread_mutexattr_init(&attr);
    //设置属性类型
    pthread_mutexattr_settype(&attr, PTHREAD_MUTEX_RECURSIVE);
    //出初始化锁
    pthread_mutex_t Lock;
    pthread_mutex_init(&lock, &attr);

8-自旋锁、互斥锁汇编分析

实验,线程在等在锁的时候在做什么事情?
OSSpinLock
让线程sleep时间长一点,然后过掉第一个端点,查看
用lldb-step,他是执行一个oc的指令,可能是几行汇编代码,想要他执行一个汇编代码stepi->si简写,可以看到他一直在执行一个while循环,
pthread_mutex_lock 互斥锁
lldb-c可以快跳到下一个端点
通过上面的方法我们找到底层执行的一个函数mutexwait里面又个syscall,调用了系统级别的函数,执行之后,线程不做事情了,休眠了
os_unfair_lock
他也会执行systcall去休眠,其实他也是互斥锁
low_level lock低级锁,等不到锁就会休眠

19-pthread_mutex03-条件

pthread_mutex在我们创建锁的时候,传进来的type决定什么锁
条件
以前我们加锁的时候很简单,就是等待别的锁放开之后才可以加锁,条件也是另外一条件来放开这把锁.
假如 我们有两条线程同时对一个数组操作,一个添加对象,一个删除对象,我们想哟在删除的时候判断一个只有不是0的时候才可以删除。这样就可以加上一个条件,如果不符合条件,线程就是会休眠,并且放开,刚刚加的锁。如果其他的线程添加了元素可以发送信号来唤醒条件休眠的线程,
使用场景可以用来实现线程等待

20-NSLock、NSRecursiveLock、NSCondition

NSLock就是对mutex普通锁的封装

NSLock
//初始化锁
        self.ticketLock = [[NSLock alloc] init];
        self.moneyLock = [[NSLock alloc] init];
//加锁
 [self.moneyLock lock];
//解锁
    [self.moneyLock unlock];

这种方式更加的面向对象,
可以使用打断点看汇编的方式,查看他底层调用的什么,也可以是gunstep开源的里面查看,对NSLock是对mutex普通锁的封装
NSRecursiveLock也是对mutex递归锁的封装,API跟NSLock基本一致

  • NSCondition
    NSCondition是对mutex和cond的封装

    NSCondition
self.condition= [[NSCondition alloc] init];
        _arr = [NSMutableArray array];
//添加对象
//加锁
 [self.condition lock];
    [self.arr addObject:@"nihao"];
    NSLog(@"添加了元素");
//通知condition
    [self.condition signal];
//解锁
    [self.condition unlock];
//删除对象
//加锁
[self.condition lock];
    if (self.arr.count == 0)
    {
        //进入休眠
        [self.condition wait];
    }
    [self.arr removeLastObject];
    NSLog(@"删除了元素");
  //解锁
    [self.condition unlock];

21答疑

解答删除在添加的后面,多线程不确定谁先执行的时候,也是闲添加再删除,因为又个条件等待

22多线程遗留问题

[condition signal]是不会可以放外边更好,如果两个代码很近中间没有很多的操作,其实都可以,

runloop相关的问题
线程的任务一旦执行完毕,生命周期就结束了,这个线程就无法再使用了,虽然他还在内存中,但是他已经不是激活状态了,还是不能做事情了。可以用手动开启runloop来让线程存活,处于激活状态
为什么不用强指针,而使用runloop
这里说的线程的命,是保证线程的生命周期,保证线程处于激活状态,
dispatch_get_global_queue

//1
[self performSelector:<#(nonnull SEL2)#> withObject:<#(nullable id)#> afterDelay:<#(NSTimeInterval)#>];
//3

为什么打印1和3可以,而2不可以,因为1和3不需要runloop的支持的,2的方法本质是讲一个定时器添加到runloop上面,但是子线程的runloop默认是没有开启的,所以不能打印
主线程的runloop默认是打开的,所以主线的UI刷新,点击时间,performSelector,普通的代码平不是交给runloop的

23-NSconditionLock

利用conditionLock可以实现线程之间等待,

//创建一        
NSConditionLock *condition;
       self.condition= [[NSConditionLock alloc] init];//初始化的时候如果不指定condition的话,他默认就是0
        self.condition = [[NSConditionLock alloc] initWithCondition:1];

//加锁
- (void)addPerosn
{
    [self.condition lockWhenCondition:2];
    NSLog(@"2");
    [self.condition unlockWithCondition:3];
}

- (void)deletePerosn
{
    [self.condition lockWhenCondition:1];
    
    NSLog(@"1");

    [self.condition unlockWithCondition:2];
    
}

{
    [self.condition lockWhenCondition:3];
    
    NSLog(@"3");

    [self.condition unlock];
    
}

24-SerialQueue

直接使用GCD的串行队列,也是可以实现线程同步的
iOS中的线程同步方案

OSSpinLock
    os_unfair_lock
    pthread_mutex
    dispatch_semaphore
    dispatch_queue(DISPATCH_QUEUE_SERIAL)
    NSLock
    NSRecursiveLock
    NSCondition
    NSConditionLock
    @synchronized

线程同步的本质就是不能让多条线程同时占用一块资源,让他们安顺序来访问,利用串行队列

26semaphore01-最大并发数量

semaphore信号量,
信号量的初始值,可以用来控制线程并发访问的最大数量,比如卖票,希望有三个线程在卖票。
信号量的初始值为1,代表同时只允许1条线程访问资源,保证线程同步

初始化
 self.semaphore = dispatch_semaphore_create(5);//最大并发数
- (void)saveMoney
{
    for (int i = 0; i < 20; i++)
    {
        //创建20条子线程
        [[[NSThread alloc] initWithTarget:self selector:@selector(drawMoney) object:nil] start];
        
    }
}
- (void)drawMoney
{
    dispatch_semaphore_wait(self.semaphore, DISPATCH_TIME_FOREVER);
    NSLog(@"%@", [NSThread currentThread]);
    dispatch_semaphore_signal(self.semaphore);
}

26-semaphore02-线程同步

dispatch_semaphore_wait(),
做的事情若果信号量的值<=0,当第6个进来的时候,就让当前线程会进入休眠状态,等到信号量>0;执行就-1,然后往下执行后面的代码,
如果信号量>0,开始的时候是5,执行就-1,然后往下执行后面的代码
dispatch_semaphore_signal(),
这个就是让信号量+1;

用信号量保证线程同步,可以设置信号量=1;保证同时只能有一条线程访问
信号量的初始值为1,代表同时只允许1条线程访问资源,保证线程同步

27-semaphore03-@synchronized

@synchronized是对mutex递归锁的封装
源码查看:objc4中的objc-sync.mm文件
@synchronized(obj)内部会生成obj对应的递归锁,然后进行加锁、解锁操作,传进来的对象一样,找到的就是同一把锁

- (void)saveMoney
{
    @synchronized ([self class]) {
        [super saveMoney];
    }
}
- (void)drawMoney
{
    @synchronized ([self class]) {
        [super drawMoney];
    }
}

同步方案对比

性能从高到低排序
os_unfair_lock ios10才支持
OSSpinLock
dispatch_semaphore ios8支持
pthread_mutex
dispatch_queue(DISPATCH_QUEUE_SERIAL)
NSLock 对mutex的封装
NSCondition
pthread_mutex(recursive) //递归锁
NSRecursiveLock
NSConditionLock
@synchronized

使用技巧

static dispatch_semaphore_t semaphore ;
    static dispatch_once_t onceToken;
    dispatch_once(&onceToken, ^{
        semaphore = dispatch_semaphore_create(1);
    });
    
    dispatch_semaphore_wait(semaphore, DISPATCH_TIME_FOREVER);
    //
    dispatch_semaphore_signal(semaphore);

29-自旋锁、互斥锁对比

什么情况使用自旋锁比较划算?
预计线程等待锁的时间很短
加锁的代码(临界区)经常被调用,但竞争情况很少发生
CPU资源不紧张
多核处理器

什么情况使用互斥锁比较划算?
预计线程等待锁的时间较长
单核处理器
临界区有IO操作
临界区代码复杂或者循环量大
临界区竞争非常激烈

automaic

原子不可再分割,原子性操作就说说明这个操作不可以再分割的,他是一个整体,要执行就要全部执行完,其他线程才能执行。
原子性,一旦我们给属性automaic他就会对我们get和set加锁,都是原子性操作,也就是保证线程同步
atomic用于保证属性setter、getter的原子性操作,相当于在getter和setter内部加了线程同步的锁
可以参考源码objc4的objc-accessors.mm
它并不能保证使用属性的过程是线程安全的,

reallySetProperty方法里面查看
if (!atomic) {
       oldValue = *slot;
       *slot = newValue;
   } else {
       spinlock_t& slotlock = PropertyLocks[slot];
       slotlock.lock();
       oldValue = *slot;
       *slot = newValue;        
       slotlock.unlock();
   }
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

多线程 线程安全 的相关文章

  • 浅谈ES6的Promise对象

    相信凡是写过javascript的童鞋也一定都写过回调方法 xff08 callback xff09 xff0c 简单说回调方法就是将一个方法func2作为参数传入另一个方法func1中 xff0c 当func1执行到某一步或者满足某种条件
  • node版本管理器——nvm

    nvm是管理node版本的一个工具 xff0c 具体介绍不再赘述 xff0c 请到GitHub xff08 https github com creationix nvm xff09 查看 安装 Linux Mac curl o https
  • centos下修改mysql默认端口

    mysql5 6安装 xff1a br wget http repo mysql com mysql community release el7 5 noarch rpm br rpm ivh mysql community release
  • nginx开启gzip压缩

    nginx安装 xff1a yum install y nginx 配置文件默认在 etc nginx nginx conf 打开nginx conf添加 gzip span class hljs function start span c
  • wordpress安装后问题汇总

    问题一 xff1a wordpress写文章界面点击 添加媒体 和 可视化 文本 无反应 解决方法 xff1a 在wp config php中追加 define 34 CONCATENATE SCRIPTS 34 false 问题二 xff
  • ucos ii是怎么实现多任务运行的?很通俗易懂的描述

    问题 xff1a ucos上建立一个任务 xff0c 格式如上图 xff0c 它是一个死循环 xff0c 但如果我建立了五个任务 xff0c 并且五个任务里面没有延时 xff0c 就只是像无操作系统那样写法 xff0c 用死循环让它们一直跑
  • 解决vim中文乱码

    执行 xff1a cd xff5e vim vimrc 将如下文本复制保存退出即可 set fileencodings 61 utf 8 ucs bom gb18030 gbk gb2312 cp936 set termencoding 6
  • nginx开启ssl证书

    修改listen为443 如果你又开启防火墙 xff0c 还需将443端口放开 在server中添加 ssl on ssl session timeout 5m ssl certificate cert 214050429370638 pe
  • 一键安装全局npm模块

    GitHub xff1a https github com flitrue npm install global 简介 当我们使用nvm管理node xff0c 切换node版本时 xff0c 安装在全局的npm包无法共用之前node版本中
  • vscode设置命令行启动

    方法一 xff1a 配置 启动 VS Code打开命令面板 shift 43 ctrl 43 P mac shift 43 cmmand 43 P xff0c 输入 shell command xff0c 找到 Install code c
  • Vue和React的生命周期

    最新的Vue和React生命周期 Vue 生命周期 vue生命周期主要有8个 beforeCreatecreatedbeforeMountmountedbeforeUpdateupdatedbeforeDestroydestroyed Re
  • js实现千位分隔符运算

    方法一 xff1a span class token keyword function span span class token function format span span class token punctuation span
  • 调整浏览器滚动条样式

    我们知道浏览器自带滚动条很丑 xff0c 有时影响整个页面到美观 xff0c 尤其在页面内嵌一个滚动列表 xff0c 显得奇丑无比 xff0c 下面我们根据如下代码调节滚动条样式 span class token punctuation s
  • 视觉SLAM理论入门——(7)视觉里程计之特征点法—对极几何

    提取ORB特征后 xff0c 需要根据点估计相机的运动 根据相机的原理 xff0c 可以分为下面几种情况 xff1a 1 当采用单目相机 xff0c 只知道2D像素坐标 xff0c 需要根据两组2D点估计运动 xff0c 这时用对极几何求解
  • 驱动程序之_1_字符设备_13_USB设备_3_鼠标驱动

    驱动程序之 1 字符设备 13 USB设备 3 鼠标驱动 从上一篇文章知道 xff0c 当我们接入一个USB设备 xff0c USB总线驱动会为我们构建一个device并注册 xff0c 编写驱动程序时只需要构造driver并注册到总线即可
  • 【转帖】驱动程序与应用程序之间的通信

    驱动程序必须与应用程序进行通信 xff0c 才能最终达到应用程序控制设备的目的 xff0c 不然驱动有QIU用 要通信就涉及到3个方面 1 应用程序与驱动程序通信 2 驱动程序与应用程序通信 3 数据传输 下面分别讨论 1 应用程序与驱动程
  • STM32最小系统硬件组成详解

    STM32最小系统硬件组成详解 0组成 xff1a 电源 复位 时钟 调试接口 启动 1 电源 xff1a 一般3 3V LDO供电 加多个0 01uf去耦电容 2 复位 xff1a 有三种复位方式 xff1a 上电复位 手动复位 程序自动
  • 从特效入手,深入了解CSS(一):动态加载特效

    不建议跳跃阅读 xff01 这篇文章将从头开始介绍如何实现一个特效 中间偶尔会穿插一些css3或平时接触不多的css属性 首先看一看这一期的特效 xff1a HTML部分 span class token tag span class to
  • Nuxt.js数据预取

    本文采用的技术框架有 xff1a 后台 xff1a Express 43 MongoDb 前台 xff1a Vue2 js 43 Nuxt js 64 2 9 2 在Nuxt中发送请求有两种方案 xff1a 前后台分离的方案 xff08 数
  • 【matplotlib】matplotlib使用详解 使用python绘制漂亮的论文数据图

    预备知识 Matplotlib 是 Python 中最受欢迎的数据可视化软件包之一 xff0c 支持跨平台运行 xff0c 它是 Python 常用的 2D 绘图库 xff0c 同时它也提供了一部分 3D 绘图接口 Matplotlib 通

随机推荐

  • apt-get指令的autoclean,clean,autoremove的区别

    下面总结一下有关apt get的常用但容易混淆的指令 br strong apt get autoclean strong 如果你的硬盘空间不大的话 xff0c 可以定期运行这个程序 xff0c 将已经删除了的软件包的 deb安装文件从硬盘
  • 2021美团笔试题(第十套)个人解答

    1 淘汰分数 span class token comment 暴力解法 span span class token keyword import span span class token namespace java span clas
  • DeepSOCIAL:基于YOLOv4的人群距离监测!集检测、跟踪以及逆透视映射一体的系统!...

    点击下方 AI算法与图像处理 xff0c 一起进步 xff01 重磅干货 xff0c 第一时间送达 论文 xff1a https doi org 10 3390 app10217514 代码 xff1a https github com D
  • HAL 0.5.10 Specification

    HAL 0 5 10 Specification David Zeuthen lt a href david 64 fubar dk a gt Version 0 5 10 Table of Contents 1 Introduction
  • LINUX USB 系统(1) 收藏

    LINUX USB 系统 1 收藏 1 简述 xff1a USB 出自豪门 xff0c 一问世便有 IBM Microsoft compaq 等前呼后拥 xff0c 不红实在是没有道理 xff0c 以致于连三岁小毛孩都知道买游戏手柄要买 U
  • 高通brew 方案开机揭秘

    摘要 xff1a 本文试图通过代码来深入剖析 Qualcomm 手机开机的整个过程 xff0c 即从按下开机键一直到出现待机界面 xff0c Qualcomm 的手机软件在整个流程中究竟完成了哪些工作 本文的主要目标是理清手机的初始化流程
  • 编程之美读书笔记_3.3_计算字符串的相似度

    3 3 计算字符串的相似度 和计算两字符串的最长公共子序列相似 设Ai 为字符串A a1a2a3 am 的前i 个字符 xff08 即为a1 a2 a3 ai xff09 设Bj 为字符串B b1b2b3 bn 的前j 个字符 xff08
  • 有铅喷锡和无铅喷锡的选择

    有铅喷锡和无铅喷锡 xff08 SAC xff09 在生产中工艺要求是一个非常重要的因素 xff0c 他直接决定着一个PCB板的质量和定位 xff0c 比如喷锡 镀金 沉金 xff0c 相对来说沉金就是面对高端的板子 xff0c 沉金由于质
  • 硬盘的那些事(主分区、扩展分区、逻辑分区、活动分区、系统分区、启动分区、引导扇区、MBR等

    硬盘的那些事 xff08 主分区 扩展分区 逻辑分区 活动分区 系统分区 启动分区 引导扇区 MBR等 主分区 xff0c 扩展分区 xff0c 逻辑分区 xff0c 活动分区 xff0c 系统分区 xff0c 启动分区 主引导扇区 xff
  • Vcc(电源)和GND(地)之间接电容的作用

    1 在直流电源 xff08 Vcc xff09 和地之间并接电容的电容可称为滤波电容 xff0e 滤波电容滤除电源的杂波和交流成分 xff0c 压平滑脉动直流电 xff0c 储存电能 xff0e 取值一般100 xff0d 4700uF x
  • APM/Pixhawk地面站航迹规划指令单

    本文来源于http ardupilot org copter docs mission command list html condition distance 由于对APM Pixhawk的爱好 xff0c 翻译成中文供参考 xff0c
  • APM/Pixhawk路径规划飞行(自动起飞/降落/航路点飞行)

    APM Pixhawk路径规划飞行 xff08 自动起飞 降落 航路点飞行 xff09 本节主要介绍各类飞行器一般的航路点设置 xff0c 已经实现了自动起飞降落和按计划轨迹飞行 设置家的位置 对于直升机和多旋翼家的位置一般是飞控解锁的位置
  • E2上GBA模拟器移植的困难。。。

    终于决心动手写GBA的模拟器了 修改了半晌 xff0c 终于吧Oop的模拟器源码编译通过 xff0c 但是到了机器上却跑不起来 xff0c 那个源码用了一个非常奇怪的方式来处理系统响应 xff1a 主动调用系统去处理 xff0c 没开线程
  • 蓝牙物理链路类型:SCO和ACL链路

    蓝牙物理链路ACL Asynchronous Connectionless 另外的一种链路是SCO Synchronous Connection Oriented 主要用来传输对时间要求很高的数据通信 蓝牙基带技术支持两种连接类型 xff1
  • Linux内核角度分析tcpdump原理(二)

    上篇文章介绍了在内核角度tcpdump的抓包原理 1 xff0c 主要流程如下 xff1a 应用层通过libpcap库 xff1a 调用系统调用创建socket sock fd 61 socket PF PACKET SOCK RAW ht
  • 3 Linux目录管理

    注意 xff1a 在下面的讲解中 xff0c 每个命令都有很多的参数说明 选项 xff0c 我们只讲其中的几个 xff0c 关键是让学生掌握命令的语法 xff1b 学生学习完语法后 xff0c 就可以自己按照参数书写各种命令 xff0c 这
  • 多线程-读写安全

    多线程31 读写安全01 简介 文件操作 IO操作 读取文件 往文件中写入内容 不能允许读取和写入同时进行 我们之前做的加锁那种操作 xff0c 平时开发中有点缺陷我们最好让读的操作可以多条线程一起读取 多读单写 xff0c 多线程出乱主要
  • 单片机的程序具体运行过程以及栈空间的使用

    单片机的程序具体运行过程以及栈空间的使用 初始化RAM xff1a 将启动文件里选择的RAM空间清零 xff0c 将初始化不为零的全局变量在RAM里赋值 给PC指针赋初值 xff1a 将程序的第一条语句在ROM的地址取出给PC指针 程序执行
  • __block-内存管理

    我们创建一个对象如果我们block内部用到了 block类型的变量 xff0c 他会拥有这个对象 xff0c 我们可以通过cpp文件来分析 一旦访问对象 xff0c desc结构体里面就会多两个成员 xff0c 一个是copy 和dispo
  • 多线程 线程安全

    多线程的安全隐患 我们用多线程有很多好处 xff0c 但是也存在安全隐患 资源共享 1块资源可能会被多个线程共享 xff0c 也就是多个线程可能会访问同一块资源 比如多个线程访问同一个对象 同一个变量 同一个文件 当多个线程访问同一块资源时