zoukankan      html  css  js  c++  java
  • 【Objective-C学习记录】第二十八天

    iOS内存管理的方式是引用计数机制,分为MRC(人工引用计数)和ARC(自动引用计数)。

    引用计数管理内存的理念是:通过控制内存或者对象的引用来实现生成、持有、释放、销毁对象的操作。

    如果增加的次数大于减少的次数,会造成内存泄露;

    如果减少的次数大于增加的次数,会造成过度释放;

    如果增加的次数等于减少的次数,还继续访问,会造成野指针。

    1.生成:对象的引用计数从0到1

    2.持有:增加一个引用,让对象的引用计数加1

    3.释放:减少一个引用,让对象的引用计数加1

    4.销毁:当对象的引用计数到0时(事实上并不会为0,后面会解释),系统就会回收这个内存空间

    当这块空间被系统回收之后,就不能通过指针去访问这块空间了,容易造成野指针。

    注意!引用计数的概念只存在于堆区域,针对堆区的对象。

    retainCount方法

    可以通过改方法获取对象的引用计数的值,返回值是NSUInteger。

    1.生成

       + (instancetype)alloc;在堆区域开辟一块内存空间,释放对象,并且将内存清零,同时将此对象的引用计数变为1,是从0到1的过程。

    1 Hero *hero = [[Hero alloc] init];//该过程为hero对象开辟了内存空间,引用计数加1

    需要注意的是,直接赋值引用计数不会增加,如下:

    1 Hero *am = hero//不会引起hero对象引用计数加1

    2.持有

       retain;让对象的引用计数加1。

    1 [hero retain];//此时hero的引用计数为2
    2 [am retain];//此时hero/am的引用计数为3

    3.释放

       A.release;让对象的引用计数减1,而且是立即减1。

    1 [hero release];//hero的引用计数为2
    2 [hero release];//hero的引用计数为1
    3 [hero release];//hero的引用计数为1

    需要注意的是,在对hero对象进行连续三次释放操作后,我们在XCode可以通过retainCount方法查看hero的引用计数的值,结果发现并不是我们所期望的0,这是因为系统内部引用计数值没有0,0只是人与人直接方便交流和理解而引人的一个数,但从本质上来说,该对象已经被释放了,如果再去访问hero对象有可能造成野指针(被回收的内存空间被其他指针或变量占用后,再次通过原指针去访问该内存)。

    即引用计数值最小是1,没有0。

       B.autorelease;此方法也是让对象的引用计数减1,不过区别于release,并不是立即减1,而是在未来的某个时刻,触发减1操作。该操作与自动释放池相关。

       自动释放池(autoreleasepool)

       自动释放池是一个容器,来记录池子内部对象接收到的autorelease消息,哪个对象接收,接收了几次,谁先接收,谁后接收,当池子释放时,就会根据这些记录的信息来进行减1操作。

       需要注意的是,自动释放池与栈区类似,遵循先进后出即先开辟空间的后被进行减1操作。

    1 Hero *sf = [[Hero alloc] init];
    2 NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
    3 [hero autorelease];
    4 [am autorelease];
    5 [sf autorelease];
    6 [pool release];//该语句执行时,会对上述标记的指针进行减1操作,通常情况下系统会自动完成此功能

    4.销毁

       dealloc;从内存中销毁对象。可在类的.m文件中直接重写此方法,当对象真正从内存中销毁时会自动调用:

    1 - (void)dealloc
    2 {
    3     NSLog("%@ is dealloc", self);
    4     [super dealloc];
    5 }

    可以将销毁方法和初始化方法对比一下:

    初始化:                销毁:

    [super init]           释放实例变量

    初始化实例变量       [super dealloc]

    不难发现销毁方法与初始化方法的顺序相反。因此只要重写了dealloc方法,[super dealloc]永远都在最后一行。分为两步:1.先将自身的实例变量释放掉2.执行父类中的dealloc方法,释放继承过来的实例变量。

    协议

    类似于Java C++里的接口对象

    copy,需要类使用NSCopying协议

    1.伪拷贝

       特点:相当于没有拷贝,只是让外界的对象执行了一次retain操作。 

    1 - (id)copyWithZone:(nullable NSZone *)zone
    2 {
    3     return [self retain];
    4 }    

    2.浅拷贝

       特点:拷贝的是地址,会初始化一个新的对象,但是新对象和旧对象共用一份内容,改变其中一个对象实例变量的值,另一个也会访问到改变之后的值。

       注意!如果实例变量是字符串且初始化时指向了常量区,那么在拷贝后更改字符串的值,相当于在常量区又重新开辟了一个新的空间,所以这种情况下并不会影响另一个对象实例变量的值。

    1 - (id)copyWithZone:(NSZone *)zone
    2 {
    3      //实例化一个新的对象
    4      Hero *hero = [[Hero allocWithZone:zone] init];
    5      //为新对象的实例变量赋值  
    6      Hero.name = self.name;
    7      Hero.level = self.level; 
    8      return hero;   
    9 }

    3.深拷贝

       特点:会重新初始化一个对象,并且内存也是两份,改变其中一个的值,另一个不会发生改变。可以理解为复制粘贴操作。

    1 - (id)copyWithZone:(NSZone *)zone
    2 {
    3      Hero *hero = [[Hero allocWithZone:zone] init];
    4      hero.name = [self.name copy];
    5      hero.level = [self.level copy];
    6      return hero;
    7 }
  • 相关阅读:
    js:鼠标事件
    js:argument
    js:|| 和 && 运算符 特殊用法
    css:选择器
    css:清除浮动 overflow
    jquery:after append appendTo三个函数的区别
    WIndow Document
    css:颜色名和十六进制数值
    安装centos出错
    Leetcode | Unique Paths I & II
  • 原文地址:https://www.cnblogs.com/shvier/p/5086926.html
Copyright © 2011-2022 走看看