首页 > 系统相关 >Objective-C 内存管理

Objective-C 内存管理

时间:2022-12-01 16:37:29浏览次数:48  
标签:释放 name 管理 str1 retainCount int1 内存 Objective copy


 

 

 

 

大概是因为 Objective-C 是 C的超集,所以Objective-C 也使用alloc来申请内存,不同的是C调用free来直接释放内存,而Objective-C 不直接调用dealloc来释放内存。整个Objective-C 都使用对象的引用,而每个对象都有一个引用计数器。当计数器为0时,系统调用dealloc来释放内存。Objective-C 业提供了autorelease 属性,从而可以让系统自动释放对象所占有的内存。程序中可以设置一个自动释放池,系统使用这个池来跟踪对象。使用如下语句可以创建一个自动释放池:



[java]  ​​view plain​​ ​​copy​​ ​​print​​ ​​?​​



  1. NSAutoreleasePool* pool = [[NSAutoreleasePool alloc]init];  



在我们的程序入口处,xcode也帮我们建了一个自动释放池,而且把整个程序循环加了进去。



[java]  ​​view plain​​ ​​copy​​ ​​print​​ ​​?​​



  1. int main(int argc, char *argv[])  
  2. {  
  3. @autoreleasepool {//这个说实话我也没搞明白,不过我知道它创建了一个自动释放池,然后把我们的程序放到了这个池子里  
  4. return UIApplicationMain(argc, argv, nil, NSStringFromClass([AppDelegate class]));  
  5.     }  
  6. }//先别管这个的具体意思,你只要知道xcode帮我们建了一个自动释放池,然后把整个程序放到了自动释放池里面。  



在这个 自动释放池建立之后,基础框架就自动将数组、字符串等对放到这个池中。当下面语句被执行时,池中的对象就被自动释放:



[java]  ​​view plain​​ ​​copy​​ ​​print​​ ​​?​​



  1. [ pool drain];  



总之,一个对象被标识为自动释放对象,那么就会被加到自动释放池中。除了使用drain来释放池中的对象外,在自动释放池本身被释放时,池中的所有对象也会被释放。自动释放池语句如下:



[java]  ​​view plain​​ ​​copy​​ ​​print​​ ​​?​​



  1. [ pool release ];  


之前是没有ARC(Automatic Reference Counting)功能的,现在有了,我想ARC 主要是为从Java转过来的程序员而设计的吧,因为这个差不多相当于Java里面的自动垃圾收集器吧。从C/C++转过来的程序员大多数还是愿意手动去释放的,因为他们不大相信自动释放的效率,还有就是他们喜欢掌控一切的感觉,比如我。如果你要开启,在创建工程的时候请勾选上这个选项:

如果你使用手动管理内存,请往下看:

一、申请内存(alloc)

当使用 alloc 创建了一个对象时,需要在用完这个对象后释放(release)它,你不需要自己去释放一个被设置为自动释放(autorelease)的对象,如果你真的这么干了,对不起,程序会崩溃。



[java]  ​​view plain​​ ​​copy​​ ​​print​​ ​​?​​



  1. //str1 是autorelease的,会自动释放  
  2.  NSString* str1 = [NSString string];  
  3. //str2需要手动释放  
  4.  NSString* str2 = [[NSString alloc]init];  
  5. //str2 使用完后  
  6.  [str2 release];  



为便于理解,你可以理解成autorelease型的对象会在该释放他的时候自动释放。

 

二、释放内存(dealloc)

当一个对象从内存上删除之前,系统就会调用dealloc方法,这是释放对象成员变量的最好时机。比如:



[java]  ​​view plain​​ ​​copy​​ ​​print​​ ​​?​​



  1. - (void)dealloc{  
  2.     [_window release];  
  3. super dealloc];  
  4. }  



上例中,先调用release释放了成员变量_window所占用的内存。相对而言,用标准的release比autorelease更快一点。最后一行[ super dealloc ]; 非常重要,必须调用这个方法让父类清楚它自己,否则会造成内存泄漏。

 

