12.Object-C--浅谈OC的内存管理机制

时间:2021-11-19 22:40:05

  

  昨天学习了OC的内存管理机制,今天想总结一下,所以接下来我要在这里bibi一下:OC的内存管理。

  首先我要说的是,内存管理的作用范围。

  内存管理的作用范围

  任何继承了NSObject的对象,对其他基本数据类型无效,例如:int ,float,BOOL等。
  每个OC对象都有自己的引用计数器,用来表示对象被引用的次数。
  每个OC对象内部都有4个字节的存储空间用来存储引用计数器。

  在使用alloc、new或者copy创建新对象的时候,新对象的引用计数器默认值为1
  当一个对象的引用计数器的值为0时,对象占用的内存就会被系统回收。

  下面说说,引用计数器的用法
  1> retain:计数器+1,会返回对象本身(id)。
  2> release:计数器-1,没有返回值(void)。
  3> retainCount:获取对象当前的引用计数器的值。

  对象销毁
  当一个对象的引用计数器的值为0时,该对象将被销毁,其占用的内存被系统回收。当一个对象被回收时,调用dealloc方法。一般情况下会重写dealloc,来释放相关资源。重写dealloc方法时,有一定要在最后面调用[super dealloc]。对象被回收之后,其占用的内存不可再用。

  如下代码,简单说一下内存管理操作

 int main()
{
Person *p = [[Person alloc] init]; // 创建对象,此时引用计数器值为:1 [p retain]; // 对对象p做一次retain操作 引用计数器+1,此时为:2 [p release]; // 对对象p做一次release操作 引用计数器-1,此时为:1 [p release]; // 对对象p做一次release操作 引用计数器-1,此时为:0 // p.name = @"jack"; // 这时p为野指针,p指向的对象为僵尸对象
p = nil; // 将p指针的值清空 return ;
}

  OC中有一个名词叫僵尸对象,下面说说,什么是僵尸对象?

  僵尸对象:所占用内存已经被回收的对象,僵尸对象不能使用。
  野指针:指向僵尸对象的指针,给野指针发送消息会报错(EXC_BAD_ACCESS)。
  空指针:没有指向任何东西的指针(存储的内容是nil、NULL、0),给空指针发送消息不会报错。

  之前说的是单个对象,那么多个对象之前的内存管理是怎么样子的呢?
  多对象之间的内存管理
  使用某个对象,则对该对象做一次retain操作。结束使用时,对该对象做一次release操作。
  原则是:谁retain,谁release;谁alloc,谁release。

  示例代码如下:

 @class Dog; // 因为Dog类还没有声明,要先告诉编译器Dog是一个类
@interface Person : NSObject
@property(nonatomic,assign) NSString *name;
@property(nonatomic,assign) int age;
// Person对象包含一个Dog对象,当调用Dog对象时,要对dog对象进行一次retain操作
@property(nonatomic,retain) Dog *dog;
@end
@implementation Person
- (void)setDog:(Dog *)dog
{ // 判断dog是否为新dog
if (dog != _dog) {
// 是新dog则对老dog进行一次release操作
[_dog release];
// 对新dog进行一次retain操作
_dog = [dog retain];
}
}
// dealloc重写,用来release Person的Dog
- (void)dealloc
{
// 谁retain谁release,当_dog的值为nil、NULL、0时 对它发送release消息,没有任何意义
[_dog release];
// 重写dealloc要在最后面调用父类的dealloc方法[super dealloc]
[super dealloc];
}
@end

  @property参数

  set方法内存管理相关参数 
  retain:release旧值,retain新值(适用于OC对象类型)。
  assign:直接赋值(默认、适用于非OC对象类型)。
  copy:release旧值,copy新值(一般用于NSString *)。

  set、get方法的生成
  readwrite:同时生成set方法和get方法的声明与实现(默认)。
  readonly:只生成get方法的声明与实现。

  多线程管理
  nonatomic:性能高 (一般使用这个值)。
  atomic      :性能低 (默认值)。

  set方法和get方法的名称
  setter:决定set方法的名称,必须有冒号:
  getter:决定get方法的名称 (一般用在BOOL类型 isXX)。

  对象循环引用中的内存管理
  当两个对象互相引用时,在一个对象的@property中使用retain,另一个对象的@property中使用assign。

 @class Dog; // 因为Dog类还没有声明,要先告诉编译器Dog是一个类
