React中commit阶段发生了什么

news2024/11/16 0:50:52

对于commit阶段的主要工作是循环effectList链表去将有更新的fiber节点应用到页面上是commit的主要工作。

EffectList

什么是副作用?

函数在执行过程中对外部造成的影响可以称之为副作用,副作用包含的类型很多,比如说标记值为Placement时,会叫Dom节点进行插入与移动,Passive代表useEffect进行回调执行,ChildDeletion指移除子Dom节点

对于React的工作原理可以简单概括为

  • 触发更新
  • render阶段:计算更新会造成的副作用
  • commit阶段:执行副作用

更新Dom变化主要就是Placement、ChildDeletion在起作用,在render阶段来进行保存副作用,在commit阶段来使用副作用。

什么是EffectLitst

在重构前,render阶段带有副作用的节点会链接形成链表,这条链表被称为EffectList,在commit阶段,不需要深度优先遍历整棵树,只需要遍历Effects List就能找到所有有副作用的节点并执行对应操作。

在render阶段的complete阶段,如果遇到flags字段不为NoFlags时(代表需要副作用),此时就把该Fiber节点添加到EffectList中。

例一:如下图,会通过EffectList来将副作用节点进行链接,避免了深度优先遍历对应的Fiber树。

例二:

<div id="1">
  <div id="4"/>
  <div id="2">
      <div id="3"/>
  </div>
</div>

最终形成的EffectList为:
firstEffect => div4
div4.nextEffect => div3
div3.nextEffect => div2
div2.nextEffect => div1

// div4为firstEffect,div1为lastEffect
因为Fiber树是通过深度优先遍历来进行构建,所以div4先完成completeWork来构建出firsteEffect

commit阶段

React16 引入 Fiber 架构后,将整个调度分为了两个阶段 render & commit,在 render 阶段,React 会计算 DOM 的更新,并将所有需要更新的 fiber 整理成一个 effect list,在 commit 阶段中, React 会遍历 effect list 执行所有的副作用,期间会执行更新相关的生命周期、挂载 DOM 等等。

在commit阶段的入口是commitRoot函数,它会告知scheduler以立即执行的优先级去调度commit阶段的工作。

function commitRoot(root) {
  const renderPriorityLevel = getCurrentPriorityLevel();
  runWithPriority(
    ImmediateSchedulerPriority,
    commitRootImpl.bind(null, root, renderPriorityLevel),
  );
  return null;
}

scheduler去调度的是commitRootImpl,它是commit阶段的核心实现,整个commit阶段被划分成三个部分。

commitRootImpl

对于React中会有一个节点名为FiberRoot来作为整个组件树的顶层节点,对于finishedWork属性指向的是完成的第一个work,也为ReactDom.render的组件,在commit阶段中我们通过根节点拿到finishedWork,然后通过finishedWork来获取firstEffect

  // 如果 root 有副作用的话,其副作用将会放置在 effectList 的末尾,
  // root 无副作用的话,那么 firstEffect 就是根组件的 firstEffect
  let firstEffect;
  if (finishedWork.effectTag > PerformedWork) {
    if (finishedWork.lastEffect !== null) {
      finishedWork.lastEffect.nextEffect = finishedWork;
      firstEffect = finishedWork.firstEffect;
    } else {
      firstEffect = finishedWork;
    }
  } else {
    // There is no effect on the root.
    firstEffect = finishedWork.firstEffect;
  }

对于commit阶段,React将其拆分为了多个小阶段,每个小阶段都会执行对于nextEffect对应的遍历流程,保证了useEffect可以在useLayoutEffect之前被处理,对应代码如下