在ARC模式下,dealloc 不会被调用到,取而代之的是,需要实现finalize方法 。

三、引用计数器(retainCout)

整个Objective-C都使用对象引用,而每个对象有一个引用计数器。当时用alooc(或copy)方法创建一个对象时。其计数器的值为 1 。当计数器为 0 时,系统自动调用 dealloc方法来释放内存中的对象。比如:



[java]  ​​view plain​​ ​​copy​​ ​​print​​ ​​?​​



  1. STU *stu = [[STU alloc]init];//计数器为1  
  2. //计数器为2  
  3. //计数器为1  
  4. //计数器为0,系统自动调用dealloc方法  
  5. //释放之后,如果调用该对象的任何一个方法,程序就会异常终止  
  6. //崩溃  



为了防止上述的异常崩溃出现,可以在最后一个release之后加一句:



[java]  ​​view plain​​ ​​copy​​ ​​print​​ ​​?​​



  1. stu =nil;  



上述语句将该对象置为nil。在这之后,任何调用该member的方法都返回nil,而不是异常终止。再来看下面的例子:



[java]  ​​view plain​​ ​​copy​​ ​​print​​ ​​?​​



  1. int main(int argc, char *argv[]){  
  2.     NSAutoreleasePool* pool = [[NSAutoreleasePool alloc]init];  
  3.       
  4. 222];  
  5.     NSNumber *int2;  
  6.     NSMutableArray* arr1 = [NSMutableArray array];  
  7. "初始化以后:int1(retainCount):%lu int2(retainCount):%lu",(unsigned long)[int1 retainCount],(unsigned long)[int2 retainCount]);  
  8.       
  9.     [arr1 addObject:int1];  
  10. "将int1添加到数组以后:int1(retainCount):%lu int2(retainCount):%lu",(unsigned long)[int1 retainCount],(unsigned long)[int2 retainCount]);  
  11.       
  12.     int2 = int1;  
  13. "进行赋值操作以后:int1(retainCount):%lu int2(retainCount):%lu",(unsigned long)[int1 retainCount],(unsigned long)[int2 retainCount]);  
  14.       
  15.     [int1 retain];  
  16. "int1 retain以后:int1(retainCount):%lu int2(retainCount):%lu",(unsigned long)[int1 retainCount],(unsigned long)[int2 retainCount]);  
  17.       
  18.     [int1 release];  
  19. "int1 release以后:int1(retainCount):%lu int2(retainCount):%lu",(unsigned long)[int1 retainCount],(unsigned long)[int2 retainCount]);  
  20.       
  21. 0];  
  22. "从数组中删除int1以后:int1(retainCount):%lu int2(retainCount):%lu",(unsigned long)[int1 retainCount],(unsigned long)[int2 retainCount]);  
  23.       
  24.     [pool drain];  
  25. return 0;  
  26. }  



程序结果:

初始化以后:int1(retainCount):1 int2(retainCount):0

将int1添加到数组以后:int1(retainCount):2 int2(retainCount):0

进行赋值操作以后:int1(retainCount):2 int2(retainCount):2

int1 retain以后:int1(retainCount):3 int2(retainCount):3

int1 release以后:int1(retainCount):2 int2(retainCount):2

从数组中删除int1以后:int1(retainCount):1 int2(retainCount):1

基础框架所提供的一些方法也会增加对象的引用次数,比如,把对象添加到一个数组中的时候。

在上面的例子中,首先将NSNumber对象int1的值设置为整数222,我们定义了int2但是没有初始化。这时候打印引用计数器,会发现int1的计数器为1,int2的计数器为0。也就是说,只有真正创建了一个对象,并且正常初始化了之后,才会增加引用计数器。

随后使用addObject:方法将int1添加到数组arr1中,打印计数器,不难发现int1的计数器变为2,int2的计数器还是0。也就是说当对象添加到任何类型的集合中时,都会使改对象的有效引用增加。

接下来,将int1的值赋给int2,这时候的结果值得我们注意。int1与int2的引用计数器都是2了,这说明进行赋值操作并未使int1的计数器增加,但是int2的计数器变为了2。这是因为当把int1赋给int2的时候,并不是复制实际的对象,而是将该对象的内存地传递给了int2。也就是说这两个指针指向的是同一个对象。

