ObjectiveC 内存管理

内存的五大区域

  • 栈:局部变量 (当局部变量的作用域被执行完毕之后,这个局部变量就会被系统立即回收);
  • 堆:OC对象和使用C函数申请的空间;
  • BSS段:未初始化的全局变量、静态变量,一旦初始化就回收并转存到数据段之中;
  • 数据段:已经初始化的全局变量、静态变量,直到程序结束的时候才会被回收;
  • 代码段:二进制代码,程序结束的时候,系统会自动回收存储在代码段中的数据.

        栈、BSS段、数据段、代码段存储在它们中的数据的回收,是由系统自动完成的,不需要我们干预。存储在堆中的OC对象,系统不会自动回收,直到程序结束的时候才会被回收。因此,内存管理的范围是存储在堆中的0C对象。

引用计数器

定义

  1. 每个对象都有个属性,叫做retainCount,即引用计数器,类型是unsigned long 占据8个字节.引用计数器的作用:用来记录当前这个对象有多少个人在使用它。默认情况下,创建1个对象出来这个对象的引用计数器的默认值是1。
  2. 当多1个人使用这个对象的时候。应该先让这个对象的引用计数器的值+1 ,代表这个对象多1个人使用。
  3. 这个对象少1个人使用的时候。应该先让这个对象的引用计数器的值-1 ,代表这个对象少1个人使用。
  4. 当这个对象的引用计数器变为0的时候,代表这个对象无人使用。这个时候系统就会自动回收这个对象。

如何操作引用计数器

  1. 为对象发送1条retain消息,对象的引用计数器就会加1,当多1个人使用对象的时候才发;
  2. 为对象发送1条release消息,对象的引用计数器就会减1,当少1个人使用对象的时候才发;
  3. 为对象发送1条retainCount消息,就可以取到对象的引用计数器的值;
  4. 在对象被回收的时候,会自动调用对象的dealloc方法.

        重写dealloc方法的规范:必须要调用父类的dealloc方法,并且要放在最后一句代码。

[super dealloc];

内存管理的分类

  • MRC: Manual Reference Counting 手动引用计数
  • -------------------i0S5-------------------------
  • ARC: Automatic Reference Counting 自动引用计数

内存管理的原则

  • 有对象的创建,就要匹配1个release;

  • retain的次数和release的次数要匹配;
  • 谁用谁retain,谁不用谁release 。

回收对象的本质

        申请1个变量,实际上就是向系统申请指定字节数的空间,这些空间系统就不会再分配给别人了, 当变量被回收的时候,代表变量占用的字节空间从此以后系统可以分配给别人使用了,但是字节空间中存储的数据还在。

成员变量的setter方法

- (void)setCar:(Car *)car
{
    if(_car!=car)// 旧对象不是同一个对象
    {
        [_car release];// release旧对象
         _car = [car release];// retain新对象
     }
}

@property

@property的作用

  1. 简化代码@property 自动生成实例变量的声明,以及对应的 Getter 和 Setter 方法,避免繁琐的手动编写。

  2. 定义属性的访问控制:通过各种修饰符,可以控制属性的访问方式(如读写权限、内存管理语义等)

@property的参数

  • nonatomic atomic:默认是atomic,是线程安全的,但效率低,建议atomic;
  • retain assign:默认值是assign,生成的setter方法的视线就是直接赋值;retain的setter方法实现就是标准的MRC内存管理代码;当属性的类型是OC对象时,就使用retain,非OC对象使用assign;但是reatin并不会在dealloc中生成release的代码,我们需要手动写;
  • readwrite readonly:前者同时生成getter、setter,后者只会生成setter;

@class

        当两个类相互包含的时候,如Person.h中包含Book.h 而Book.h中又包含Person.h。这个时候,就会循环引用,就会造成无限递归的问题,而导致无法编译通过。

        解决方法:其中一边不要使用#import引入对方的头文件,而是使用@class+类名来标注这是1个类。这样子就可以在不引入对方头文件的情况下,告诉编译器这是1个类。

@cLass与#import的区别

  1. #import是将指定的文件的内容拷贝到写指令的地方。
  2. @class 并不会拷贝任何内容。只是告诉编译器,这是1个类,这样编译器在编译的时候才可以知道这是1个类。

对象相互引用

        A对象的属性是B对象,B对象的属性是A对象。这个时候如果两边都使用retain 那么就会发生内存泄露。解决方案:1端使用retain 另外1端使用assign,使用assign的那1端在dealloc中不再需要release了。

自动释放池

