总结

标号 主题 内容
autorelease autorelease基本概念/自动释放池/autorelease基本使用
autorelease注意事项 注意点/应用场景
ARC 什么是ARC/ARC的注意点和优点/ARC的判断原则/ARC机制判断/ARC快速使用
ARC下的内存管理 ARC下单对象内存管理/多对象内存管理/循环引用问题/下@property参数
ARC和MRC兼容和转换 ARC模式下如何兼容非ARC的类/如何将MRC转换为ARC
Category 什么是Category/Category的格式/注意事项
 
类扩展 什么是类扩展/类扩展书写格式
**Block 什么是Block/block的格式/应用场景/注意事项
typedef和Block 函数指针回顾/bl

一.autorelease

  • autorelease其实就是将release延迟
1.autorelease基本概念
  • autorelease是一种支持引用计数的内存管理方式,只要给对象发送一条autorelease消息,会将对象放到一个自动释放池中,当自动释放池被销毁时,会对池子里面的所有对象做一次release操作

    • 注意,这里只是发送release消息,如果当时的引用计数(reference-counted)依然不为0,则该对象依然不会被释放。

  • autorelease方法会返回对象本身

Person *p = [Person new];
p = [p autorelease];
//只要调用autorelease就不用调用release了
  • 调用完autorelease方法后,对象的计数器不变
Person *p = [Person new];
p = [p autorelease];
NSLog(@"count = %lu", [p retainCount]); // 1
  • autorelease的好处

    • 不用再关心对象释放的时间
    • 不用再关心什么时候调用release
  • autorelease的原理

    • autorelease实际上只是把对release的调用延迟了,对于每一个autorelease,系统只是把该 Object放入了当前的autorelease pool中,当该pool被释放时,该pool中的所有Object会被调用Release。
2.自动释放池
  • 创建自动释放池格式:
  • iOS 5.0前
NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init]; // 创建自动释放池
[pool release]; // [pool drain]; 销毁自动释放池
  • iOS 5.0 开始