然后我们向int1发送retain消息来增加引用计数,int2的引用计数器当然也随之增加咯。

然后向int1发送release消息,int1与int2的引用计数当然同时减 1 咯。

最后,从arr1中移除 int1 ,这样int1与int2的引用计数又同时减 1 。但是这时候对象并未真正释放,因为引用计数器 1,并不是0。如果要释放你还得 release一次。

Objective-C的内存管理系统基于引用计数。我们需要跟踪引用,以及在运行期内是否真的释放了内存。简单来说就是每次调用了alloc或者retain之后都需要调用release。

在程序中,你只在两种情况下跟踪一个对象:(1)本地变量(在一个方法里面临时使用,比如创建一个字符串)(2)成为一个类的成员变量只要掌握了跟踪者两种情况,并作出相应内存释放,你就基本掌握了Objective-C的内存管理。

三、字符串处理

如果使用alloc或者copy创建一个对象(比如字符串),那么在方法结束的时候需要release或者autorelease这个对象。假如我们是通过别的方式创建的,就可以不管。在下面的粒子中,只需要release那些使用alloc创建的对象:



[java]  ​​view plain​​ ​​copy​​ ​​print​​ ​​?​​



  1. NSNumber* num1 = [[NSNumber alloc]initWithFloat:1.80];  
  2. 145.0];  
  3. //我们只需要释放num1,不用释放num2  
  4.    [num1 release];  



再看下面的例子:



[java]  ​​view plain​​ ​​copy​​ ​​print​​ ​​?​​



  1. int main(int argc, char *argv[]){  
  2.     NSAutoreleasePool* pool = [[NSAutoreleasePool alloc]init];  
  3.       
  4. "LiuWei";  
  5. "IUKEY"];  
  6. "MasterLiuWei"];  
  7.     NSMutableArray* arr1 = [NSMutableArray array];  
  8. //初始化以后 各个retainCount:str1:fffffffffffffff str2:fffffffffffffff str3:1  
  9.       
  10.     [arr1 addObject:str1];  
  11.     [arr1 addObject:str2];  
  12.     [arr1 addObject:str3];  
  13. //将字符串添加到数组以后,各个retainCount:str1:fffffffffffffff str2:fffffffffffffff str3:2  
  14.     
  15.     [str1 retain];  
  16.     [str2 retain];  
  17.     [str3 retain];  
  18. //执行retain以后,各个retainCount:str1:fffffffffffffff str2:fffffffffffffff str3:3  
  19.       
  20.     [str3 release];  
  21. //执行release以后,各个retainCount:str1:fffffffffffffff str2:fffffffffffffff str3:2  
  22.       
  23.     [pool drain];  
  24. return 0;  
  25. }  



你可能会对结果产生疑问,为什么是fffffffffffffff 呢?在程序中,NSString 的对象 str1 赋值为静态的字符串@"welcome"。字符串常量的内存分配方式和其他的对象不同,这种方式没有引用计数机制,因此永远不能释放这些对象。当向 str1 发送消息 retainCount 的时候,它返回fffffffffffffff ,即最大的无符号整数。注意:这同样适用于用字符串常量初始化那些不可变的字符串对象。

 

值得注意的是下面的语句:



[java]  ​​view plain​​ ​​copy​​ ​​print​​ ​​?​​



  1. NSMutableString* str3 = [NSMutableString stringWithString:@"MasterLiuWei"];  



这条语句使用字符串常量来初始化一个可变字符串对象,通过 stringWithString: 方法进行。由于可变字符串的至可能在程序运行过程中发生变化。但是字符串常量是无法改变的,所以系统拷贝这个字符串常量到 str3 上来完成初始化。因此可变字符串对象拥有了引用计数。

当我们使用stringWithString:  方法创建不可变字符串对象时,这个对象其实被添加到了自动释放池。通过array 方法所创建的对象arr1 对象也被添加到了自动释放池。