原理

        存入到自动释放池中的对象,在自动释放池被销毁的时候。会自动调用存储在该自动释放池中的所有对象的release方法。

        可以解决的问题:将创建的对象,存入到自动释放池之中,就不再需要手动的relase这个对象了。

创建自动释放池

@autoreleasepool
{

}

将对象存储到自动释放池

  1. 在自动释放池之中调用对象的autorelease方法,就会将这个对象存入到当前自动释放池之中;
  2. 这个autorealse方法返回的是对象本身,所以我们可以这么写:
@autoreleasepool
{
    Preson *p=[[[Person alloc] init]autorelease];
}

        此时当自动释放池执行完毕之后就会立即为自动释放池里的对象发送一条release方法。(只是省略创建对象匹配的release) 。

注意事项

  • 内存占用大的对象不要随意使用autorelease,害怕大对象释放较晚导致内存持续不降;
  • 内存小的对象安心使用。

类方法规范

  1. 一般情况下,要求提供与自定义构造方法相同功能的类方法.这样可以快速的创建1个对象;
  2. 使用类方法创建的对象,要求这个对象在方法中就已经被autorelease过了。这样,我们只要在自动释放池中,调用类方法来创建对象,那么创建的对象就会被自动的加入到自动释放中。
  3. 如果没有参数就直接是类名 如果有参数就是 类名withXX:
+ (instancetype) person
{
return [[[self alloc] init autoreleasel;
}

        这样,我们直接调用类方法就可以得到1个已经被autorelease过的对象。       

@autoreleasepool
{
Person *p1 = [Person person];
//这个p1对象已经被autorelase过了.不需要再调用autorelase
1/这个p1对象就被存储到当前自动释放池之中,
}//当自动释放池结束.就会为存储在其中的p1对象发送release消息

 ARC

        当ARC开启时,编译器会自动的在合适的地方插入retain、release、autorelase代码。编译器自动为对象做引用计数,而作为开发者,完全不需要担心编译器会做错(除非开发者自己错用了ARC)。另外,在dealloc中,不允许[super dealloc]。

ARC机制下,对象何时被释放?

  • 本质:对象的引用计数器为0的时候,自动释放;
  • 表象:只要没有强指针指向这个对象,这个对象就会立即回收;

强指针与弱指针

强指针:默认情况下,我们声明1个指针就是1个强指针。也可以使用_strong来显示的声明这是1个强指针.

  • Person *p1;这是1个强指针,指针默认情况下都是1个强指针。
  • _strong Person *p2;这也是1个强指针.使用_strong来显示的声明强指针。

弱指针:使用_weak标识的指针就叫做弱指针.

无论是强指针还是弱指针,都是指针,都可以用来存储地址,这1点没有任何区别。都可以通过这个指针访问对象的成员。唯一的区别就是在ARC模式下,他们用来作为回收对象的基准。

但是不能创建一个用弱指针存储某个对象,因为该对象刚刚创建出来就会被释放。

单个对象的内存管理

在ARC的机制下:当1个对象没有任何的强指针指向它的时候,这个对象就会被立即回收。

1)当指向对象的所有的强指针被回收的时候,对象就会被立即回收。

int main(int argc, const char * argv[])

{

@autoreleasepool

{Person *p1 = [Person new];//p1是1个强指针.

Person *p2 = P1;//p2也是个强指针.P1和p2都指向Person对象.

//每1个指针变量默认情况下都是1个强指针变量;

}//当执行到这里的时候。P1指针被回收,p2指针也被回收。那么Person对象就没有任何//强指针指向它了,对象就在这被回收.

return 0;

}

2)将所有指向对象的强指针赋值为nil的时候,对象就会被立即回收.

int main(int argc, const char * argvll)

@autoreleasepool

{Person *p1 = [Person new] ;//p1是1个强指针,

//因为我们说过,每1个指针变量默认情况下都是1个强指针变量。

P1 = nil;//当执行到这句话的时候.p1赋值为nil.

//p1指针不再执行Person对象.

//Person对象没有被任何的指针所指向,所以Person对象在这里被释放.

}

return 0;

}

使用建议

1)在ARC机制下.如果属性的类型是OC对象类型的.绝大多数场景下使用strong;

2)在ARC机制下.如果属性的类型不是OC对象类型的,使用assign;

3)strong和weak都是应用在属性的类型是OC对象的时候.属性的类型不是OC对象的时候就使用assign。

强指针相互引用--将一个改为weak

程序使用ARC,但有的类是MRC.使用-fno-objc-arc编译这些类

上一篇:Docker 安装ros 使用rviz 等等图形化程序