@autoreleasepool
{ //开始代表创建自动释放池 } //结束代表销毁自动释放池
  • 在iOS程序运行过程中,会创建无数个池子。这些池子都是以栈结构存在(先进后出)

  • 当一个对象调用autorelease方法时,会将这个对象放到栈顶的释放池

3.autorelease基本使用
NSAutoreleasePool *autoreleasePool = [[NSAutoreleasePool alloc] init];

Person *p = [[[Person alloc] init] autorelease];

[autoreleasePool drain];

@autoreleasepool
{ // 创建一个自动释放池
Person *p = [[Person new] autorelease];
} // 销毁自动释放池(会给池子中所有对象发送一条release消息)
4.注意点
  • 一定要在自动释放池中调用autorelease,才会将对象放入自动释放池中
  • 在自动释放池中创建了对象,一定要调用autorelease,才会将对象放入自动释放池中
  • 只要在自动释放池中调用autorelease就会将对象放入自动释放池中 -

二.autorelease注意事项

1.autorelease使用注意
  • 并不是放到自动释放池代码中,都会自动加入到自动释放池
 @autoreleasepool {
// 因为没有调用 autorelease 方法,所以对象没有加入到自动释放池
Person *p = [[Person alloc] init];
[p run];
}
  • 在自动释放池的外部发送autorelease 不会被加入到自动释放池中

    • autorelease是一个方法,只有在自动释 放池中调用才有效。
 @autoreleasepool {
}
// 没有与之对应的自动释放池, 只有在自动释放池中调用autorelease才会放到释放池
Person *p = [[[Person alloc] init] autorelease];
[p run]; // 正确写法
@autoreleasepool {
Person *p = [[[Person alloc] init] autorelease];
} // 正确写法
Person *p = [[Person alloc] init];
@autoreleasepool {
[p autorelease];
}
  • 自动释放池的嵌套使用

    • 自动释放池是以栈的形式存在
    • 由于栈只有一个入口, 所以调用autorelease会将对象放到栈顶的自动释放池
    • 栈顶就是离调用autorelease方法最近的自动释放池

      @autoreleasepool { // 栈底自动释放池
      @autoreleasepool {
      @autoreleasepool { // 栈顶自动释放池
      Person *p = [[[Person alloc] init] autorelease];
      }
      Person *p = [[[Person alloc] init] autorelease];
      }
      }
  • 自动释放池中不适宜放占用内存比较大的对象

    • 尽量避免对大内存使用该方法,对于这种延迟释放机制,还是尽量少用
    • 不要把大量循环操作放到同一个 @autoreleasepool之间,这样会造成内存峰值的上升
// 内存暴涨
@autoreleasepool {
for (int i = 0; i < 99999; ++i) {
Person *p = [[[Person alloc] init] autorelease];
}
} // 内存不会暴涨
for (int i = 0; i < 99999; ++i) {
@autoreleasepool {
Person *p = [[[Person alloc] init] autorelease];
}
}
  • 一个程序中可以创建N个自动释放池,并且自动释放池可以嵌套

    • 自动释放池是以栈的形式存储的,栈先进后出
    • 给一个对象方法一条@autorelease,永远会将对象放入栈顶的自动释放池中
@autoreleasepool {
@autoreleasepool {
@autoreleasepool {
Person *p = [[[Person alloc] init] autorelease];
}//在此处p就销毁了
}
}
2.autorelease错误用法
  • 不要连续调用autorelease

    • 一个alloc/new对应一个autorelease或者release
 @autoreleasepool {
// 错误写法, 过度释放
Person *p = [[[[Person alloc] init] autorelease] autorelease];
}
  • 调用autorelease后又调用release
 @autoreleasepool {
Person *p = [[[Person alloc] init] autorelease];
[p release]; // 错误写法, 过度释放
}
3.应用场景(面试)
  • 创建类工厂方法时,要创建一个类方法,并且加上autorelease
  • 注意
    • Foundation框架的类,但凡是通过类工厂方法创建对象时都是autorelease的

三.ARC

1.什么是ARC
  • Automatic Reference Counting,自动引用计数

    • 手动管理内存, 可以简称MRC (Manual Reference Counting)

  • 在工程中永远不写retain,release和autorelease三个关键字就好~这是ARC的基本原则。

  • 当ARC开启时,编译器将自动在代码合适的地方插入retain, release和autorelease

2.ARC的注意点和优点
  • ARC的注意点

    • ARC是编译器特性,而不是运行时特性(Xcode的功能)
    • ARC不是其它语言中的垃圾回收,有着本质区别,不是定时查看,而是运行程序
    • 不能在重写dealloc中调用[super dealloc]
    • 不能写autorelease/release/retain
  • ARC的优点

    • 完全消除了手动管理内存的烦琐
    • 基本上能够避免内存泄露
    • 有时还能更加快速,因为编译器还可以执行某些优化
3.ARC的判断原则(没有引用计数)
  • ARC的判断原则

    • 只要还有一个强指针变量指向对象,对象就会保持在内存中
  • 强指针

    • 默认情况下,所有指针变量都是强指针
    • 被__strong修饰的指针
int main
{
{
Person *p = [ [Person alloc] init]; }//在此处被释放
} int main
{
{
Person *p = [ [Person alloc] init];
p = nil;//在此处被释放
}
} int main
{
{
__strong Person *p = [ [Person alloc] init];
__weak Person *p2 = p; }//在此处被释放
} int main
{
{
__strong Person *p = [ [Person alloc] init];
__weak Person *p2 = p;
p = nil;//在此处被释放
}
} - 强指针
Person *p1 = [[Person alloc] init];
__strong Person *p2 = [[Person alloc] init];
  • 弱指针

    • 被__weak修饰的指针
    • 如果用一个弱指针保存刚创建的对象,就会立即释放
__weak  Person *p = [[Person alloc] init];//立马被释放掉
  • 注意:当使用ARC的时候,暂时忘记“引用计数器”,因为判断标准变了。

4. 单个对象的内存管理
  • 不用时就赋值为nil,保存时用strong

ARC快速入门

1.ARC机制判断
  • ARC机制下有几个明显的标志:

    • 不允许调用对象的 release方法
    • 不允许调用 autorelease方法
    • 再重写父类的dealloc方法时,不能再调用 [super dealloc];
2.ARC快速使用
int main(int argc, const char * argv[]) {
// 不用写release, main函数执行完毕后p会被自动释放
Person *p = [[Person alloc] init];
return 0;
}

ARC下的内存管理

  • 在ARC中保存一个对象用strong
1.ARC下单对象内存管理
  • 局部变量释放对象随之被释放
int main(int argc, const char * argv[]) {
@autoreleasepool {
Person *p = [[Person alloc] init];
} // 执行到这一行局部变量p释放
// 由于没有强指针指向对象, 所以对象也释放
return 0;
}
  • 清空指针对象随之被释放
int main(int argc, const char * argv[]) {
@autoreleasepool {
Person *p = [[Person alloc] init];
p = nil; // 执行到这一行, 由于没有强指针指向对象, 所以对象被释放
}
return 0;
}
  • 默认清空所有指针都是强指针
int main(int argc, const char * argv[]) {
@autoreleasepool {
// p1和p2都是强指针
Person *p1 = [[Person alloc] init];
__strong Person *p2 = [[Person alloc] init];
}
return 0;
}
  • 弱指针需要明确说明

    • 注意: 千万不要使用弱指针保存新创建的对象
int main(int argc, const char * argv[]) {
@autoreleasepool {
// p是弱指针, 对象会被立即释放
__weak Person *p1 = [[Person alloc] init];
}
return 0;
}
2.ARC下多对象内存管理
  • ARC和MRC一样,想拥有某个对象必须用强指针保存对象,但是不需要在dealloc方法中release
@interface Person : NSObject

// MRC写法
//@property (nonatomic, retain) Dog *dog; // ARC写法
@property (nonatomic, strong) Dog *dog; @end
3.ARC下循环引用问题
  • ARC和MRC一样, 如果A拥有B, B也拥有A,那么必须一方使用弱指针
  • 在ARC中如果保存对象不要用assign,而是用weak
    • assign是专门用于保存基本数据类型的
@interface Person : NSObject

//@property (nonatomic, retain) Dog *dog;
@property (nonatomic, strong) Dog *dog; @end @interface Dog : NSObject // 错误写法, 循环引用会导致内存泄露
//@property (nonatomic, strong) Person *owner; // 正确写法, 当如果保存对象建议使用weak
//@property (nonatomic, assign) Person *owner;
@property (nonatomic, weak) Person *owner;
@end
4.ARC下@property参数
  • strong : 用于OC对象,相当于MRC中的retain
  • weak : 用于OC对象,相当于MRC中的assign
  • assign : 用于基本数据类型, 跟MRC中的assign一样

ARC和MRC兼容和转换

1.ARC模式下如何兼容非ARC的类

在Build Phases中

  • 转变为非ARC -fno-objc-arc
  • 转变为ARC的, -f-objc-arc (不常用)
2.如何将MRC转换为ARC
  • 先将项目转换成MRC
  • Edit-->Convert--> To Object-C ARC --> Check-->Next-->Save
  • 转换大项目时时常会出错

Category

1.什么是Category
  • Category有很多种翻译: 分类 \ 类别 \ 类目 (一般叫分类)

  • Category是OC特有的语法, 其他语言没有的语法

  • Category的作用

    • 可以在不修改原来类的基础上, 为这个类扩充一些方法(还有继承的方法来实现)
    • 一个庞大的类可以分模块开发,有利于管理\多人开发
2.Category的格式
  • 在.h文件中声明类别

    • 1)新添加的方法必须写在 @interface 与 @end之间
    • 2)ClassName 现有类的类名(要为哪个类扩展方法)
    • 3)CategoryName 待声明的类别名称
    • 4)NewMethod 新添加的方法
      //分类的声明
      @interface ClassName (CategoryName)
      NewMethod; //在类别中添加方法
      //不允许在类别中添加变量
      @end
      • 注意: 1)不允许在声明类别的时候定义变量
  • 在.m文件中实现类别:

    • 1)新方法的实现必须写在@ implementation与@end之间
    • 2)ClassName 现有类的类名
    • 3)CategoryName 待声明的类别名称
    • 4)NewMethod 新添加的方法的实现