// ReactFiberWorkLoop.js
function commitRootImpl() {
  // 刷新所有的 PassiveEffect
  do {
    flushPassiveEffects();
  } while (rootWithPendingPassiveEffects !== null);
  
  // Get the list of effects.
  // effectList 的第一个节点
  let firstEffect;
  // ...
  // 省略 if 判断,如果 root 有副作用的话,其副作用将会放置在 effectList 的末尾,root 无副作用的话,那么 firstEffect 就是根组件的 firstEffect
  firstEffect = finishedWork.firstEffect;
  if (firseEffect !== null) {
    nextEffect = firstEffect;
    // 每一阶段的详细代码后续会进行说明
    // 第一阶段,before mutation
    do {
      commitBeforeMutationEffects();
    } while(nextEffect !== null)
    // ...
    // 将游标重置,指向 effect list 头
    nextEffect = firstEffect;
    // 第二阶段 mutation
    do {
      commitMutationEffects(root, renderPriorityLevel);
    } while(nextEffect !== null)
            
    // 将当前的 workInProgress树 作为 current 树
    root.current = finishedWork;
    
    // ...
          // 第三阶段 layout 
    do {
      commitLayoutEffects(root, expirationTime);
    } while(nextEffect)
            
    // 让调度器在 帧 的末尾暂停,给浏览器机会执行一次 重绘
    requestPaint();
    
    // rootDoesHavePassiveEffects 标志位判断,该标志位是在 commit 第一阶段进行设置,标记当前 commit 是否具有 passiveEffect
    if (rootDoesHavePassiveEffects) {
      rootDoesHavePassiveEffects = false;
            rootWithPendingPassiveEffects = root;
            pendingPassiveEffectsExpirationTime = expirationTime;
            pendingPassiveEffectsRenderPriority = renderPriorityLevel;
    } else {
      // 遍历 effect list 逐个设置为 null 以便 GC
    }
    
    // 确保 root 上所有的 work 都被调度完
    ensureRootIsScheduled(root);
    
    // 检测在 useLayoutEffect 中是否做了布局修改等,刷新布局.
    // 如果在 layoutEffect 中调用了 setState 也会在该函数中检测中并开启新的一轮调度
    flushSyncCallbackQueue();
  } else { ... }
}

Before Mutation

对于before mutation针对类组件调用getSnapshotBeforeUpdate,让我们可以在Dom变更前获取组件实例的信息。对于函数式组件会进行异步调度useEffect。

对于Before Mutation阶段的入口函数是commitBeforeMutationEffects,其函数源码如下:

function commitBeforeMutationEffects() {
  while (nextEffect !== null) {
    const effectTag = nextEffect.effectTag;
    // 只有class组件会进入这个判断
    // 对于使用 getSnapShowBeforeUpdate 的组件 fiber.effectTag |= SnapShot
    if ((effectTag & Snapshot) !== NoEffect) {
      // ...
      const current = nextEffect.alternate;
      // 执行 getSnapShotBeforeUpdate 生命周期
      commitBeforeMutationEffectOnFiber(current, nextEffect);
      resetCurrentDebugFiberInDEV();
    }
    // 对于使用 useEffect 的组件,其 Fiber.effectTag = UpdateEffect | PassiveEffect
    if ((effectTag & Passive) !== NoEffect) {
      // If there are passive effects, schedule a callback to flush at
      // the earliest opportunity.
      if (!rootDoesHavePassiveEffects) {
        rootDoesHavePassiveEffects = true;
        scheduleCallback(NormalPriority, () => {
          // 执行useEffect
          flushPassiveEffects();
          return null;
        });
      }
    }
    // 向下遍历
    nextEffect = nextEffect.nextEffect;
  }
}

可以将上述源码分为两个部分。

第一个主要用于判断Class组件中的getSnapShotBeforeUpdate,进入第一个判断时,会在内部执行getSnapShotBeforeUpdate生命周期,也只有Class组件会进入当前判断。

第二个判断主要用于函数式组件且使用了useEffect。

  • 对rootDoesHavePassiveEffects进行标记,在commit阶段的末尾会判断当前commit是否具有被动的副作用,如果具有被动的副作用,则会设置一些额外的标志位用于下一轮的调度。
  • 然后再进行加入回调,在回调内部会进行调用flushPassiveEffects。

对于回调会被异步触发,这里的异步不是setState那样的批处理,而是事件循环中的同步任务与异步任务。当创建scheduleCallback时,此时就会创建一个task任务,当前task保存着我们传入的回调函数,将其压入到taskQueue队列中,当浏览器的每一帧还有剩余时间时,React会调用postMessage将performWorkUntilDeadline函数压入到异步队列中,等待所有的同步任务执行完之后执行该函数。源码如下:

// Scheduler.js
 function unstable_scheduleCallback(priorityLevel, callback, options) {
   // ...
   var newTask = {
    id: taskIdCounter++,
    callback,
    priorityLevel,
    startTime,
    expirationTime,
    sortIndex: -1,
  };
  // ...
   if (startTime > currentTime) {
     //... 超时调用
   } else {
     // 正常调用
     push(taskQueue, newTask);
     if (!isHostCallbackScheduled && !isPerformingWork) {
      isHostCallbackScheduled = true;
      // 发送一个 postMessage
      requestHostCallback(flushWork);
    }
   }
 }