程序中的代码块将这些字符串添加到数组中,冰箱这给谢对象发送retain 消息。从程序的结果可以看出,这些操作改变了他们的引用技术。

在释放自动释放池之前,str3已经被释放了一次。所以 str3 的引用计数变为2。随后,自动释放池的释放使得这些对想的引用技术减为 0 ,这使得他们被释放。当释放自动释放池的时候,池中的每一个对象都会收到一条 release 消息(收到的条数等于当时发送autorelease消息的条数)。由于在使用 stringWithString: 方法所创建的字符串对象 str3 时,系统将他添加到自动释放池,也就是说调用了 autorelease 方法,所以,当自动释放池被释放(drain)时,str3会收到一条  release消息,这将导致他的引用计数减为1。当自动释放池中的数组被释放的时候,数组中的每个元素也将被释放,所以数组中的每个元素都将收到一条release'消息,这将使str3的引用计数变为0 。系统随之将调用str3的dealloc方法将str3释放。

注意:如果我们过度释放,程序将异常终止。

四、类成员变量

在大多数情况下,一个成员变量的setter方法应该仅仅 aytorelease/release 旧的对象,然后 retain/copy 新的对象。我们只需要在 dealloc的时候调用release就好了。所以真正需要管理的就是方法内部的局部变量的引用。比如,设置字符串(使用copy):



[java]  ​​view plain​​ ​​copy​​ ​​print​​ ​​?​​



  1. - (void)setName:(NSString*)newName{  
  2. if (name != newName) {  
  3.         [name release];  
  4. //name 的计数器为1  
  5.     }  
  6. }  



下面是使用retain管理成员对象的另一个例子:



[java]  ​​view plain​​ ​​copy​​ ​​print​​ ​​?​​



  1. - (void) setScore:(NSNumber*)inputScore{  
  2.     [score autorelease];  
  3.     score = [inputScore retain];  
  4. }  



[java]  ​​view plain​​ ​​copy​​ ​​print​​ ​​?​​



  1. - (void)dealloc{  
  2.     [score release];  
  3. super dealloc];  
  4. }  



下面是使用本地对象去设置一个成员变量的例子:



[java]  ​​view plain​​ ​​copy​​ ​​print​​ ​​?​​



  1. NSNumber* value1 = [[NSNumber alloc]initWithFloat:180.0];  
  2.   [self setScore:value1];  
  3.     
  4. 72.5];  
  5.   [self setScore:value2];  
  6.   [value1 release];  



下面我来分析一下几种成员变量的处理方法。

 

首先定义一个类,它只包含NSString类型的name属性,并手动书写了这个属性的存取方法。



[java]  ​​view plain​​ ​​copy​​ ​​print​​ ​​?​​



  1. #import <Foundation/Foundation.h>  
  2.   
  3. @interface Student : NSObject{  
  4.     NSString* name;  
  5. }  
  6. -(void)setName:(NSString*)s;  
  7. -(NSString*)name;  
  8. @end  



在下面的方法实现文件中,我们直接把setName: 方法中的形参 s 的值赋给 name 属性,并没有做其他的操作。



[java]  ​​view plain​​ ​​copy​​ ​​print​​ ​​?​​



  1. #import "Student.h"  
  2.   
  3. @implementation Student  
  4. -(void)setName:(NSString *)s{  
  5.     name =s;  
  6. }  
  7.   
  8. -(NSString*)name{  
  9. return name;  
  10. }  
  11. @end  



下面是测试代码:



[java]  ​​view plain​​ ​​copy​​ ​​print​​ ​​?​​



  1. NSMutableString* str1 = [NSMutableString stringWithString:@"LiuWei"];  
  2.     Student* stu = [[Student alloc]init];  
  3.       
  4. "str1的引用计数器:%x",[str1 retainCount]);  
  5.       
  6.     [stu setName:str1];  
  7. "str1的引用计数器:%x",[str1 retainCount]);  
  8.     [stu release];  



测试结果:

 

str1 的引用计数器:1

str1 的引用计数器:1