//分类的实现
@implementation ClassName(CategoryName) NewMethod
... ...
@end
  • 使用Xcode创建分类
选择:Object - C File
file Type: Category
Class:Person

Category注意事项

1.分类的使用注意事项
  • 分类只能增加方法, 不能增加成员变量
@interface Person (NJ)
{
// 错误写法
// int _age;
}
- (void)eat;
@end
  • 分类中写property只会生成setter/getter方法声明
@interface Person (NJ)
// 只会生成getter/setter方法的声明, 不会生成实现和私有成员变量
@property (nonatomic, assign) int age;
@end
  • 分类可以访问原来类中的成员变量
@interface Person : NSObject
{
int _no;
}
@end @implementation Person (NJ)
- (void)say
{
NSLog(@"%s", __func__);
// 可以访问原有类中得成员变量
NSLog(@"no = %i", _no);
}
@end
  • 如果分类和原有类出现同名的方法, 优先调用分类中的方法, 原有类中的方法会被忽略(在开发中不要这样做)
@implementation Person

- (void)sleep
{
NSLog(@"%s", __func__);
}
@end @implementation Person (NJ)
- (void)sleep
{
NSLog(@"%s", __func__);
}
@end int main(int argc, const char * argv[]) {
Person *p = [[Person alloc] init];
[p sleep];
return 0;
} 输出结果:
-[Person(NJ) sleep]
2.分类的编译的顺序
  • 多个分类中与原有类有同名方法,则执行最后编译的文件方法(注意开发中千万不要这么干)