// ScheduleHostConfig.default.js
const channel = new MessageChannel();
const port = channel.port2;
channel.port1.onmessage = performWorkUntilDeadline;

requestHostCallback = function(callback) {
        scheduledHostCallback = callback;
        if (!isMessageLoopRunning) {
                isMessageLoopRunning = true;
                port.postMessage(null);
        }
};

对于flushPassiveEffects主要用于刷新useEffect,并且与scheduleCallback这个回调函数结合,会被添加进异步任务队列中等待执行。

Mutation

在Mutation阶段后会将workInProgress Tree变为current树,在mutation阶段主要执行的函数为commitMutationEffects,对于commitMutationEffects函数的代码如下:

function commitMutationEffects(root: FiberRoot, renderPriorityLevel) {
  while (nextEffct) {
    // 获取当前的effectTag来匹配对应的副作用标签
    const effectTag = nextEffect.effectTag;
    let primaryEffectTag = effectTag & (Placement | Update | Deletion | Hydrating);
    // ...
    switch(primaryEffectTag) {
      // 单纯的挂载 DOM
      case Placement: {
        // 挂载 DOM
        commitPlacement(nextEffect);
        nextEffect.effectTag &= ~Placement;
        return; 
      }
      // 更新组件及DOM
      case PlacementAndUpdate: {
        // 挂载 DOM
        commitPlacement(nextEffect);
        nextEffect.effectTag &= ~Placement;
        // 刷新 layoutEffect.desotry
        const current = nextEffect.alternate;
        commitWork(current, nextEffect);
        break;
      }
      // 更新组件
      case Update: {
        const current = nextEffect.alternate;
        commitWork(current, nextEffect);
        break;
      }
      // 卸载
      case Deletion: {
        commitDeletion(root, nextEffect, renderPriorityLevel);
        break;
      }
      // ...
    }
    nextEffect = nextEffect.nextEffect;
  }
}

对于effectTag与对应匹配的含义如下:

  • Placement:当增加Dom时,会加入对应的标志位
  • PlacementAndUpdate:当Dom变化,并组件发生更新时
  • Update:组件更新,但Dom无变化时
  • Deletion:当组件卸载时,会加入对应的标志位

不同的标志位,其内部调用的逻辑比较相似,主要的函数为commitPlacement和commitWork。

对于commitPlacement函数源码分析如下:

function commitPlacement(finishedWork: Fiber): void {
  ...

  // 找到目标节点DOM层面的父节点(parent)
  const parentFiber = getHostParentFiber(finishedWork);

  // 根据目标节点类型,改变parent
  let parent;
  let isContainer;
  const parentStateNode = parentFiber.stateNode;
  
  // 对于HostRoot和HostPortal都没有对应的Dom节点
  switch (parentFiber.tag) {
    // 原生的HTML标签
    case HostComponent:
      parent = parentStateNode;
      isContainer = false;
      break;
    // 根节点
    case HostRoot:
      parent = parentStateNode.containerInfo;
      isContainer = true;
      break;
    case HostPortal:
      parent = parentStateNode.containerInfo;
      isContainer = true;
      break;
    case FundamentalComponent:
      if (enableFundamentalAPI) {
        parent = parentStateNode.instance;
        isContainer = false;
      }
  }
  if (parentFiber.effectTag & ContentReset) {
    // 插入之前重设文字内容
    resetTextContent(parent);
    // 删除ContentReset的effectTag
    parentFiber.effectTag &= ~ContentReset;
  }

  // 找到基准节点
  const before = getHostSibling(finishedWork);
  // 根据上方isContainer的标识执行不同的插入操作
  // 执行插入操作
  if (isContainer) {
    // 在外部DOM节点上插入
    insertOrAppendPlacementNodeIntoContainer(finishedWork, before, parent);
  } else {
    // 直接在父节点插入
    insertOrAppendPlacementNode(finishedWork, before, parent);
  }
}

对于commitPlacement函数的主要功能步骤如下:

  • 找到目标节点的父节点
  • 根据目标节点类型,找到对应的赋值对应的parent
  • 如果目标节点对应的DOM节点目前只有文字内容,类似<div>hello</div>,并且持有ContentReset(内容重置)的effectTag,那么插入节点之前先设置一下文字内容
  • 找到基准节点
  • 执行插入

对于CommitWork的源码如下:

对于commitWork主要讲解Dom节点(HostComponent)的更新和文本节点(HostText)的更新