@interface Person : NSObject
@property(assign,nonatomic) NSString *name;
@property(assign,nonatomic) int age;
// Person对象包含一个Dog对象,当调用Dog对象时,要对dog对象进行一次retain操作
@property(retain,nonatomic) Dog *dog;
@end
@implementation Person
- (void)setDog:(Dog *)dog
{ // 判断dog是否为新dog
if (dog != _dog) {
// 是新dog则对老dog进行一次release操作
[_dog release];
// 对新dog进行一次retain操作
_dog = [dog retain];
}
}
// dealloc重写,用来release Person的Dog
- (void)dealloc
{
// 谁retain谁release,当_dog的值为nil、NULL、0时 对它发送release消息,没有任何意义
[_dog release];
// 重写dealloc要在最后面调用父类的dealloc方法[super dealloc]
[super dealloc];
}
@end
@interface Dog : NSObject
// Person和Dog互相调用,Person中用retain,Dog中用assign
@property(assign,nonatomic) Person *person;
@property(assign,nonatomic) NSString *name;
@end
@implementation Dog
// Dog没有对person进行retain操作,不用对它进行release
@end
int main()
{
// 对象的循环调用
Person *p1 = [[Person alloc] init]; // 引用计数器值为:1
Dog *d1 = [[Dog alloc] init]; // 引用计数器值为:1
Dog *d2 = [[Dog alloc] init]; // 引用计数器值为:1 p1.dog = d1; // d1引用计数器值为:2
d1.person = p1; // p1引用计数器值为:2
// p1的dog换成d2,在p1的set方法里对d1进行一次release,对d2进行一次retain。
p1.dog = d2; // d1引用计数器值为:1 d2引用计数器值为:2
[d1 release]; // d1引用计数器值为:0
[d2 release]; // d2引用计数器值为:1
[p1 release]; // p1引用计数器值为:0,d2引用计数器值为:0
return ;
}

@class的使用

通常引用一个类有两种办法:

一种是通过#import方式引入;

 #import <Foundation/Foundation.h>
#import "Book.h" @interface Person : NSObject
{
Book *_book;
} - (void)setBook:(Book *)book;
- (Book *)book; @end

一种是通过@class引入

1、#import方式会包含被引用类的所有信息,包括被引用类的变量和方法;

2、@class方式只是告诉编译器在A.h文件中B *b只是类的声明,具体这个类里有什么信息,这里不需要知道,等实现文件中真正要用到时,才会真正去查看B类中信息;

3、使用@class方式由于只需要知道被引用类(B类)的名称就可以了,而在实现类由于要用到被引用类中的实体变量和方法,所以在.m文件中需要使用#import来包含被引用类的头文件;
4、如果有上百个头文件都#import了同一个文件,或者这些文件依次被#improt,那么一旦最开始的头文件稍有改动,后面引用到这个文件的所有类都需要重新编译一遍,这样的效率也是可想而知的,而相对来 讲,使用@class方式就不会出现这种问题了。

5.对于循环依赖关系来说,比方A类引用B类,同时B类也引用A类,B类的代码:

由上可知,@class是放在.h中的,只是在引用一个类,将这个被引用类作为一个类型,在.m实现文件中,如果需要引用到被引用类的实体变量或者方法时,还需要使用#import方式引入被引用类。

下来说一说,autorelease方法

  1> 会将对象放到一个自动释放池中。
  2> 当自动释放池被销毁时,会对池内所有对象做一次release操作。
  3> autorelease方法会返回对象本身。
  4> 调用autorelease方法之后,对象的计数器不变。

自动释放池
在程序运行过程中,可以创建多个自动释放池。自动释放池以栈的形式存在内存中(先进后出)。
当对象调用autorelease方法时,会将该对象放到栈顶的释放池。

创建自动释放池

 int main()
{
// 创建自动释放池
@autoreleasepool{
// 创建对象,并调用autorelease方法
Person *p = [[[Person alloc] init] autorelease];
} // 自动释放池销毁,对象销毁 return ;
}

一般当对象占用内存较大时,不要使用autorelease。调用autorelease之后,不用再使用release。

一般来说,除了alloc、new或copy之外的方法创建的对象都被声明了autorelease。

开发中可以提供一些类方法来快速创建已经autorelease的对象。

例如:

 + (id)person
{
// 创建对象时,不直接使用类名,建议使用self
return [[[self alloc] init] autorelease]
}

ARC(Automatic Reference Counting)

在ARC模式下把指针分为两种
强指针:默认情况下,所有指针都是强指针__strong。
弱指针:__weak 。

ARC的判断准则:只要没有强指针指向对象,就会释放对象。
 @interface Person : NSObject
@property(strong,nonatomic) Dog *dog;
@end @implementation Person
// 重写dealloc
-(void)dealloc
{
NSLog(@"Person被杀死了");
// [super dealloc];不允许调用
}
@end @interface Dog : NSObject
@property(weak,nonatomic) Person *person;
@end @implementation Dog
- (void)dealloc
{
NSLog(@"Dog被杀死了");
}
@end int main()
{
// ARC模式下的循环调用
Person *p = [[Person alloc] init];
Dog *d = [[Dog alloc] init]; p.dog = d;
d.person = p; return ;
}// 程序结束,所有对象被销毁

ARC特点

1> 不允许调用release、retain、retainCount。
2> 允许重写dealloc,但是不允许调用[super dealloc]。
3> @property参数
    strong:成员变量是强指针(适用于OC对象类型)。
    weak:成员变量是弱指针(适用于OC对象类型)。
    assign:适用于非OC对象类型。
4> 用strong替代retain。
ARC中的循环引用:strong替代retain,weak替代assign 。

