一.概述

随着业务的快速增加,商城app的大小也在迅速增加,一度超过了300M。安装包大小的不断增加对app下载成本,推广效率产生了比较大的影响。从2018年9月份我们对商城app开始了为期二期的专项瘦身工作:一期从V7.2.0-V7.5.2版本,共计瘦身46M(设备:iPhone X,iOS12、13)。为了进一步减小包大小,同时为了建立长效机制,从今年5月份开始了第二期的专项优化工作,二期优化从最高的V8.1.0版本的272M到现在的V8.3.6共计已完成瘦身41M,当然二期优化还在继续推进中。

在本次安装包瘦身过程中我们也遇到了不少坑,当然也积累了些经验,现在分享给大家。

二.优化方向

优化指标

itunes connect上有两种包大小显示:“Download Size”“Install Size”“Download Size”即下载包大小,超过150M需要使用无线网下载的限制就是这个大小(现在已经放宽到200M);“Install Size”即安装后占用的磁盘空间大小,在appstore上显示的也是这个大小,用户往往会误认为这是下载安装包消耗的流量大小。 所以一开始我们就将“Install Size”作为了优化指标。“Install Size”减小后,“Download Size”自然也会减小。

优化方向

ipa 解压后,JD4iPhone.app主要的成分如下:

  • Frameworks,动态库存放路径;
  • PlugIns,插件存放路径,如today extension;
  • 可执行文件;
  • Assets.car,Asset Catalog编译产物;
  • react.bundle,内置的ReactNative业务;
  • bundle,主要存放资源文件;
  • 其他文件;

这些都是包大小的主要影响因素,优化工作都是围绕这些元素进行,后续的监控点也主要是这些元素。

三.优化措施

1.动态库

由于iOS8对可执行文件__Text字段60M限制,商城App多个版本逼近60M大关,所以有不少模块以动态库的形式集成到工程中。在做启动优化的时候,流量较低的库都采用手动方式加载了,以优化前的V8.1.0 版本为例,共计19个动态库,总大小超过了100M(包含arm64armv7架构)。所以动态库的优化成了瘦身工作的重要组成部分。那么怎么给动态库瘦身呢?

1.1 梳理动态库使用方,精简代码

梳理之前我们以为使用精简代码的方式优化动态库会比较容易,比如去除无用代码,提取公共代码。后来发现这些库大部分来自第三方厂家...nm命令查看这部分库里面有很多重复的类库,因为都是商用的库,虽然努力推动着去优化,但是目前看来收益并不是很高。

1.2 strip

经过调研,我们了解到有两种去除动态库多余符号(符号表等)的方式:

  • 在链接时去除,即在动态库工程中Other Linker Flags中添加-s参数,经过测试:不管是在启动时加载,还是手动方式加载动态库都没问题。于是准备使用这个方案。然而,在执行的时候发现了一个严重的问题:加了此参数后,不能生成完整的dsym文件,这会影响崩溃后符号的解析。于是此方案作罢。

  • 使用strip -x命令处理动态库。因为是对动态库产物进行处理,所以不会对dsym产生影响,经过测试,strip后的动态库,也可以使用dsym文件找到符号。于是我们尝试在工程中添加脚本统一处理工程中的动态库。在添加脚本的时候遇到个问题:动态库被拷贝到沙盒的时候会签名,而我们的strip操作发生在这个后面。在debug环境下,加载动态库的时候会提示签名后动态库被修改的错误。而在release导出包的时候会重新对动态库进行签名。所以在release下不会有问题。最终,我们修改了脚本,只在release环境下,执行strip操作:

    if [ $CONFIGURATION == Release ]; then strip -x dylib路径
    fi
    

经过strip处理后共计减少28M(arm64+armv7),瘦身效果明显。

2.资源文件

在一期优化前通用包(包含多种指令集,分辨率图片,多种机型通用的包)中的图片在 13500 左右,在 V8.1.0 版本通用包中的的图片还11000 张以上。资源文件的数量非常之多,使用场景异常复杂。资源文件的优化自然成了我们花费精力最多的地方,我们需要一整套的方案去应对接下来的优化。