function commitWork(current: Fiber | null, finishedWork: Fiber): void {
  switch (finishedWork.tag) {
    // 更新HostComponent是更新fiber节点的props
    case HostComponent: {
      const instance: Instance = finishedWork.stateNode;
      if (instance != null) {
        // Commit the work prepared earlier.
        const newProps = finishedWork.memoizedProps;
        const oldProps = current !== null ? current.memoizedProps : newProps;
        const type = finishedWork.type;
        // updateQueue已经是在complete过程中节点props被diff的结果
        const updatePayload: null | UpdatePayload = (finishedWork.updateQueue: any);
        finishedWork.updateQueue = null;
        if (updatePayload !== null) {
          // 更新对应Dom节点的props
          commitUpdate(
            instance,
            updatePayload,
            type,
            oldProps,
            newProps,
            finishedWork,
          );
        }
        // 更新对应Dom节点的事件监听器
        if (enableFlareAPI) {
          const prevListeners = oldProps.listeners;
          const nextListeners = newProps.listeners;
          if (prevListeners !== nextListeners) {
            updateEventListeners(nextListeners, finishedWork, null);
          }
        }
      }
      return;
    }
    case HostText: {
      const textInstance: TextInstance = finishedWork.stateNode;
      // 通过props来获取对应的文本
      const newText: string = finishedWork.memoizedProps;
      const oldText: string =
        current !== null ? current.memoizedProps : newText;
        
      // 最后通过commitTextUpdate来更新文本
      commitTextUpdate(textInstance, oldText, newText);
      return;
    }
    case HostRoot: {
      if (supportsHydration) {
        const root: FiberRoot = finishedWork.stateNode;
        if (root.hydrate) {
          // We've just hydrated. No need to hydrate again.
          root.hydrate = false;
          commitHydratedContainer(root.containerInfo);
        }
      }
      return;
    }
  }
}

Layout

对于layout阶段的入口函数是commitLayoutEffects,对于classComponent和functionComponent,针对前者,调用生命周期componentDidMount和componentDidUpdate,调用setState的回调;针对后者,填充useEffect 的 effect执行数组。

commitLayoutEffects源码如下:

function commitLayoutEffects(
  root: FiberRoot,
  committedExpirationTime: ExpirationTime,
) {
  while (nextEffect !== null) {
    //...

    const effectTag = nextEffect.effectTag;

    if (effectTag & (Update | Callback)) {
      recordEffect();
      const current = nextEffect.alternate;
      // 执行componentDidmount
      commitLayoutEffectOnFiber(
        root,
        current,
        nextEffect,
        committedExpirationTime,
      );
    }

    // ...
    nextEffect = nextEffect.nextEffect;
  }
}

function commitLifeCycles(
  finishedRoot: FiberRoot,
  current: Fiber | null,
  finishedWork: Fiber,
  committedLanes: Lanes,
): void {
  switch (finishedWork.tag) {
    case FunctionComponent:
    case ForwardRef:
    case SimpleMemoComponent:
    case Block: {
      // 执行useLayoutEffect的创建
      commitHookEffectListMount(HookLayout | HookHasEffect, finishedWork);

      // 填充useEffect的effect执行数组
      schedulePassiveEffects(finishedWork);
      return;
    }
    case ClassComponent: {
      const instance = finishedWork.stateNode;
      if (finishedWork.flags & Update) {
        if (current === null) {
          // 如果是初始挂载阶段,调用componentDidMount
          instance.componentDidMount();
        } else {
          // 如果是更新阶段,调用componentDidUpdate
          const prevProps =
            finishedWork.elementType === finishedWork.type
              ? current.memoizedProps
              : resolveDefaultProps(finishedWork.type, current.memoizedProps);
          const prevState = current.memoizedState;

          instance.componentDidUpdate(
            prevProps,
            prevState,
            // 将getSnapshotBeforeUpdate的结果传入
            instance.__reactInternalSnapshotBeforeUpdate,
          );
        }
      }

      // 调用setState的回调
      const updateQueue = finishedWork.updateQueue;
      if (updateQueue !== null) {

        commitUpdateQueue(finishedWork, updateQueue, instance);
      }
      return;
    }

    ...

  }
}

commit最后

在上述三个子阶段都完成之后, React 已经将所有的DOM都挂载到屏幕上,并且也执行了 didMount,但是在上述三个子阶段的执行过程中,可能会发生新的副作用,比如说在 layoutEffect 中调用了 setState ,因此在 commit 的末尾会做一次检测,入口函数就是 flushSyncCallbackQueue