@implementation Person

- (void)sleep
{
NSLog(@"%s", __func__);
}
@end @implementation Person (NJ)
- (void)sleep
{
NSLog(@"%s", __func__);
}
@end @implementation Person (MJ)
- (void)sleep
{
NSLog(@"%s", __func__);
}
@end int main(int argc, const char * argv[]) {
Person *p = [[Person alloc] init];
[p sleep];
return 0;
} 输出结果:
-[Person(MJ) sleep]
  • 方法调用的优先级(从高到低)

    • 分类(最后参与编译的分类优先)
    • 原有类
    • 父类

七.类扩展(匿名分类)

1.什么是类扩展
  • 延展类别又称为扩展(Extendsion),Extension是Category的一个特例

  • 可以为某个类扩充一些私有的成员变量和方法

    • 写在.m文件
    • 英文名是Class Extension
2.类扩展书写格式
@interface 类名 ()
@end
    • 对比分类, 就少了一个分类名称,因此也有人称它为”匿名分类”

八.Block

1.什么是Block
  • Block是iOS中一种比较特殊的数据类型(定义变量/作为形参/作为返回值类型)
  • 初始化方式
    • 定义的同时初始化
    • 先定义后初始化
  • Block应用场景比较广泛

    • 动画
    • 多线程
    • 集合遍历
    • 网络请求回调
  • Block的作用

    • 用来保存某一段代码,可以在恰当的时间再取出来调用
    • 功能类似于函数和方法
  • 注意

    • 如果没有形参,那么^后面的(形参列表)可以不写
2.block的格式
  • Block的定义格式
返回值类型 (^block变量名)(形参列表) = ^(形参列表) {

};
  • block最简单形式
void (^block名)() = ^{代码块;}

例如:
void (^myBlock)() = ^{ NSLog(@"贺梦洁"); };
  • block带有参数的block的定义和使用