2.1 资源文件的归属

在优化之前,我们需要将不同的资源文件归属到对应的模块,落实到对应的负责人。得益于商城App高度的模块化,资源文件的归属、甚至获取模块代码大小变得很简单:

  • 根据资源文件在本地工程project.pbxproj所在模块的路径和ipa包中的资源文件进行匹配。

  • 分析 linkmap 文件,获取各个静态库组件代码部分在可执行文件中的占比。

2.2 单个资源文件大小统计

以往,我们常常以为本地开发工程中的资源文件大小就是最后安装包中的大小。而经过本次瘦身实践发现:很多图片在本地和安装包中的大小差异非常之大,往往相差几倍。通过调研知道 Apple 为了在优化 iPhone 设备读取 png 图片速度,将 png 转换成 CgBI 非标准的 png 格式:

  • extra critical chunk (CgBI)
  • byteswapped (RGBA -> BGRA) pixel data, presumably for high-speed direct blitting to the framebuffer
  • zlib header, footer, and CRC removed from the IDAT chunk
  • premultiplied alpha (color' = color * alpha / 255)

苹果的优化,对于大多数应用来说都是包大小的负优化,商城也不例外。所以一般的压缩(有损,无损)处理并不能达到很好的瘦身效果。而经过测试,并不是所有的资源文件都会被负优化

  • 放在根目录下png格式的图片。

  • 放在Asset Catalog中的png,jpg格式的图片,其中jpg会转成png。

放在根目录下的jpg,bundle中的png不会被优化,这个规律也在后续优化中起到了重要作用。

终上所述,我们决定使用安装包中的资源文件来统计大小。

2.2.1 Asset Catalog中的文件大小计算

Assest.car 做为 Asset Catalog 的编译产物,苹果也提供了命令行工具assetutil来获取car文件信息:

sudo xcrun --sdk iphoneos assetutil --info xxx/Assets.car > xxx/Assets.json

Assets.json 中的图片详细数据如下:

  {"AssetType" : "Image","BitsPerComponent" : 8,"ColorModel" : "RGB","Colorspace" : "srgb","Compression" : "lzvn","Encoding" : "ARGB","Idiom" : "universal","Image Type" : "kCoreThemeOnePartScale","Name" : "1001", //xxx.imageset 的文件名"NameIdentifier" : 11584,"Opaque" : false,"PixelHeight" : 48,"PixelWidth" : 72,"RenditionName" : "1001@3x.png",//工程文件中的实际图片名"Scale" : 3,"SHA1Digest" : "E34FCAC314E26DE7FF30442AA33E436B242AA4BA","SizeOnDisk" : 800,//占用的磁盘大小,Asset Catalog中的图片编译后的大小取改值。"Template Mode" : "automatic"},

最终我们使用SizeOnDisk 字段来获取图片大小。使用SizeOnDisk计算精度很高(所有图片的SizeOnDisk相加和car文件大小误差在1M以内)。

对于 Assets.car 的分析,还有个小插曲:我们最开始是使用 cartool 解压。分享模块在更换双十一大促氛围兜底图后,因为部分活动图片大于了32KB(微博分享缩略限制不能超过32KB),触发调用兜底图分享逻辑,分享失败,最后定位是因为 Apple 的负优化,将源大小为22KB,负优化后部分设备上大于了 32KB,而 cartool 解压出的图片大小为 18KB,所以放弃原先使用 cartool,改用 assetutil。

2.2.2 其他的资源文件大小计算

其他的文件大小计算方式相对简单,苹果的APFS文件系统的最小存储单元为4KB,即使只有几十字节大小的文件,占用的空间也是4KB。 对于安装包里面的独立文件我们使用4KB对齐的方式进行大小计算,有些大点的文件磁盘占用空间并不是4的整数倍,但大小相近,影响不大:

Math.ceil(size/4000)*4,size为代码、图片实际大小,单位字节;

在 MB、KB、Byte 之间的换也是对齐 Apple 使用的是 1000 而不是 1024(即1MB = 1000 KB = 1000*1000 Byte)。

2.3 模块所用资源文件大小,数量统计

在介绍模块的资源文件大小统计前,先简单介绍下 App Slicing ,在我们将ipa包提交到iTunes connect,App store会针对不同的设备,系统制作成不同的精简版app:可执行文件,动态库根据不用的指令集,Asset Catalog中的资源文件根据不同的屏幕分辨率进行分发,最终做到按需下载。

关于App Slicing 的内容详细叙述,感兴趣的可以查看App Thinning in Xcode。

App Slicing只会对在Asset Catalog的资源文件进行分发,而放在根目录,bundle中的资源文件不会分发,所以在统计模块所使用资源文件之前我们需要注意到这个特性。如果以通用包来统计模块使用的资源文件大小,数量,其实并不能真正反映此模块对整个安装包大小的影响。所以我们决定使用iPhone X,iOS11做为参考标准。

要统计单个设备的资源文件使用情况,一个方式是使用adhoc包导出支持单个设备的安装包统计,不过这样的方式需要每次集成后都需要单独打包,因为现在ci并不会出支持单个设备的包。后来我们发现assetutil除了可以导出car文件信息之外,还可以从通用包car文件导出指定设备的car文件,入参较多,经过尝试如下:

sudo xcrun --sdk iphoneos assetutil --idiom phone --subtype 570 --scale 3 --display-gamut srgb --graphicsclass MTL2,2 --graphicsclassfallbacks MTL1,2:GLES2,0 --memory 1 --hostedidioms car,watch xxx/Assets.car -o xxx/thinning_assets.car

从上文知道car以外的资源文件不会分发,获取指定设备的car文件后我们就可以计算出模块所用资源文件大小,数量。

2.4 资源文件优化

2.4.1 大资源文件优化

上文提到了苹果对图片的负优化,大图经过负优化后对安装包的大小影响更大,动辄几百K, 甚至上M 。这也是一期优化通过改造 Assets.car 中的 183 张图片能优化了近 30M 原因。

结合上文中负优化规律,改造处理方案如下:

  • 删除无用或者可以使用其他方案替换的图片;
  • 优先转网络下载,使用默认图/纯色兜底,如楼层背景图;
  • 不能转下载的使用压缩过的jpg格式图片。
  • 图片经过压缩后( 主要是tinypng有损压缩)后放到 bundle 中使用。

二期优化开始,对大资源的处理不在局限于 Assets.car 中的大图(大于50KB),对于放在 bundle 中的大图、音视频、模型文件针对这部分大文件,逐一梳理后并针对性处理,收益很高。

2.4.2 无用图片筛查

对于无用图片,毫无疑问就是删除,商城因为各个组件是以二进制的形式呈现,现有的组件化环境没有提供所有组件一键切换二进制和源码的能力,不能使用现有的源码扫描工具,扫描无用图片。

通过分析现有检测无用图片的原理:根据各种类型的源文件,通过正则表达式获取使用的图片名,获取使用图片集合,扫描所有图片名集合,取所有图片集合和使用图片集合差值获取无用图片集合;

获取到使用图片集合和所有图片集合,就可以过滤出无用图片。
既然扫描源码的路走不通,又不能放任不管,我们反其道而行通过安装包来扫描无用图片:

通过分析安装包中使用图片的文件有可执行文件、.plist、.js、.jsbundle、.nib、.stroyboardc等文件;可分为三类文件:

  • 可执行文件;
  • 可读文件(.plist、.js、.html);
  • 不可读文件(.nib、.storyboardc);

可执行文件通过 otool -v -s __TEXT __cstring 获取可执行文件中的 __TEXT segment 中的 __cstring section。__cstring 包含了可执行文件中的字符串常量(源码中的 @“xxx” 字符串);

不可读文件 .nib 和 .storyboardc 分别是 xib 和 storyboard 的构建产物。ibtool 是xib 和 stroyborad 的编译工具,通过 man 查看具 ibtool 的具体使用方法发现:--flatten NO --compile 组合使用的时候可以生成可运行、可执行的 .nib 和 stroyboardc 文件。

--flatten booleanWhen combined with the --compile option and a value of NO, --flatten instructs ibtool to produce an output file that is both runnable, and editable. This option is typically used when preparing a product for localization. If no value is specified, the default flattening option is YES.

查看 Xcode Build Settings, 提供了配置项:

商城使用 Cocoapods 管理组件化,在 Pods-xxx-resources.sh 文件也找到了 ibtool 的使用:

分别在 Build Settings 中设置 Flatten Compile Storyboard Files && Flatten Compile Xib Files 设置为 NO,Pods-xxx-resources.sh 中 ibtool 添加 --flatten NO 的入参后,编译后的 .nib 和 .storyboardc 点击右键显示包内容如下:

designtime.nib、designtime.storyborad 和 xib、stroyborad 一样本质是xml 文件。

(1)针对不同的文件,使用 otool正则和 直接读取将获取到的内容拼接成引用图片的超字符 str,遍历所有图片名是否被 str 包含;
(2)如果包含直接过滤;
(3)如果不包含,再判断是否是 image_%ld 这类相似图片,如果是过滤;
(4)开发人员确定无误后删除,存在部分字符串拼接被误扫的,添加白名单过滤。

最后我们扩展 ResourceHelper 的能力,支持通过构建产物扫描无用图,实践中除了不到二十张图片通过字符串拼接的图片被误扫外(添加白名单过滤),其他图片都验证为无用图片,商城中的无用图也成功的清除和监控起来。

版本 涉及组件 图片数量 对齐磁盘空间后图片总大小
v8.1.0 46 613 4.4M
v8.2.4 28 196 1.5M

2.4.3 转下载

在进行大图,无用图片处理的同时,我们也给出了便于本地图片转下载的方案,基本功能如下:

  • 模块内内置默认配置文件,支持对不同分辨率的机型加载对应的图片。
{"imageId1": {"3x": "url1","2x": "url2"},"imageId2": {"3x": "url3","2x": "url4"}...
}
  • 支持图片url的在线更新。
  • 支持在线的图片降质,webp压缩。

哪些图片适合转下载?

  • 功能性引导图
  • 背景图:如楼层背景,页面背景。
  • 标签,提示类的图片。
  • 其他入口较深的图片。

2.4.4 iconfont

即使经过了图片转下在,无用图片删除,但是工程中的图片数量还是极为可观,其中各种各样的icon图标占了不少的数量。为了进一步减少图片数量,我们引入了iconfont。
iconfont优点:

  • 矢量,缩放不失真。
  • 可以设置颜色。
  • 接入成本低,不需要引入额外的类库。

iconfont 可以解决因为icon大小,颜色不同而重新切图的窘境。
从京东内部的quark平台了解到目前已经可以很好的支持iconfont,目前已有模块在试用,后续我们也将会大规模推广。

2.4.5 监控

为了建立长效机制,我们拟出了资源文件使用规范,同时也搭建了资源分析系统,来跟踪各模块的资源使用情况,主要功能如下:

  • 安装包大小,资源文件大小数量,包成分的展示;
  • 各模块,包括JDReact模块资源文件使用情况的记录展示;
  • 各模块不同版本间的对比;
  • 违反资源文件使用规范模块的邮件触达(开发中);
  • 根据各模块的资源文件使用情况,动态给出优化建议(开发中);

2.5 内置的ReactNative业务

JDReact提供了预置和后装两种发布方式,而为了用户体验,大部分业务模块都选择使用预置包的方式。时间一长,文件的数量就越来越多。由于文件系统的4K对齐,对包大小的影响也是非常大。对内置的ReactNative业务优化如下:

  • 推动流量相对较低的模块转后装方式;
  • 根据资源文件使用规范,推动业务整改;

2.6 可执行文件

因为 iOS8 60M的限制,主执行文件的大小一直比较稳定,所以这部分工作的优先级放到了后面。

2.6.1无用类/方法

无用类通过 otool 逆向Mach-O文件 __DATA.__objc_classlist段和__DATA.__objc_classrefs 段获取所有 OC 类和被引用的类,两个集合差值为无用类集合,结合 nm -nm 得到地址和对应类名符号化无用类类名;根据商城的限制做过滤,规则如下:

  • otool 逆向 __DATA.__objc_nlclslist 获取实现 load 方法的类过滤(RN与原生的桥接类、Swizzle Method 类);
  • 通过 otool 逆向 __TEXT.__cstring 获取所有字符串常量,过滤通过 NSClassFromString 调用的;
  • 子类实例化,父类没有实例化,父类不会出现在中 __objc_classrefs,通过 otool -oV 逆向出类的继承关系,过滤出子类被实例化(NSClassFromString 调用),父类没有实例化(NSClassFromString 调用)的类;
  • 过滤使用 Plist 文件引用的类;

无用方法 通过 otool 逆向 __DATA.__objc_selrefs 段获取使用到的方法,通过 otool -oV 获取实现的所有方法取差值。然后过滤掉 setter、getter、系统方法和协议、自定义的协议、sel 调用。

结合 linkMap 映射出无用的方法和类归属的组件,并且初步量化大小,如下所示:

  业务组件 数量 大小 基础组件 数量 大小
无用类 41 214 590KB 22 61 212KB
无用方法 64 1434 430KB 49 1182 259KB

因为基础组件中的无用方法和类,不能确定是否被非商城的 App 使用,只能对业务组件优化,考虑到涉及组件众多,并且收益和工程量不成正比,并且删除方法风险比较大,将无用方法和类优化的优先级后滞。

2.7 插件

在ipa包中我们也注意到了PlugIns目录,这里主要存放一些插件,比如today extension,share extension等,虽然这些插件在整个ipa包中的大小占比不大,但是我们还是决定梳理下有没有优化点。梳理后发现这些插件对于一些基础类库(网络框架,图片加载框架等)的使用都是以拷贝代码的方式加到工程中...我们知道其实这些类库完全可以和主app共享,因为主app中这些库是以动态库的形式使用的。经过优化后,成功的将today extension的大小减少了0.9M(嗯~蚊子虽小...)。

2.8 Xcode配置

除了以上提到的优化点,我们也对Xcode对包大小优化的一些相关配置做了尝试:

2.8.1 Link-Time Optimization(LTO)

苹果在WWDC2016对LTO的介绍如下:

What is Link-Time Optimization (LTO)?
Maximize runtime performance by optimizing at link-time
Inline functions across source files
Remove dead code
Enable powerful whole program optimizations

通过修改Build Settings中的Link-Time Optimization=Incremental,测试后ipa包减少4M,后续经过灰度验证后可以考虑打开。

2.8.2 Compress PNG Files & Remove Text Metadata From PNG Fils

上文提到的 负优化 使png格式图片增大,那么能否关闭负优化?在尝试将 Compress PNG Files 设置为 NO 对包大小没有任何影响,想放弃又不甘心,通过创建新的 Demo 工程测试,通过查看 Build 日志发现是通过copypng 将原 png 图片复制到构建产物根目录的,幸运的是 copypng 不是一个可执行文件,而是一个由 perl 编写的脚本。 copypng部分源码如下:

#!/usr/bin/perlmy $PNGCRUSH = `xcrun -f pngcrush`;
chomp $PNGCRUSH;my $compress = 0;
my $stripPNGText = 0;
my @FILES = ();# Gather command line options.
while( @ARGV ) {$_ = shift @ARGV;next if ( $_ eq "" );if ( $_ =~ /-strip-PNG-text/ ) {$compress = 1;$stripPNGText = 1;next;}if ( $_ eq "-compress" ) {$compress = 1;next;}
}
my @args;
if ( $compress ) {@args = ( $PNGCRUSH, "-q", "-iphone", "-f", "0" );if ( $stripPNGText ) {push ( @args, "-rem", "text" );}push ( @args, $SRCFILE, $DSTFILE );
} else {@args = ( "cp", "$SRCFILE", "$DSTFILE" );
}

其中 Compress PNG Files 和 Remove Text Metadata From PNG Fils 分别对应入参为 -compress 和 -strip-PNG-text。看到源码,即使我们不懂 perl 也应该明白了。 为什么 Compress PNG Files 设置为 NO,不能取消负优化,要想取消根目录下的负优化,需要将 Compress PNG Files 和 Remove Text Metadata From PNG File 都设置为 NO 才能取消

测试将 Compress PNG Files 和 Remove Text Metadata From PNG File 设置为 NO 之后安装包优化 1.6M

同时也探究其对 Assets.car 的影响,通过对比是取消负优化对 Assets.car 的编译工具 actool 的影响,取消后没有 --compress-pngs 的入参。

--compress-pngs
PNGs copied into iOS targets will be processed using pngcrush to optimize reading the images on iOS devices. This has no effect for images that wind up in the compiled CAR file, as it only affects PNG images copied in to the output bundle.

在验证了 Compress PNG Files 和 Remove Text Metadata From PNG File 对 actool 是否有入参 --compress-pngs 的关系 后,我们也验证了对大小的影响,结论是取消负优化,不会影响 Assets.car 的大小

在二期优化过程中通过梳理根目录下的图片,现在只剩下 AppIcon 和 Launch Iamge,对于 Launch Iamge 我们通过 Launch Screen Storyboard 只保留一份启动图优化包大小,不考虑取消负优化。同时通过创建一个新工程只给 Asset Catalog 中添加图片, 查看 Build Settings 是没有这两项配置项,可是 Build 日志 actool 也是有 --compress-pngs 的入参。相信 Apple 已经给我们做了最佳的选择。

2.8.3 Asset Catalog Compiler 之 Optimization

对于 Optimization 中的 space 的优化,在一期就想通过灰度验证是否有其他影响,如果没有影响后启用,因为那时启用精简包可以优化十几M,在后面重点开始优化 Assets.car 后,考虑到启用之后可能会消极的优化 Assets.car 就搁置,到目前商城最新版 8.4.0 以 iPhoneX 的 adhoc 包数据对比重点优化 Assets.car 后,启用也只在 iOS12 以下 1.1M 的影响。

iPhoneX iOS 11 iOS 12 iOS 13
default 17.3M 15.5M 14.9M
space 16.2M 15.5M 14.9M
time 17.3M 15.5M 14.9M

Apple 在 iOS 12 Optimizing App Assets。space 也不准备启用,还是那句话相信 Apple 已经给我们做了最佳的选择。

3.最后

以上即为商城App包大小优化的主要措施,希望对大家有用。随着业务的快速发展,包大小的增加在所难免。但是在整个业务迭代过程中,我们需要更加合理的设计规范,资源文件使用规范,合理的业务准入准出原则。这样我们的安装包大小才能得到较好的控制,毕竟优化过程也是相当痛苦的。

京东商城iOS客户端安装包瘦身实践相关推荐

  1. iOS App 安装包瘦身指南

    以下列出了安装包瘦身的无脑执行流程,其中"奇技淫巧"部分为选做题 资源优化 删除无用图片 使用LSUnusedResources查找无用图片 删除重复资源 压缩图片资源 使用Ima ...

  2. Unity3D_NGUI_安卓APK安装包瘦身实践

    减包瘦身是个精细活.本文整理了0907版本操作过程,以备日后参考. 经过一番折腾,各位攻城狮的努力,美术设计师的支持,策划爷的理解,UI资源(图集.字体.单局外模型贴图)从45.4MB减少到24.5M ...

  3. iOS安装包瘦身的那些事儿

    在我们提交安装包到App Store的时候,如果安装包过大,有可能会收到类似如下内容的一封邮件: 收到这封邮件的时候,意味着安装包在App Store上下载的时候,有的设备下载的安装包大小会超过100 ...

  4. Android App安装包瘦身计划

    Android App安装包瘦身计划 Android App安装包体积优化: 理由, 指标和可以采用的方法. 本文内容归纳如下图: 为什么要安装包瘦身 安装包需要瘦身吗? 不需要吗? 安装包要瘦身的主 ...

  5. 贝壳找房APP安装包瘦身

    一.背景 在满足产品.UI要求的前提下,尽量减少安装包体积. 以customer插件为例: 插件大小为5MB, 其中1MB是dex,而bkbase占了dex的580KB: 3.1MB是图片. 减少re ...

  6. iOS App 瘦身实践总结

    文章最后有我的 12 条小总结. 原文始发地址:我的 GitHub 写在前面 最近公司需求不多,正好研究一下 App 瘦身的办法,写了点小总结. 如果你不知道下面几个问题,不妨可以看看文章. 使用 . ...

  7. iOS - ipa安装包大小优化

    在App Store上显示的下载大小和实际下载下来的大小,我们通过下表做一个对比: iPhone型号 系统 AppStore 显示大小 下载到设备大小 iPhone6 10.2.1 91.5MB 88 ...

  8. iOS 启动优化和安装包瘦身

    iOS 启动优化和安装包瘦身 1 启动优化 在iPhone的启动方式中,分为冷启动和热启动两种方式: 1.冷启动(Cold Launch):从零开始启动APP ,需要系统新创建一个进程进行启动,这是一 ...

  9. 完美高仿精仿京东商城手机客户端android版源码

    转自http://blog.csdn.net/xiaocnblogs/article/details/45815445, 完美高仿精仿京东商城手机客户端android版源码,喜欢的朋友可以下载吧. 源 ...

最新文章

  1. Redis 限流的 3 种方式,还有谁不会!
  2. 从ACT-R探讨认知智能
  3. CLR Profiler 性能分析工具 (转)
  4. 【Groovy】Groovy 动态语言特性 ( Groovy 中函数实参自动类型推断 | 函数动态参数注意事项 )
  5. freeMarker自定义函数的使用
  6. 135. Leetcode 46. 全排列 (回溯算法-排列问题)
  7. JS判断是否选中的是表格内当前选中的那一行
  8. Azure Table storage 基本用法 -- Azure Storage 之 Table
  9. QCon速递:Xen漏洞热补丁修复、异地双活、ODPS新功能与金融互联网
  10. 日志显示TypeError: Failed to fetch报错与TypeError: NetworkError when attempting to fetch resource报错
  11. 引用dll文件要提交解决方案
  12. TP5模型修改器和读取器
  13. top 命令按内存和 cpu 排序
  14. 【RRT三维路径规划】基于matlab RRT算法无人机三维路径规划【含Matlab源码 1270期】
  15. 华为初级证书-HCIA(HCNA)-RS(Routing Switching)
  16. 简单高效实现导出excel数据表格
  17. 大数据模型案例库-涵盖多行业,超百个实战项目案例
  18. html 下划线 鼠标手,(腾讯设计规范.ppt
  19. 怎么用c语言画余弦函数,用c语言绘制余弦函数图像
  20. 尚硅谷前端视频总结(二)

热门文章

  1. 射频百科:双工器是什么?双工器工作原理
  2. synology nfs_如何手动和自动关闭和重新启动Synology NAS
  3. 求教Latex的双栏表格排版
  4. 相机快门之:电子快门
  5. 洛谷P2619 [国家集训队]Tree I 题解
  6. 基于单片机的超市储物柜设计_基于单片机的自动存储柜的设计
  7. matlab输出以下图形,输出高品质MATLAB图形的方法与技巧_陈丽安.pdf
  8. 魅族便签,能否成为国内便签应用的No.1?
  9. 新媒体推广之今日头条
  10. 谈新手Java SE JDK 下载那个版本好!