程序首先为Student类创建了对象stu ,然后调用这个对象的setName 方法,将name的值设置为  str1的值(str1 和name 这两个指针指向的是同一块内存空间)。这种做法有一个问题,在设置完毕以后,如果不需要str1对象并将其释放,那么str1的 引用计数器变为0。毫无疑问,name的引用计数器也将变为0 ,存储在那么中的值则变为无效的。

由于str1是用 NSMutableString stringWithString: 方法创建的,该对象也被自动添加到自动释放池之中(虽然没有显式将对象添加到 自动释放池中)。因此,在自动释放池释放的时候,str1 也会被释放,这时,任何str1所指向的对象的访问都将是无效的。

下面我们来看第二种写法。接口文件未改动,所以参看上面的接口代码。在实现文件中我们执行了 [ name retain] 方法,这样会使 name 的引用计数器增加 1 ,从而在str1 释放的时候,name 还能够正常使用。



[java]  ​​view plain​​ ​​copy​​ ​​print​​ ​​?​​



  1. #import "Student.h"  
  2.   
  3. @implementation Student  
  4. -(void)setName:(NSString *)s{  
  5.     name =s;  
  6.     [name retain];  
  7. }  
  8.   
  9. -(NSString*)name{  
  10. return name;  
  11. }  
  12. @end  



再来测试:



[java]  ​​view plain​​ ​​copy​​ ​​print​​ ​​?​​



  1. NSMutableString* str1 = [NSMutableString stringWithString:@"LiuWei"];  
  2.     Student* stu = [[Student alloc]init];  
  3.       
  4. "str1的引用计数器:%x",[str1 retainCount]);  
  5.       
  6.     [stu setName:str1];  
  7. "str1的引用计数器:%x",[str1 retainCount]);  
  8.     [stu release];  
  9. "str1的引用计数器:%x",[str1 retainCount]);  



 

测试结果:

 

str1 的引用计数器:1

str1 的引用计数器:2

str1 的引用计数器:1

根据结果,在调用 setName: 之后,str1的引用计数器变为2 ,上一个程序中所产生的问题得以解决。接着就在程序中执行了 [str1 release] 方法,使得 str1 的引用计数器减少为 1,这时候对name的引用依然有效。

由于程序使用了alloc方法创建 Student 类的对象,就需要负责将这个对象释放掉。我们可以手动释放,也可以调用 它的autorelease 方法,将其添加到自动释放池中。在这之前我们都能正常使用这个对象。

但是这样的写法还是存在一些问题,setName: 方法保持了作为参数传入的字符串对象,但是这个对象什么时候释放呢?当多次 更改name 值的时候,原来的那些值会怎样?应该释放他们所占用的内存么?

下面来看最终的处理方法:



[java]  ​​view plain​​ ​​copy​​ ​​print​​ ​​?​​



  1. //  Student.h  
  2.   
  3. #import <Foundation/Foundation.h>  
  4.   
  5. @interface Student : NSObject{  
  6.     NSString* name;  
  7. }  
  8. -(void)setName:(NSString*)s;  
  9. -(NSString*)name;  
  10. - (void)dealloc;  
  11. @end  



[java]  ​​view plain​​ ​​copy​​ ​​print​​ ​​?​​



  1. //  Student.m  
  2.   
  3. #import "Student.h"  
  4.   
  5. @implementation Student  
  6. -(void)setName:(NSString *)s{  
  7.     [name autorelease];  
  8.     name = [s retain];  
  9. }  
  10.   
  11. -(NSString*)name{  
  12. return name;  
  13. }  
  14.   
  15. - (void)dealloc{  
  16.     [name release];  
  17. super dealloc];  
  18. }  
  19. @end  



测试代码:



[java]  ​​view plain​​ ​​copy​​ ​​print​​ ​​?​​



  1. NSMutableString* str1 = [NSMutableString stringWithString:@"LiuWei"];  
  2.    Student* stu = [[Student alloc]init];  
  3.      
  4. "str1的引用计数器:%x",[str1 retainCount]);  
  5.      
  6.    [stu setName:str1];  
  7. "str1的引用计数器:%x",[str1 retainCount]);  