总结

对于commit阶段分为三个过程,分别为before Mutation/mutation/layout,将effectList的处理分为三个阶段保证可以在不同生命周期函数中进行调用,相对于同步执行的useEffectLayout,对于useEffect的异步调用提供了一种不阻塞页面渲染的副作用操作入口。最后标记还未处理的优先级和调用ensureRootIsScheduled函数来确保root上所有被跳过的低优先级任务再次被调度。

在before mutation这个阶段,对于class组件而言,执行getSnapShotBeforeUpdate对应的生命周期,对于函数式组件会进行异步回调执行flushPassiveEffects,等待同步任务执行完毕后执行useEffects对应的副作用。

在mutation阶段会挂载和更新Dom,并清理上一轮的useLayoutEffect。此时会将当前的workInProgress树作为 current树。

在layout阶段中对于Class组件会执行componentDidMount生命周期,对于函数式组件会执行useLayoutEffect。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/181468.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

客快物流大数据项目(一百零九):Spring Boot概述

文章目录 Spring Boot概述 一、什么是SpringBoot 二、​​​​​​​为什么要学习Spring Boot

PHP转Go实践:xjson解析神器「开源工具集」

前言 近期会更新一系列开源项目的文章&#xff0c;新的一年会和大家做更多的开源项目&#xff0c;也欢迎大家加入进来。 xutil 今天分享的文章源自于开源项目jinzaigo/xutil的封装。 在封装过程中&#xff0c;劲仔将实现原理以及相关实践思考&#xff0c;写成文章分享出来&am…

Python3学习——条件控制、循环语句与迭代器

目录 一、编程第一步——斐波那契数列 二、条件控制 (一)if/else语句 判断狗狗的年龄&#xff1a; (二)多层if/else嵌套 判断数字能否被2或3整除&#xff1a; (三)match...case匹配——python3中新增 根据数字判断星期&#xff1a; 三、循环语句 (一)while循环 1.循环…

Java:Idea创建项目和Spring工程基本使用

一、创建项目 1、创建新的空的项目&#xff1a; Empty Project–next 2、定义项目的名称&#xff0c;并指定位置 3、对项目进行设置&#xff0c;JDK版本、编译版本 4、添加模块信息 5、修改maven路径 6、项目目录结构 二、搭建Spring的框架 1、在核心配置文件中添加Spring的j…

C++11 并发指南五(stdcondition_variable 详解)

