移动开发避坑指南——内存泄漏
在日常编写代码时难免会遇到各种各样的问题和坑,这些问题可能会影响我们的开发效率和代码质量,因此我们需要不断总结和学习,以避免这些问题的出现。接下来我们将围绕移动开发中常见问题做出总结,以提高大家的开发质量。本系列文章讲围绕内存泄漏、语言开发注意事项等展开。本篇我们将介绍Android/iOS常见的内存泄漏问题。
一、Android端
GEEK TALK
内存泄漏(Memory Leak),简单说就是不再使用的对象无法被GC回收,占用内存无法释放,导致应用占用内存越来越多,内存空间不足而出现OOM崩溃;另外因为内存可用空间变少,GC更加频繁,更容易触发FULL GC,停止线程工作,导致应用卡顿。
Android应用程序中的内存泄漏是一种常见的问题,以下是一些常见的Android内存泄漏:
1.1 匿名内部类
匿名内部类持有外部类的引用,匿名内部类对象泄露,从而导致外部类对象内存泄漏,常见Handler、Runnable匿名内部类,持有外部Activity的引用,如果Activity已经被销毁,但是Handler未处理完消息,导致Handler内存泄露,从而导致Activity内存泄露。
示例1:
public class TestActivity extends AppCompatActivity {
private static final int FINISH_CODE = 1;
private Handler handler = new Handler() {
@Override
public void handleMessage(@NonNull Message msg) {
if (msg.what == FINISH_CODE) {
TestActivity.this.finish();
}
}
};
@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
handler.sendEmptyMessageDelayed(FINISH_CODE, 60000);
}
}
示例2:
public class TestActivity extends AppCompatActivity {
@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
new Handler().postDelayed(new Runnable() {
@Override
public void run() {
TestActivity.this.finish();
}
}, 60000);
}
}
示例1和示例2均为简单计时一分钟关闭页面,如果页面在之前被主动关闭销毁,Handler中仍有消息等待执行,就存在到Activity的引用链,导致Activity销毁后无法被GC回收,造成内存泄露;示例1为Handler匿名内部类,持有外部Activity引用:主线程 —> ThreadLocal —> Looper —> MessageQueue —> Message —> Handler —> Activity;示例2为Runnable匿名内部类,持有外部Activity引用:Message —> Runnable —> Activity.
修复方法1:主要针对Handler,在Activity生命周期移除所有消息。
@Override
protected void onDestroy() {
super.onDestroy();
handler.removeCallbacksAndMessages(null);
}
修复方法2:静态内部类+弱引用,去掉强引用关系,可以修复类似匿名内部类造成内存泄露。
static class FinishRunnable implements Runnable {
private WeakReference<Activity> activityWeakReference;
FinishRunnable(Activity activity) {
activityWeakReference = new WeakReference<>(activity);
}
@Override
public void run() {
Activity activity = activityWeakReference.get();
if (activity != null) {
activity.finish();
}
}
}
new Handler().postDelayed(new FinishRunnable(TestActivity.this), 60000);
1.2 单例/静态变量
单例/静态变量持有Activity的引用,即使Activity已经被销毁,它的引用仍然存在,从而导致内存泄漏。
示例:
static class Singleton {
private static Singleton instance;
private Context context;
private Singleton(Context context) {
this.context = context;
}
public static Singleton getInstance(Context context) {
if (instance == null) {
instance = new Singleton(context);
}
return instance;
}
}
Singleton.getInstance(TestActivity.this);
调用示例中的单例,传递Context参数,使用Activity对象,即使Activity销毁,也一直被静态变量Singleton引用,导致无法回收造成内存泄露。
修复方法:
Singleton.getInstance(Application.this);
尽量使用Application的Context作为单例参数,除非一些需要需要Activity的功能,比如显示Dialog,如果非要使用Activity作为单例参数,可以参考匿名内部类修复方法,在合适时机比如Activity的onDestroy生命周期释放单例,或者使用弱引用持有Activity。
1.3 监听器
示例: EventBus注册监听未解绑,导致注册到EventBus一直被引用,无法回收。
public class TestActivity extends AppCompatActivity {
@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
EventBus.getDefault().register(this);
}
}
修复方法: 在对应注册监听的生命周期解绑,onCreate对应onDestroy。
@Override
protected void onDestroy() {
super.onDestroy();
EventBus.getDefault().unregister(this);
}
1.4 文件/数据库资源
示例: 打开文件数据库或者文件,发生异常,未关闭,导致资源一直存在,导致内存泄漏。
public static void copyStream(File inFile, File outFile) {
try {
FileInputStream inputStream = new FileInputStream(inFile);
FileOutputStream outputStream = new FileOutputStream(outFile);
byte[] buffer = new byte[1024];
int len;
while ((len = inputStream.read(buffer)) != -1) {
outputStream.write(buffer, , len);
}
} catch (IOException e) {
e.printStackTrace();
}
}
修复:在finally代码块中关闭文件流,保证发生异常后一定能执行到
public static void copyStream(File inFile, File outFile) {
FileInputStream inputStream = null;
FileOutputStream outputStream = null;
try {
inputStream = new FileInputStream(inFile);
outputStream = new FileOutputStream(outFile);
byte[] buffer = new byte[1024];
int len;
while ((len = inputStream.read(buffer)) != -1) {
outputStream.write(buffer, , len);
}
} catch (IOException e) {
e.printStackTrace();
} finally {
close(inputStream);
close(outputStream);
}
}
public static void close(Closeable closeable) {
if (closeable != null) {
try {
closeable.close();
} catch (Exception e) {
e.printStackTrace();
}
}
}
1.5 动画
示例: Android动画未及时取消释放动画资源,导致内存泄露。
public class TestActivity extends AppCompatActivity {
private ImageView imageView;
private Animation animation;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_test);
imageView = (ImageView) findViewById(R.id.image_view);
animation = AnimationUtils.loadAnimation(this, R.anim.test_animation);
imageView.startAnimation(animation);
}
}
修复: 在页面退出销毁时取消动画,及时释放动画资源。
@Override
protected void onDestroy() {
super.onDestroy();
if (animation != null) {
animation.cancel();
animation = null;
}
}
二、IOS端
GEEK TALK
目前我们已经有了ARC(自动引用计数)来替代MRC(手动引用计数),申请的对象在没有被强引用时会自动释放。但在编码不规范的情况下,引用计数无法及时归零,还是会存在引入内存泄露的风险,这可能会造成一些非常严重的后果。以直播场景举例,如果直播业务的ViewController无法释放,会导致依赖于ViewController的点位统计数据异常,且用户关闭直播页面后仍然可以听到直播声音。熟悉内存泄漏场景、养成避免内存泄露的习惯是十分重要的。下面介绍一些iOS常见内存泄漏及解决方案。
2.1 block引起的循环引用
block引入的循环引用是常见的一类内存泄露问题。常见的引用环是对象->block->对象,此时对象和block的引用计数均为1,无法被释放。
[self.contentView setActionBlock:^{
[self doSomething];
}];
例子代码中,self强引用成员变量contentView,contentView强引用actionBlock,actionBlock又强引用了self,引入内存泄露问题。
解除循环引用,就是解除强引用环,需要将某一强引用替换为弱引用。如:
__weak typeof(self) weakSelf = self;
[self.contentView setActionBlock:^{
__strong typeof(weakSelf) strongSelf = weakSelf;
[strongSelf doSomething];
}];
此时actionBlock弱引用self,循环引用被打破,可以正常释放。
或者使用RAC提供的更简便的写法:
@weakify(self);
[self setTaskActionBlock:^{
@strongify(self);
[self doSomething];
}];
需要注意的是,可能和block存在循环引用的不仅仅是self,所有实例对象都有可能存在这样的问题,而这也是开发过程中很容易忽略的。比如:
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
Cell *cell = [tableView dequeueReusableCellWithIdentifier:@"identifer"];
@weakify(self);
cell.clickItemBlock = ^(CellModel * _Nonnull model) {
@strongify(self);
[self didSelectRowMehod:model tableView:tableView];
};
return cell;
}
这个例子中,self和block之间的循环引用被打破,self可以正常释放了,但是需要注意的是还存在一条循环引用链:tableView强引用cell,cell强引用block,block强引用tableView。这同样会导致tableView和cell无法释放。
正确的写法为:
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
Cell *cell = [tableView dequeueReusableCellWithIdentifier:@"identifer"];
@weakify(self);
@weakify(tableView);
cell.clickItemBlock = ^(CellModel * _Nonnull model) {
@strongify(self);
@strongify(tableView);
[self didSelectRowMehod:model tableView:tableView];
}; return cell;}
2.2 delegate引起的循环引用
@protocol TestSubClassDelegate <NSObject>
- (void)doSomething;
@end
@interface TestSubClass : NSObject
@property (nonatomic, strong) id<TestSubClassDelegate> delegate;
@end
@interface TestClass : NSObject <TestSubClassDelegate>
@property (nonatomic, strong) TestSubClass *subObj;
@end
上述例子中,TestSubClass对delegate使用了strong修饰符,导致设置代理后,TestClass实例和TestSubClass实例相互强引用,造成循环引用。大部分情况下,delegate都需要使用weak修饰符来避免循环引用。
2.3 NSTimer强引用
self.timer = [NSTimer timerWithTimeInterval:1 target:self selector:@selector(doSomething) userInfo:nil repeats:YES];
[NSRunLoop.currentRunLoop addTimer:self.timer forMode:NSRunLoopCommonModes];
NSTimer实例会强引用传入的target,就会出现self和timer的相互强引用。此时必须手动维护timer的状态,在timer停止或view被移除时,主动销毁timer,打破循环引用。
解决方案1:换用iOS10后提供的block方式,避免NSTimer强引用target。
@weakify(self);
self.timer = [NSTimer timerWithTimeInterval:1 repeats:YES block:^(NSTimer * _Nonnull timer) {
@strongify(self);
[self doSomething];
}];
解决方案2:使用NSProxy解决强引用问题。
// WeakProxy
@interface TestWeakProxy : NSProxy
@property (nullable, nonatomic, weak, readonly) id target;
- (instancetype)initWithTarget:(id)target;
+ (instancetype)proxyWithTarget:(id)target;
@end
@implementation TestWeakProxy
- (instancetype)initWithTarget:(id)target {
_target = target;
return self;
}
+ (instancetype)proxyWithTarget:(id)target {
return [[TestWeakProxy alloc] initWithTarget:target];
}
- (void)forwardInvocation:(NSInvocation *)invocation {
if ([self.target respondsToSelector:[invocation selector]]) {
[invocation invokeWithTarget:self.target];
}
}
- (NSMethodSignature *)methodSignatureForSelector:(SEL)aSelector {
return [self.target methodSignatureForSelector:aSelector];
}
- (BOOL)respondsToSelector:(SEL)aSelector {
return [self.target respondsToSelector:aSelector];
}
@end
// 调用
self.timer = [NSTimer timerWithTimeInterval:1 target:[TestWeakProxy proxyWithTarget:self] selector:@selector(doSomething) userInfo:nil repeats:YES];
2.4 非引用类型内存泄漏
ARC的自动释放是基于引用计数来实现的,只会维护oc对象。直接使用C语言malloc申请的内存,是不被ARC管理的,需要手动释放。常见的如使用CoreFoundation、CoreGraphics框架自定义绘图、读取文件等操作。
如通过CVPixelBufferRef生成UIImage:
CVPixelBufferRef pixelBuffer = CMSampleBufferGetImageBuffer(sampleBuffer);
CIImage* bufferImage = [CIImage imageWithCVPixelBuffer:pixelBuffer];
CIContext *context = [CIContext contextWithOptions:nil];
CGImageRef frameCGImage = [context createCGImage:bufferImage fromRect:bufferImage.extent];
UIImage *uiImage = [UIImage imageWithCGImage:frameCGImage];
CGImageRelease(frameCGImage);
CFRelease(sampleBuffer);
2.5 延迟释放问题
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(20 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
[self doSomething];
});
上述例子中,使用dispatch_after延迟20秒后执行doSomething方法。这并不会造成self对象的内存泄漏问题。但假设self是一个UIViewController,即使self已经从导航栈中移除,不需要再使用了,self也会在block执行后才会被释放,造成业务上出现类似内存泄露的现象。
在这种长时间的延时执行中,好也加入weakify-strongify对,避免强持有。
@weakify(self);
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(20 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
@strongify(self);
[self doSomething];
});
相关文章:
移动开发避坑指南——内存泄漏
在日常编写代码时难免会遇到各种各样的问题和坑,这些问题可能会影响我们的开发效率和代码质量,因此我们需要不断总结和学习,以避免这些问题的出现。接下来我们将围绕移动开发中常见问题做出总结,以提高大家的开发质量。本系列文章…...
太好玩了,我用 Python 做了一个 ChatGPT 机器人
毫无疑问,ChatGPT 已经是当下编程圈最火的话题之一,它不仅能够回答各类问题,甚至还能执行代码! 或者是变成一只猫 因为它实在是太好玩,我使用Python将ChatGPT改造,可以实现在命令行或者Python代码中调用。…...
STM32存储左右互搏 SDIO总线读写SD/MicroSD/TF卡
STM32存储左右互搏 SDIO总线读写SD/MicroSD/TF卡 SD/MicroSD/TF卡是基于FLASH的一种常见非易失存储单元,由接口协议电路和FLASH构成。市面上由不同尺寸和不同容量的卡,手机领域用的TF卡实际就是MicroSD卡,尺寸比SD卡小,而电路和协…...
累积分布函数图(CDF)的介绍、matlab的CDF图绘制方法(附源代码)
在对比如下两个误差的时候,怎么直观地分辨出来谁的误差更低一点?: 通过这种误差时序图往往不容易看出来。 但是如果使用CDF图像,以误差绝对值作为横轴,以横轴所示误差对应的累积概率为纵轴,绘制曲线图&am…...
代码随想录算法训练营第四十一天|343.整数拆分、96不同的二叉搜索树
文档链接:https://programmercarl.com/ LeetCode343.整数拆分 题目链接:https://leetcode.cn/problems/integer-break/ 思路: j * (i - j) 是单纯的把整数拆分为两个数相乘,而j * dp[i - j]是拆分成两个以及两个以上的个数相乘…...
全量知识系统 程序详细设计之 统一资产模型(QA-SmartChat)
Q1. 下面我们聊聊整个全知系统的设计 的矩阵和函数,矩阵表示的是“活物”,分别 类似 一个基因的活性、一个实体的辨识度和某种特征的可区分度。 函数的可微、可积和可导性 则表示 运动的控制方式 在全知系统设计中,矩阵和函数是两个核心的组…...
已解决org.springframework.web.client.HttpClientErrorException: 400异常的正确解决方法,亲测有效!!!
已解决org.springframework.web.client.HttpClientErrorException: 400异常的正确解决方法,亲测有效!!! 文章目录 问题分析 报错原因 解决思路 解决方法 总结 在日常开发过程中,通过Spring框架提供的RestTemplat…...
内网渗透-Windows内网渗透
内网渗透-Windows内网渗透 文章目录 内网渗透-Windows内网渗透前言一、信息收集 1.1、SPN1.2、端口连接1.3、配置文件1.4、用户信息1.6、会话收集1.7、凭据收集 navicat:SecureCRT:Xshell:WinSCP:VNC: 1.8、DPAPI1.9、域信任1.10、…...
机器人方向控制中应用的磁阻角度传感芯片
磁阻传感器提供的输出信号几乎不受磁场变动、磁温度系数、磁传感器距离与位置变动影响,可以达到高准确度与高效能,因此相当适合各种要求严格的车用电子与工业控制的应用。所以它远比采用其它传感方法的器件更具有优势。 机器人的应用日渐广泛࿰…...
如何在树莓派安装Nginx并实现固定公网域名访问本地静态站点
文章目录 1. Nginx安装2. 安装cpolar3.配置域名访问Nginx4. 固定域名访问5. 配置静态站点 安装 Nginx(发音为“engine-x”)可以将您的树莓派变成一个强大的 Web 服务器,可以用于托管网站或 Web 应用程序。相比其他 Web 服务器,Ngi…...
Ubuntu与主机windows共享文件夹
一、创建共享文件夹: 虚拟机->设置->选项->共享文件夹->总是启用->选择本地的共享文件夹(如E:\Share)->确定。 二、设置挂载: 首先赋予/etc/fstab文件可编辑的权限; sudo chmod 777 /…...
(四)C++自制植物大战僵尸游戏启动流程
植物大战僵尸游戏开发教程专栏地址http://t.csdnimg.cn/ErelL 一、启动方式 鼠标左键单机VS2022上方工具栏中绿色三角按钮(本地Windows调试器)进行项目启动。第一次启动项目需要编译项目中所有代码文件,编译生成需要一定的时间。不同性能的电…...
华为的AI战略地图上,才不是只有大模型
图片来源:pixabay© 钛媒体ToB深水区 图片来源:pixabay 大模型火热了一年,现在还没做AI化改造的企业,就像是工业革命浪潮伊始与火车赛跑的那辆马车。 最早的蒸汽火车缓慢又笨重,甚至铁轨上还预留了马匹行走的空…...
采用C#.Net +JavaScript 开发的云LIS系统源码 二级医院应用案例有演示
采用C#.Net JavaScript 开发的云LIS系统源码 二级医院应用案例有演示 一、系统简介 云LIS是为区域医疗提供临床实验室信息服务的计算机应用程序,可协助区域内所有临床实验室相互协调并完成日常检验工作,对区域内的检验数据进行集中管理和共享࿰…...
Vue3(三):生命周期、路由、自定义hooks
这里终于明白了为什么一直有这个语法报错,就是在提示你哪里错的地方上方注释一行/*eslint-disable*/,之前一直警告这个错误感谢老师! 一、vue2和vue3生命周期 还有一个问题就是父组件和子组件哪个先挂载完毕呢?答案是子组件先挂…...
UE4_导入内容_骨架网格体
FBX 导入支持 骨架网格体(Skeletal Mesh) 。这提供了一种简化的处理流程来将有动画的网格体从 3D应用程序中导入到虚幻引擎内,以便在游戏中使用。除了导入网格体外,如果需要,动画和变形目标都可以使用FBX格式 在同一文…...
第十五届蓝桥杯c++b组赛后复盘和真题展示
题目变成八道了,分数一百分可能,感觉拿奖难度还是很高 第一题是一个简单的握手问题 答案算出来1204,纯手写 第二题是 物理题 纯蒙,随便猜了个轨迹,答案具体忘了,最后是 .45 第三题暴力 第四题 我是傻逼…...
代码随想录 二叉树—二叉搜索树中的搜索
思路:当节点为空或者等于目标值,直接返回。由于是二叉搜索树,特点是左子树的值都小于根节点值,右子树的值均大于根节点,那么,左右子树的构建可以通过值的判断来递归调用。 c题解: /*** Defini…...
⑤-1 学习PID--什么是PID
PID 算法可以用于温度控制、水位控制、飞行姿态控制等领域。后面我们通过PID 控制电机进行说明。 自动控制系统 在直流有刷电机的基础驱动中,如果电机负载不变,我们只要设置固定的占空比(电压),电机的速度就会稳定在…...
【OTA】STM32-OTA升级——持续更新
【OTA】STM32-OTA升级——持续更新 文章目录 前言一、ymodem串口协议1、Ymodem 协议2、PC3、蓝牙4、WIFI云平台 二、UDS车载协议1.UDS协议 总结 前言 提示:以下是本篇文章正文内容,下面案例可供参考 一、ymodem串口协议 1、Ymodem 协议 STM32 Ymodem …...
java 字符集
ASCII 与 GBK ASCII:英文专用GBK:中文专用 万国码 unicode想要统一这个世界上所有的语言,所以创造了UTF-32但是使用32位,也就是4个字节,对于很多语言来说,过于奢侈,也会造成通信效率和存储效率变低 UTF-8 unicode 创造…...
Alibaba --- 如何写好 Prompt ?
如何写好 Prompt 提示工程(Prompt Engineering)是一项通过优化提示词(Prompt)和生成策略,从而获得更好的模型返回结果的工程技术。总体而言,其实现逻辑如下: (注:示例图…...
用html写一个雨的特效
<!DOCTYPE html> <html lang"en"> <head><meta charset"UTF-8"><title>雨特效</title><link rel"stylesheet" href"./style.css"> </head> <body> <div id"wrap-textu…...
前端 接口返回来的照片太大 加载慢如何解决
现象 解决 1. 添加图片懒加载 背景图懒加载 对背景图懒加载做的解释 和图片懒加载不同,背景图懒加载需要使用 v-lazy:background-image,值设置为背景图片的地址,需要注意的是必须声明容器高度。 <div v-for"img in imageList&quo…...
003 传参
文章目录 传参http 状态码传参方式(1)URL请求参数 key 与 方法中的形参名一致(2)URL请求参数 key与RequestParam("id") 中的别名一致(3) 形参是POJO类,URL 参数 key 与pojo类的 set方…...
QT写Windows按键输出(外挂)
一、前言 玩游戏的时候遇到些枯燥无味反反复复的按鼠标键盘的情况时,就想写个外挂自动释放。刚好在学qt所以试验了下QT能不能对外输出按键与鼠标。 二、思路 qt中的按键鼠标全是输入,没有直接对外输出键盘鼠标指令的类,但是我们换个思路&…...
Stable Diffusion之文生图模型训练
1、数据准备 提前准备好一组相关的照片。 在线的图片处理网站 BIRME - Bulk Image Resizing Made Easy 2.0 (Online & Free) 将图片转成统一大小,支持批量处理,效率高 2、生成提示词 进入stable diffusion webui页面 旧版直接使用 train/proproc…...
SpringBoot整合支付宝沙箱支付
环境说明:SpringBoot3.0.2 支付宝沙箱地址:沙箱地址 获取配置信息 因支付需要回调地址,回调地址必须是公网,如果有公网的话,那直接在下面配置文件填写自己的公网,没有的话,就需要我们借助第三…...
探索进程控制第一弹(进程终止、进程等待)
文章目录 进程创建初识fork函数fork函数返回值fork常规用法fork调用失败的原因 写时拷贝进程终止进程终止是在做什么?进程终止的情况代码跑完,结果正确/不正确代码异常终止 如何终止 进程等待概述进程等待方法wait方法waitpid 进程创建 初识fork函数 在…...
在mac环境下使用shell脚本实现tree命令
文章目录 使用ls实现tree使用find实现tree 使用ls实现tree 实现思路 使用ls -F 打印文件类型,如果是目录后面跟/,如果是可执行文件后面跟*;使用grep -v /$ 筛选文件排除目录,-v为反向筛选;使用grep /$ 仅筛选目录&am…...
中英网站建立/企业推广策划书
Ubuntu修改主机名参考 在CentOS7中,有三种定义的主机名: 1. 静态的(Static hostname) “静态”主机名也称为内核主机名,是系统在启动时从/etc/hostname自动初始化的主机名。 2. 瞬态的(Tansient hostname࿰…...
南通通州建设工程质量监督网站/慈溪seo排名
什么是readline readline允许从可读流中以逐行的方式读取数据,比如process.stdin等。 在node.js命令行模式下默认引入了readline模块,但如果是使用node.js运行脚本的话,则需要自己通过require(‘readline’)方式手动引入该模块。 怎么使用r…...
网站搭建和网页设计/app推广员怎么做
场景介绍 美国有一家做草地喷水头的公司,市场竞争异常激烈。他们想在产品上做一些突破,在做用户调研的时候发现,用户最大的问题是草地喷水头太费水,于是他们就想能不能让喷水头为用户节省水,比如下雨天就不用浇水了&am…...
我的世界做皮肤壁纸网站/厦门百度关键词seo收费
转自: http://rfyiamcool.blog.51cto.com/1030776/1335700/ 简单的说就是,每个硬件设备(如:硬盘、网卡等)都需要和 CPU 有某种形式的通信以便 CPU 及时知道发生了什么事情,这样 CPU 可能就会放下手中的事情去处理应急…...
公司自己做网站流程和备案/电子商务
从这篇文章起,我们看看Vue3.0的响应式部分源码。这部分reactive方法是核心,reactive方法如下: export function reactive<T extends object>(target: T): UnwrapNestedRefs<T> export function reactive(target: object) {// if…...
微信开放平台与个人网站怎么/排名sem优化软件
首先,推荐我很喜欢的奥迪公司关于服务的一个宣传片: 这个视频的内容让我看过之后一直记忆犹新。一个企业或者团队,如果能够培养每一个员工,并让他们具备如此优秀的服务意识和责任感是非常难能可贵的。它已经从一个工作准则上升到了…...