好了,对于OC内存管理的初步总结先写到这里,因为是初次学习,有错误请指正,感谢!

12.Object-C--浅谈OC的内存管理机制的更多相关文章

  1. 浅谈Linux的内存管理机制

    转至:http://ixdba.blog.51cto.com/2895551/541355 一 物理内存和虚拟内存          我们知道,直接从物理内存读写数据要比从硬盘读写数据要快的多,因此, ...

  2. 转:浅谈Linux的内存管理机制

    一 物理内存和虚拟内存          我们知道,直接从物理内存读写数据要比从硬盘读写数据要快的多,因此,我们希望所有数据的读取和写入都在内存完成,而内存是有限的,这样就引出了物理内存与虚拟内存的概 ...

  3. OC的内存管理机制

    总的来说OC有三种内存管理机制,下面将分别对这三种机制做简要的概述. 1.手动引用计数(Mannul Reference Counting-MRC) mannul:用手的,手工的. 引用计数:reta ...

  4. 浅谈C&plus;&plus; allocator内存管理(对比new的局限性)&lpar;转&rpar;

    STL中,对内存管理的alloc的设计,迫使我去学习了allocator类.这里对allocator内存管理做了点笔记留给自己后续查阅.allocator类声明.定义于头文件<memory&gt ...

  5. 浅谈C语言内存管理、内存泄露、堆栈

    1.内存分配区间:         对于一个C语言程序而言,内存空间主要由五个部分组成:代码段(.text).数据段(.data).静态区(.BSS).堆和栈组成.         BSS段:BSS段 ...

  6. OC:内存管理、dealloc方法、copy知识点

    属性的声明:使⽤@property声明属性
 例如:@property NSString *name: 相当于@interface中声明了两个⽅法(setter.getter): 属性的实现:使⽤@s ...

  7. OC的内存管理&lpar;一&rpar;

    在OC中当一个APP使用的内存超过20M,则系统会向该APP发送 Memory Warning消息,收到此消息后,需要回收一些不需要再继续使用的内存空间,比如回收一些不再使用的对象和变量等,否则程序会 ...

  8. 浅谈OC内存管理

    一.基本原理 (一)为什么要进行内存管理. 由于移动设备的内存极其有限,所以每个APP所占的内存也是有限制的,当app所占用的内存较多时,系统就会发出内存警告,这时需要回收一些不需要再继续使用的内存空 ...

  9. Qt浅谈之一:内存泄露(总结)

    一.简介       Qt内存管理机制:Qt 在内部能够维护对象的层次结构.对于可视元素,这种层次结构就是子组件与父组件的关系:对于非可视元素,则是一个对象与另一个对象的从属关系.在 Qt 中,在 Q ...

随机推荐

  1. C&num; 拷贝数组的几种方法

    已知数组如下: int[] array = { 1, 5, 9, 3, 7, 2, 8 ,6, 4}; (1).引用复制,易引起错误,不推荐 int[] copy = array; (2).遍历拷贝 ...

  2. java charset detector

    https://code.google.com/p/juniversalchardet/downloads/list java移植mozilla的编码自动检测库(源码为c++),准确率高. 通过svn ...

  3. BZOJ 1742&colon; &lbrack;Usaco2005 nov&rsqb;Grazing on the Run 边跑边吃草&lpar; dp &rpar;

    dp... dp( l , r , k )  , 表示 吃了[ l , r ] 的草 , k = 1 表示最后在 r 处 , k = 0 表示最后在 l 处 . ------------------- ...

  4. &lbrack;转&rsqb;Pig与Hive 概念性区别

    Pig是一种编程语言,它简化了Hadoop常见的工作任务.Pig可加载数据.表达转换数据以及存储最终结果.Pig内置的操作使得半结构化数据变得有意义(如日志文件).同时Pig可扩展使用Java中添加的 ...

  5. linux命令11

    tar命令的使用 tar文件是把几个文件的(或)目录集合在一个文件夹里,是创建备份和归档的最佳工具. [root@localhost ~]# tar --help用法: tar [选项...] [FI ...

  6. Maven安装教程详解

    一.准备工作 1.确定电脑上已经成功安装jdk7.0以上版本                 2.win10操作系统                 3.maven安装包            下载地 ...

  7. rocketmq双主模式

    1.官网 https://rocketmq.apache.org/ 官方安装文档 https://rocketmq.apache.org/docs/quick-start/ 2.rocketmq多主配 ...

  8. Python之线程 1 - 线程基本概念

    一 背景知识 1.进程 2.有了进程为什么还要线程 3.线程的出现 二 线程和进程的关系 三 线程的特点 四 线程的实际应用场景 五 内存中的线程 六 用户级线程和内核级线程(了解) 1.用户级线程 ...

  9. xib view frame 大小调整

    1.IOS - xib(Interface Builder,view) - can't change view size(view不能改变大小问题) 很多时候,我们自定义tableview.colle ...

  10. Spring Cloud Zipkin

    Zipkin the idea is from the googlge paper:Dapper https://yq.aliyun.com/articles/60165 https://www.e4 ...