void (^block名称)(参数列表)
= ^ (参数列表) { // 代码实现; } 例如:
void (^myBlock)(int) = ^(int num){ NSLog(@"num = %i", num); };
  • 带有参数和返回值的block
返回类型 (^block名称)(参数列表)
= ^ (参数列表) { // 代码实现; } 例如:
int (^myBlock)(int, int) = ^(int num1, int num2){ return num1 + num2; };
  • 调用Block保存的代码,必须调用block才会执行
block变量名(实参);
3.Block应用场景
  • 当发现代码的前面和后面都是一样,只是中间部分不一样的时候,就要用Block

    • Block可以包含一段或者多段代码,所以用Block
  • Block调用:Block回调

4.Block注意事项
  • 在block内部可以访问block外部的变量
int  a = 10;
void (^myBlock)() = ^{
NSLog(@"a = %i", a);
}
myBlock();
输出结果: 10
  • block内部也可以定义和block外部同名的变量(局部变量),此时局部变量会暂时屏蔽Block外部的同名变量
int  a = 10;
void (^myBlock)() = ^{
int a = 50;
NSLog(@"a = %i", a);
}
myBlock();
输出结果: 50
  • 默认情况下, Block内部不能修改外面的局部变量

    • 因为Block中的变量和外界的变量并不是同一个变量
    • 如果Block访问外界的变量,Block会将外界的变量拷贝一份到堆内存中
int b = 5;
int a = 10;
void (^myBlock)() = ^{
b = 20; // 报错
NSLog(@"b = %i", b);
};
a = 20;
myBlock();
//结果:10
//因为Block中使用的外界的bl是拷贝的,所以在调用之前修改外界变量的值,不会影响Block中的copy的值
  • Block内部可以使用__block修改的外界变量的值

    • 如果在Block中修改了外界变量的值,会影响到外界变量的值

      __block int b = 5;
      void (^myBlock)() = ^{
      b = 20;
      NSLog(@"b = %i", b);
      };
      myBlock();
      输出结果: 20
  • 为什么不加就不能修改?加了就可以在Block中修改外界变量的值?
    • 不加__block是值传递,所以不能修改外界变量的值.
    • 加__block是地址传递,所以可以在block中修改外界变量的值.
C++文件是.cpp
  • 面试中block是存储在堆中还是栈中?

    • block可以存储在堆中也可以在栈中
    • 默认是栈中,如果对block进行一个copy操作,block会转移到
    • 如果block在栈中,block中访问了外界的对象,那么不会对对象进行retain操作
    • 但如果block在中,block中访问了外界的对象,那么会对对象进行一次retain操作
    • 如果在block中访问了外界变量,一定要给对象加上__block,只要加上__block,那么就不会对外界变量进行retain操作
    • 如果在ARC中需要在前面加上__weak
      • Person *p = [ [Person alloc] init];
      • __weak Person *weaP = p;
    • 如果在iOS开发时,在ARC中不这样就容易导致循环引用

九. typedef和Block

1.函数指针回顾
  • 函数指针使用
//加法
int sum(int value1, int value2)
{
return value1 + value2;
}
//减法
int minus(int value1, int value2)
{
return value1 - value2;
}
//主函数
int main(int argc, const char * argv[]) {
//函数指针
int (*sumP) (int, int) = sum;
int res = sumP(10, 20);
NSLog(@"sum = %i", res); //函数指针
int (*minusP) (int , int) = minus;
NSLog(@"minus = %i", minusP(10, 20));
return 0;
}
  • 函数指针别名
//起别名
typedef int (*calculate) (int, int);
//主函数
int main(int argc, const char * argv[]) {
//函数指针
calculate sumP = sum;
int res = sumP(10, 20);
NSLog(@"res = %i", res);
//函数指针
calculate minusP = minus;
NSLog(@"res = %i", minusP(10, 20));
return 0;
}
2.block和typedef
  • block使用

    int main(int argc, const char * argv[]) {
    //Block
    //定义的同时进行初始化
    int (^sumBlock) (int, int) = ^(int value1, int value2){
    return value1 + value2;
    };
    int res = sumBlock(10 , 20);
    NSLog(@"sum = %i", res); //先定义后初始化
    int (^minusBlock) (int, int) ;
    minusBlock = ^(int value1, int value2){
    return value1 - value2;
    };
    NSLog(@"minus = %i", minusBlock(10 , 20)); return 0;
    }
  • block别名

//给Block起别名
typedef int (^calculateBlock) (int, int);
//主函数
int main(int argc, const char * argv[])
{
//加法
calculateBlock sumBlock = ^(int value1, int value2)
{
return value1 + value2;
};
NSLog(@"sum = %i", sumBlock(10, 20));
//减法
calculateBlock minusBlock = ^(int value1, int value2)
{
return value1 - value2;
};
res = minusBlock(10, 20);
NSLog(@"res = %i", res); return 0;
}
 

OC-ARC,类扩展,block的更多相关文章

  1. OC分类(类目/类别) 和 类扩展 - 全解析

    OC分类(类目/类别) 和 类扩展 - 全解析   具体见: oschina -> MyDemo -> 011.FoundationLog-OC分类剖析 http://blog.csdn. ...

  2. OC中的类扩展

    类扩展 是在原有类的基础扩展一个新的属性和对象方法 但是方法的实现还是要写在原有的声明中,不然是不会被访问到的 类扩展可以扩展在新的头文件中,然后在主函数中导入. 利用类扩展可以变相的实现属性的私有化 ...

  3. OC高级编程——深入block,如何捕获变量,如何存储在堆上

    OC高级编程——深入block,如何捕获变量,如何存储在堆上   首先先看几道block相关的题目 这是一篇比较长的  博文 ,前部分是block的测试题目,中间是block的语法.特性,block讲 ...

  4. OC 类别与扩展(匿名类别)

    OC 类别与扩展(匿名类别) 类别(Categroy): 又称为扩展类,在类的原基础上扩展方法,且不可添加变量,如果扩展的方法与原始类中的方法相同,则会隐藏原始方法,且不可在扩展方法中通过super调 ...

  5. 分类(类别/Category)与 类扩展(Extension)

    一.分类(类别/Category) 1.适用范围      当你已经封装好了一个类(也可能是系统类.第三方库),不想在改动这个类了,可是随着程序功能的增加需要在类中增加一个方法,这时我们不必修改主类, ...

  6. iOS中的分类(category)和类扩展(extension)

    今天在研究swift的时候看到了分类和扩展.这是两个十分重要有用的功能,但是之前用的不多,没有深入了解过,在今天就从头理一遍. 一.分类(Category): 概念: 分类(Category)是OC中 ...

  7. iOS分类(category),类扩展(extension)—史上最全攻略

    背景: 在大型项目,企业级开发中多人同时维护同一个类,此时程序员A因为某项需求只想给当前类currentClass添加一个方法newMethod,那该怎么办呢? 最简单粗暴的方式是把newMethod ...

  8. extension(类扩展)和 category(类别)

    extension(类扩展) 简单来说,extension在.m文件中添加,所以其权限为private,所以只能拿到源码的类添加extension.另外extension是编译时决议,和interfa ...

  9. 类目(category) - 类扩展(extension) 区别

    说明: 方法,属性或变量:   类别只能添加方法,不能添加属性(理论上,但可以通过runtime的关联添加). 扩展可以添加方法和实例变量或属性,实例变量默认@private类型.扩展是类别的一个特例 ...

随机推荐

  1. 如何将声学的spectrogram(声谱图)重新反变换成时域语音信号

    最近在研究一些信号分析的事情,感兴趣如何将频谱信号反变换成时域信号.fft 与ifft可以顺畅的转变,但是这个是一帧信号,当时间较长的信号再一起是,通过反变换变成一帧一帧的时域信号,如何把他们拼接起来 ...

  2. 输入指令npx webpack-dev-server报错:Error: Cannot find module ‘webpack-cli/bin/config-yargs‘的解决方法

    输入指令npx webpack-dev-server报错:Error: Cannot find module 'webpack-cli/bin/config-yargs'的解决方法 输入指令:npx ...

  3. java eclipse调试提示Source not found 或 一闪而过 解决方法

    Web工程Eclipse  debug方式启动,在断点的位置被成功拦截,但是没有跳转到工程的代码处,提示如下: 当然这个时候如果我继续按F5的话呢,程序又会接着正常运行了.到这里那就是说程序本身是没有 ...

  4. Python基础(列表生成式)

    import os; list1 = list(range(1,11)) list2 = [x*x for x in list1 if x % 2 == 0]#列表生成式时,把要生成的元素x * x放 ...

  5. Django笔记&教程 7-3 拓展CBVs(Class-based views)

    Django 自学笔记兼学习教程第7章第3节--拓展CBVs(Class-based views) 点击查看教程总目录 一般而言,直接使用原生的Class-based views,能展现的样式和内容是 ...

  6. 记一次 .NET 某电商无货源后端服务 死锁分析

    一:背景 1. 讲故事 这个月初,星球里的一位朋友找到我,说他的程序出现了死锁,怀疑是自己的某些写法导致mongodb出现了如此尴尬的情况,截图如下: 说实话,看过这么多dump,还是第一次遇到真实的 ...

  7. Docker部署 Mysql .Net6等容器

    Centos8安装Docker 1.更新一下yum [root@VM-24-9-centos ~]# yum -y update 2.安装containerd.io # centos8默认使用podm ...

  8. [bzoj1863]皇帝的烦恼

    二分枚举答案,假设是ans,考虑判定答案从前往后计算,算出每一个将军与第一个将军最少和最多有多少个相同的奖牌,贪心转移即可 1 #include<bits/stdc++.h> 2 usin ...

  9. 如何利用 JuiceFS 的性能工具做文件系统分析和调优

    JuiceFS 是一款面向云原生环境设计的高性能 POSIX 文件系统,在 AGPL v3.0 开源协议下发布.作为一个云上的分布式文件系统,任何存入 JuiceFS 的数据都会按照一定规则拆分成数据 ...

  10. Windwos安装Node.js和npm的详细步骤

    How to Install Node.js and NPM on Windows Node.js和npm 安装 Node.js 的时候会自动安装 npm ,并且 npm 就是 Node.js 的包管 ...