C11 并发指南五(std::condition_variable 详解) 文章目录C11 并发指南五(std::condition_variable 详解)std::condition_variable 类介绍std::condition_variable_any 介绍std::cv_status 枚举类型介绍std::notify_all_at_thread_exit前面三讲《 C11 并发指南二(std::thread 详…

二叉树简单解析(1)

&#x1f340;本人简介&#xff1a; 吉师大一最爱逃课的混子、 华为云享专家、阿里云专家博主、腾讯云自媒体分享计划博主、 华为MindSpore优秀开发者、迷雾安全团队核心成员&#xff0c;CSDN2022年运维与安全领域第15名 &#x1f341;本人制作小程序以及资源分享地址&#x…

英语学习打卡day7

2023.1.27 1.ironically adv.具有讽刺意味的是;反讽地&#xff0c;讽刺地 Ironically, his cold got better on the last day of his holiday. 2.bequeath vt.遗赠;把…遗赠给;把… .传给 (比give更正式) bequeath sb sth bequeath sth to sb Don’t bequeath the problem …

JDK17 || JDK 8 完美 卸载 教程 (Windows版)

文章目录一、卸载jdk程序1 . 找到控制面板2. 卸载程序3. 找到JDK 相关的程序4. 右键 选择卸载程序5. 下一步 选择 是6.下一步 选择 是二、安装 新版 JDK三、如果不想再使用jdk环境结语一、卸载jdk程序 1 . 找到控制面板 2. 卸载程序 3. 找到JDK 相关的程序 4. 右键 选择卸载程…

IDEA界面和控制台的滚动条颜色不明显?赶快换一个吧!

前言 不知道大家是否和我一样有这么一个烦恼&#xff1a; IDEA自带的滚动条颜色很暗&#xff0c;配上一些主题颜色搭配很难发现。 所以今天就想着怎么可以修改滚动条颜色&#xff0c;首先去网上搜了搜都是什么鼠标滚轮加shift滚动&#xff0c;一点也不实用 偶然看到了个不错的…

【青训营】Go的BenchMark的使用

本文内容总结于 字节跳动青年训练营 第五届后端组 Go自带了一些性能测试工具&#xff0c;其中BenchMark是较为重要的一个。 我们以计算斐波那契数列的示例来展示BenchMark的使用 package Benchmarkimport "testing"func Fib(n int) int {if n < 2 {return n}ret…

OpenCV-PyQT项目实战(1)安装与环境配置

本系列从零开始实战解说基于 PyQt5 的 OpenCV 项目开发。 欢迎关注『OpenCV-PyQT项目实战 Youcans』系列&#xff0c;持续更新中 OpenCV-PyQT项目实战&#xff08;1&#xff09;安装与环境配置 OpenCV-PyQT项目实战&#xff08;2&#xff09;OpenCV导入图像 文章目录1. PyQt5 …

初识图像分类——K近邻法(cs231n assignment)

作者&#xff1a;非妃是公主 专栏&#xff1a;《计算机视觉》 个性签&#xff1a;顺境不惰&#xff0c;逆境不馁&#xff0c;以心制境&#xff0c;万事可成。——曾国藩 专栏系列文章 Cannot find reference ‘imread‘ in ‘init.py‘ error: (-209:Sizes of input arguments…

ppt神器islide 第1节 初步接触强大的工具-资源篇

ppt神器islide 第1节 初步接触强大的工具1 PPT大神的课程总结1.1 骨架篇1.2 色彩篇1.3 对齐篇1.4 对比篇1.5 修饰篇1.6 字体篇1.7 素材篇1.8 线条篇1.8.1 可以随意画线条&#xff0c;填充空白1.8.2 在字体上画线条&#xff0c;做成艺术字1.8.3 做对称线条&#xff0c;比如递进三…

[Vulnhub] DC-3

下载链接&#xff1a;DC-3 DC-3需要 把IDE里面的改成IDE 0:0 不然无法打开 知识点&#xff1a; Joomla cms 3.7.0 sql注入漏洞cmseek工具探测cms指纹john解密proc_popen反弹shellpython -m http.server开启http服务 & wget远程下载ubuntu16.0.4 Linux 4.4.0-21 系统漏…

使用OpenAI的Whisper 模型进行语音识别

语音识别是人工智能中的一个领域&#xff0c;它允许计算机理解人类语音并将其转换为文本。该技术用于 Alexa 和各种聊天机器人应用程序等设备。而我们最常见的就是语音转录&#xff0c;语音转录可以语音转换为文字记录或字幕。 wav2vec2、Conformer 和 Hubert 等最先进模型的最…

无穷小的比较——“高等数学”

各位CSDN的uu们你们好呀&#xff0c;今天小雅兰的内容是无穷小的比较&#xff0c;下面&#xff0c;就让我们一起进入高等数学的世界吧 回顾 定义 性质 定理 定理1&#xff1a; 定理2&#xff1a;等价无穷小替换定理 常用的等价无穷小 例题 小结 回顾 两个无穷小的商当然不一定还…

Android深入系统完全讲解(38)

9.6 配置 native 方法 9.6.1 CMakeLists.txt 文件中内容&#xff0c;配置生成一个 so 库add_library( # Sets the name of the library. native-lib # Sets the library as a shared library. SHARED # Provides a relative path to your source file(s). src/main/cpp/native…

一刷代码随想录——数组

概念数组是存放在连续内存空间上的相同类型数据的集合连续&#xff1a;增删需移动后续元素&#xff0c;删除的本质是覆盖类型&#xff1a;相同用数组&#xff0c;不同用结构数组下标从0开始C中二维数组连续分布vector的底层实现是array&#xff0c;严格来讲vector是容器&#x…

C++11 并发指南四( 详解三 stdfuture stdshared_future)

C11 并发指南四( 详解三 std::future & std::shared_future) 文章目录C11 并发指南四( 详解三 std::future & std::shared_future)std::future 介绍std::future 成员函数std::shared_future 介绍std::future_error 介绍其他与 std::future 相关的函数介绍其他与 std::f…

SWIFT Framework .NET 2023.1 Crack

SWIFT 组件 SWIFT Components 多年来一直致力于银行机构软件、在线交易软件、CRM 和零售和商业解决方案中的计费应用程序&#xff0c;以及医药和高度关键任务的 24/7 运营应用程序。作为定制银行解决方案的开发商&#xff0c;SWIFT Components 拥有所有银行产品和部门的专业知识…