测试结果:

 

 

str1 的引用计数器:1

str1 的引用计数器:2

通过上面的方法,不管name中当前存储的值是什么,我们在 setName: 方法中首先将其手动添加到自动释放池中,这也可以让其以后自动释放 name 。当程序中多次调用 setName: 方法时,这项操作将更加重要:每次存储新的值的时候,变量就得值将标记为自动释放,保持新的值到name中。

当系统释放一个对象时,系统自动调用dealloc 方法。对于保持(retain)的对象,或者使用alloc分配的对象,或者在方法中复制的对象,可以在dealloc方法中去释放它们。首先释放name变量,因为他是保持对象:然后调用父类的dealloc方法来释放Student 的对象。如果需要外部的字符串完全独立于设置方法的参数,可以在设置方法中声称字符串的全新副本,即使用copy选项。

五、自动释放( autorelease )池

 在代码中创建了一个字符串,如果需要返回这个字符串的话,那么需要使用 autorelease,而不是release。比如:



[java]  ​​view plain​​ ​​copy​​ ​​print​​ ​​?​​



  1. - (NSString*)getStr{  
  2.     NSString* str;  
  3. "Result return "];  
  4. //[ str release];  //不能释放,否则调用者无法获得返回值  
  5. //正确  
  6. return str;  
  7. }  



通过使用autorelease,该对象就被放入自动释放池,系统自动跟踪每个对象的使用情况,并在

 

释放自动释放池时, 释放池中的所有对象

注意:autorelease 不是垃圾搜集(Garbage Collection)功能。在iPhone 操作系统上的Objective-C没有垃圾搜集功能。另外,你可以创建多个自动释放池。



[java]  ​​view plain​​ ​​copy​​ ​​print​​ ​​?​​



  1. int main(int argc, char *argv[])  
  2. {  
  3.     NSAutoreleasePool* pool = [[NSAutoreleasePool alloc]init];  
  4. //1  
  5. "%x",[stu retainCount]);  
  6.       
  7.     [pool drain];  
  8. "%x",[stu retainCount]);//1  
  9.       
  10.     pool =[[NSAutoreleasePool alloc]init];  
  11.     [stu autorelease];  
  12. "%x",[stu retainCount]);//1  
  13.       
  14.     [stu retain];  
  15. "%x",[stu retainCount]);//2  
  16.       
  17.     [pool drain];  
  18. "%x",[stu retainCount]);//1  
  19.     [stu release];  
  20.       
  21. return 0;  
  22. }  



 

六、内存泄漏

为了防止内存泄漏,并确保最有效地使用内存,应用程序应该只在需要时才装载数据。,比如,用户点击了某个按钮才显示相关数据。我们在前面讲了autorelease ,但是如果你的代码总是 autorelease,就没有内存泄漏了吗?问题在于autorelease 池释放的时机。每当执行应用程序时,系统自动创建 autorelease 池。系统并不是立即释放autorelease池中的对象,而是在一个run loop之后才释放,一般是微妙级别。在 autorelease 池中的对象本可以立即释放,但是系统很有可能过一段时间才释放他们,所以 使用release 可以更有效地释放内存。所以,我们应该尽量自己管理内存,不要太依赖 autorelease 。另外,一些系统对象使用 autorelease,如NSString。你可以 ziji管理内存的类,如NSMutableString,你可以创建自己的池。

下面是几个内存管理的基本原则:

1>如果使用 alloc(或者copy)方法创建一个对象,或者使用retain 保留一个对象,那么 ,都要自己释放对象。

2>在大多数情况下,申请内存的语句数量和释放内存的语句数量应该相等

3>尽量少使用内存,用完立即释放

七、copy、nonatomic

对于字符串类型的属性变量,我们经常使用下面类似的语句:



[java]  ​​view plain​​ ​​copy​​ ​​print​​ ​​?​​



  1. @property(nonatomic,copy)NSString* name;  



这个语句就等价于:



[java]  ​​view plain​​ ​​copy​​ ​​print​​ ​​?​​



  1. -(void)setName:(NSString *)s{  
  2. if (s != name) {  
  3.         [name release];  
  4.         name = [s copy];  
  5.     }  
  6. }  



那么为什么要使用copy呢?如果直接使用下面的语句,结果将如何呢?



[java]  ​​view plain​​ ​​copy​​ ​​print​​ ​​?​​



  1. -(void)setName:(NSString *)s{  
  2.     name = s;  
  3. }  



结果是name 和 s 都指向同一个对象。当在调用 setName: 方法之后,如果 s 的值被修改,这显然不是我们想要的结果。所以,使用copy 来拷贝 s 的值到name 上,其完成的功能就是调用一个alloc 方法来创建一个新的字符串对象 (initWithStringLs).

 

在多线程中,两个或多个线程可能在同一时间执行同一代码。为了防止这种现象发生,开发人员可以使用互斥锁。nonatomic 的意思是不需要使用互斥锁, atomic 是使用互斥锁。缺省 是atomic 。 如果你的程序并没有多线程,可以设置为 nonatomic 以节省资源。

标签:释放,name,管理,str1,retainCount,int1,内存,Objective,copy
From: https://blog.51cto.com/u_3457306/5902435

相关文章

  • 《安富莱嵌入式周报》第293期:SEGGER开源其C/C++库源码emRun,丰富EMC电磁兼容资,OTA开源
    往期周报汇总地址:http://www.armbbs.cn/forum.php?mod=forumdisplay&fid=12&filter=typeid&typeid=104 视频版:https://www.bilibili.com/video/BV1ND4y1v7ik/ 1、......
  • 软件许可证管理应该怎么做?
    随着企业的不断发展企业所拥有的软件数量也在逐渐增多,并且它们对企业的发展所起的作用也越来越大。可以说,今天的软件已经无可争议的成为了国内众多企业尤其是大企业的主要资......
  • 开源数据库管理工具DBeaver使用技巧
    考虑到navicat/plsqlDeveloper为商用软件,存在版权问题,且听闻很多网友公司因此收到律师函,继而很难不怀疑数据的安全性,故寻找替代的开源数据库管理工具.查找一番,使用......
  • 04 Ceph 集群管理(转载)
    目录Ceph集群管理Ceph资源对象monitor、mgr和osd、csiprovisioner以Deployments的形式部署CSI的CephFS驱动和RBD驱动以DaemonSets的方式部署对外提供服务均......
  • linux用户和用户组管理
    linux用户和用户组管理Linux用户和用户组linux是多用户多任务操作系统,linux支持多个用户在同一时间内登录,不同用户可以执行不同的任务,并且互不影响。......
  • vuex状态管理器
    vuex核心概念//vuex中一共有五个状态StateGetterMutationActionModuleimportVuefrom'vue'importVuexfrom'vuex'Vue.use(Vuex)exportdefaultn......
  • 快速开发协同办公OA系统 让企业管理提质增效
    OA系统是一个企业除了生产控制之外的一切信息处理与管理的集合。对企业高层领导来说,OA系统是决策支持系统。它运用科学的数学模型,结合企业内部/外部的信息,为企业领导的决策......
  • 【项目管理过程的五个阶段是什么?】
    项目管理过程的五个阶段一般包括:启动、规划、执行、监控、收尾。但很多人对项目管理的全流程只知道这五大过程组。这五大过程组之间的关系是怎样的?项目管理的全流程又是什么......
  • 干货 | 五大关键点,帮助企业快速构建可落地的IT服务管理体系
    随着数字化转型的发展,IT运维管理环境日益复杂,对管理的要求也随之增高如何提升运维效率,快速落地做好运维管理,搭建一套IT服务管理必不可少,以往我们也对IT服务管理框架进行过总......
  • Python高级-with与“上下文管理器”-笔记
    如果你有阅读源码的习惯,可能会看到一些优秀的代码经常出现带有“with”关键字的语句,它通常用在什么场景呢?今对于系统资源如文件、数据库连接、socket而言,应用程序打开这......