OC教程8-内存管理

OC8-内存管理

OC语言中的内存管理机制为ARC(Automatic Reference Counting,自动引用计数)。与2011年中旬推出,替换陈旧且低效的手动内存管理,关于手动内存管理的内容,本章教程不在讲授。本章主要从以下几个方面对内存管理进行展开讲讲解。

  • 内存管理原则
  • 对象引用类型
  • 属性引用类型
  • 强引用循环
  • AUTO类型与释放池

1,内存管理原则

核心原则:没有被对象指针使用(指向)的内存立即释放。这个原则决定一般情况下,不会用内存泄露的情况存在,但存在特殊情况,也是本章最后一个专题要阐述的问题。

内存泄露是指,一块内存没有指针引用的内存,但由于一些原因,无法释放,造成内存浪费的情况。

管理原则

  • 强引用对象指针使用中的内存绝对不会释放。
  • 归零弱引用对象指针使用中的内存,释放情况由其他对象指针决定,本身不影响内存释放与否,但其指向的内存一旦释放,本身立即置nil,保证不出现野指针。
  • 弱引用对象指针使用中的内存,释放情况由其他对象指针决定,本身不影响内存释放与否,但其指向的内存一旦释放,本身值不会发生改变,会出现野指针的情况。
  • AUTO类型对象指针使用过或使用中的内存,出释放池才会释放。通过AUTO类型与释放池配合使用,可以精确调节内存时间,提前或延后。

2,对象引用类型

对象引用类型有如下四种

  • 强引用:__strong修饰的对象指针或无修饰的对象指针
  • 归零弱引用:__weak修饰的对象指针
  • 弱引用:__unsafe__unretain修饰的对象指针
  • AUTO类型:__autoreleasing修饰的对象指针

首先分析强引用对象指针的使用情况。在分析内存释放情况时,我们需要一个测试类进行释放测试。当一个对象释放时,它的dealloc方法会被调用。所以我们在dealloc方法中进行相关输出,便能精确看到,该对象何时释放。

@interface Test : NSObject

@end

@implementation Test

- (void)dealloc
{
    NSLog(@"该对象释放");
}

@end

情况1

int main(int argc, const char * argv[])
{
    {
        Test * t = [[Test alloc]init];
    }
    //代码运行至此,t的作用域结束,t指向的内存并无其他对象指针使用,所有,该内存在此释放。

    return 0;
}

情况2

int main(int argc, const char * argv[])
{
    {
        Test * t1;
        {
            Test * t = [[Test alloc]init];
            t1 = t;
        }
        //代码运行至此,t作用域结束,但t指向的内存仍有t1对象指针使用,所以在此该内存不会释放。
    }
    //代码运行至此,t1作用域结束,t1指向的内存再无其他对象指针使用,所有在此内存释放。

    return 0;
}

情况3

int main(int argc, const char * argv[])
{
    {
        Test * t = [[Test alloc]init];
        t = nil;//代码运行至此,t不再指向之前分配的内存,之前的内存无对象指针只用,释放。
    }
    return 0;
}

以上是强引用的常用情况,其对象指针并无任何修饰符进行修饰,但已经OC语法规定,对象指针无修饰时,为强引用类型。

我们继续讨论归零弱引用类型的对象指针,对内存的影响。

情况1

int main(int argc, const char * argv[])
{
    {
        __weak Test * t1;
        {
            Test * t = [[Test alloc]init];
            t1 = t;
        }
        //代码运行至此,t作用域结束,t指向的内存仍有t1对象指针使用,但t1为归零弱引用,不会影响对象释放情况,所有在此内存释放,且t1本身值变为nil
    }

    return 0;
}

情况2

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

    __weak Test * t1 = [[Test alloc]init];//在此语句运行时,分配的内存并无除弱引用对象指针以外的对象指针使用,所以,该内存立即释放。

    return 0;
}

以上是归零弱引用的情况,不常用。归零弱引用只有一种情况下使用:

  • 代码块回调接口中的自身代码块引用自身对象

例如:

@interface Room : NSObject
@property (strong, nonatomic) Light *lightA;
@property (strong, nonatomic) SwitchB *s;

@end

@implementation Room
- (instancetype)init
{
    self = [super init];
    if (self) {
        self.lightA = [[Light alloc] init];
        self.s = [[SwitchB alloc] init];

        __weak __block Room * copy_self = self;//打破强引用循环,强引用循环的概念下文会讲解。

        self.s.changeStateBlockHandle = ^(SwitchState state)
        {
            if (state == SwitchStateOff)
            {
                [self.lightA turnOff];
            }
            else
            {
                [self.lightA turnOn];
            }
        };
    }
    return self;
}
@end

弱引用和归零弱引用管理内存的释放时间相同。弱引用是OC为兼容之前特殊情况下的内存管理而做的一个不常用类型。所以之后,我们不会使用弱引用,所有需要弱引用的地方全部以归零弱引用代替。

3,属性引用类型

属性的内存控制符,有四种情况。

  • strong
  • weak
  • copy
  • assgin

对于对象来说,可选的之后前三种。第四种,assgin为无内存管理,主要针对基础数据类型设计。例如

@property (assgin, nonatomic) int a;

如果一个属性是对象,那么其属性内存控制符必然是前三种之一。

strong:默认情况下使用,除特殊情况。

weak:在遇到强引用循环时,使用。

copy:在进行数值对象赋值时,使用。

例如

@property (copy, nonatomic) NSString * name;
@property (copy, nonatomic) NSNumber * age;

但也可以strong代替,所以,copy使用场景也不多见。

4,强引用循环

在内存管理中,强引用循环是最严重的失误,会造成大量内存泄露。通过一个例子来说明为什么会产生泄露。

首先用实际生活中的一个场景来具体的说明一下,问题产生的原因。

例如,现在在一个教室,有学生有老师。老师对自己的要求是,学生不离开教室,我就不离开教室。而学生对自己的要求是,老师不离开教室,我就不离开教室。这样一直持续下去的结果就是,双方谁都不会离开教室。

然后我们在看一下代码中合适会产生这种情况。

@interface Student : NSObject

@property (strong, nonatomic) Teacher *tea;
@end
@interface Teacher : NSObject

@property (strong, nonatomic) Student *stu;
@end
main()
{
    {
        Teacher * tea = [[Teacher alloc] init];
        Student * stu = [[Student alloc] init];

        tea.stu = stu;
        stu.tea = tea;
    }

}

上述代码中,可以发现,Student类有一个strong类型的属性tea,通过管理原则我们可以知道,stu对象存在其强引用属性tea一定存在,不会释放。同样Teacher有一个strong属性stu,tea对象存在意味着stu对象也绝对不会释放。这样当两个对象指针作用域消失时,其使用的内存无法释放,出现内存泄露。

这种问题便是内存管理中会遇到的强引用循环,也是目前能够造成内存泄露的唯一原因。需要对这样的情况在设计层面进行避免。互相包含对方类型的属性的结构中,必须有一方为归零弱引用。

目前存在双向包含的场景只有在回调中会用到

  • 目标动作回调中,储存target对象的属性为weak
  • 委托回调中,储存delegate委托人的属性为weak

除上述两种情况外,其他地方默认使用strong修饰属性即可。

5,AUTO类型与释放池

在内存管理中有一种较为特殊的类型叫AUTO类型,虽然名字和自动相关,但其释放仍需要手动配置释放池来调整。

__autoreleasing:被此种修饰符修饰的对象指针,其使用过和使用中的内存在出释放池时才会释放。所以可以通过手动配置自动释放池的位置来调节释放时间。

延迟释放的例子:

@autoreleasepool
{
   {
        __autoreleasing Student * stu = [[Student alloc] init];
   }//在此,stu的作用域虽然已经结束,但stu为AUTO类型,所以等代码运行到释放池结束才会释放
}
//在此位置,内存释放

提前释放的例子:

__autoreleasing Student * stu;
@autoreleasepool
{
   stu = [[Student alloc] init];
}
//在此位置,内存释放,虽然stu的作用域没有结束

使用AUTO的类型有两种情况

情况1为对象的便利构造器方法,需要延迟释放

+(id)student
{
    __autoreleasing Student * stu = [[Student alloc] init];
    return stu;
}

OC语言规定,方法返回的内存必须为AUTO类型。

情况2为在一个封闭循环内,用便利构造器创建对象

for(int i = 0;i<10000;i++)
{
    @autoreleasepool
    {
        __autoreleasing Student * stu = [Student student];
    }
}

因便利构造器返回的对象为AUTO类型,所以该对象指针使用的内存只有在出释放池时才会释放。但for循环中无释放池,这会造成,大量无用的对象无法立即释放。

添加释放池之后,内存便可以在使用结束之后立即释放。

时间: 2024-12-18 00:27:30

OC教程8-内存管理的相关文章

关于OC中得内存管理问题,alloc,retain,release,copy,dealloc

我们都知道,一个手机,它的内存是有限的,而每一个手机应用都是需要一定空间,当应用所占空间过大时,系统就会发出警告,怎样在有限的空间中,做到更高效实用美观的效果呢? 这时候就牵涉到OC中得内存管理了. 在OC这门语言中,是不存在垃圾回收机制的,但是它采用了另外一种形式或者说方法,实现这一个空间回收的效果,那就是引用计数器. 别看-引用计数器,这个名字很高大上,实际是它就是一个整数. 所以OC中分配4个字节才存储它. 引用计数的值只有两种:0和非0,我们知道,计算机其实是很笨的,结果只有这两种时,它

黑马程序员-oc对象的内存管理

oc没有java的垃圾回收机制,所以对象的内存释放很重要,基本数据类型,我们不用理会,编译器会处理: oc的每个对象内部都由一个计数器,用来记录当前有几个指针在指向该对象:当计数器为0时该对象会从内存中释放: 相关方法和概念: 1:retain:对象方法,调用该对象方法,计数器+1,有返回值,返回对象本身: 2:release:没有返回值,计数器-1: 3:retainCount:获取当前计数器的值: 4:dealloc:当对象被回收时,就会调用该方法,覆盖该方法时一定要调用[super dea

IOS阶段学习第20天笔记(OC中的内存管理)

IOS学习(OC语言)知识点整理 一.OC中的内存管理 1)概念:内存管理的对象为所有继承了NSObject的对象,对基本数据(如:int .float.double...)无效      OC中采用引用计数器对内存做管理,他是一个整数数据,表示对象引用的次数,每个对象分配4字节      的内存空间存放引用计数器.当一个对象的引用计数器为0时 它将被自动释放,反过来说 当使用alloc.      new .copy(mutableCopy)创建新对象时,引用计数器默认为1 2)黄金法则 当使

java Vamei快速教程22 内存管理和垃圾回收

作者:Vamei 出处:http://www.cnblogs.com/vamei 欢迎转载,也请保留这段声明.谢谢! 整个教程中已经不时的出现一些内存管理和垃圾回收的相关知识.这里进行一个小小的总结. Java是在JVM所虚拟出的内存环境中运行的.内存分为栈(stack)和堆(heap)两部分.我们将分别考察这两个区域. 栈 栈的基本概念参考纸上谈兵: 栈 (stack).许多语言利用栈数据结构来记录函数调用的次序和相关变量(参考Linux从程序到进程). 在Java中,JVM中的栈记录了线程的

oc中的内存管理

•所谓内存管理, 就是对内存进行管理, 涉及的操作有: 分配内存 : 比如创建一个对象, 会增加内存占用 清除内存 : 比如销毁一个对象, 能减小内存占用 •内存管理的管理范围 任何继承了NSObject的对象 对其他非对象类型无效(int.char.float.double.struct.enum等 ) •只有OC对象才需要进行内存管理的本质原因 OC对象存放于堆里面 非OC对象一般放在栈里面(栈内存会被系统自动回收) •系统是如何判断 什么时候需要回收一个对象所占用的内存? 根据对象的引用计

黑马程序员--Objective-C之--OC中的内存管理

对于面向对象的变成语言,程序需要不断地创建对象. 初始,创建的所有程序通常都有指针指向它,程序可能需要访问这些对象的实例变量或调用这些对象的方法,随着程序的不断执行,程序再次创建了一些新的对象, 而那些老的对象已经不会再被调用,也不再有指针指向他们,如果程序没有回收他们占用的内存,就会出现内存泄露. 如果程序一直泄露内存,那么可用内存就会越来越少,直到没有足够的内存,程序將会崩溃.目前,主要有两种管理内存的技术,一是引用计数,二是垃圾回收. iOS平台目前只支持引用计数,Mac平台支持垃圾回收.

OC语法7——内存管理之@property参数

@property的参数: 我们已经知道为了给开发者提供便捷,OC提供了@porperty关键字,它可以自动生成属性的set和get方法. 但是我们又知道,在OC中还面临者对象内存管理的问题,而且我们遵循“谁创建,谁释放”的原则管理内存. 所以我们得重写set方法:把原先的成员变量(对象)release掉,然后给新成员变量retain. 还得重写realloc方法:在realloc中调用release,释放该对象. 呵呵.我们引入@property的目的就是为了让其自动生成set和get方法的,

OC - 浅谈内存管理

今天看到一篇不错的文章关于OC内存管理的,转载一下与你共享 概述我们知道在程序运行过程中要创建大量的对象,和其他高级语言类似,在ObjC中对象时存储在堆中的,系统并不会自动释放堆中的内存(注意基本类型是由系统自己管理的,放在栈上).如果一个对象创建并使用后没有得到及时释放那么就会占用大量内存.其他高级语言如C#.Java都是通过垃圾回收来(GC)解决这个问题的,但在OjbC中并没有类似的垃圾回收机制,因此它的内存管理就需要由开发人员手动维护.今天将着重介绍ObjC内存管理: 1 引用计数器 2

OC中的内存管理01

一.内存管理要遵循的原则 1> 谁创建,谁release (看到 alloc.copy.new就应该有写release的冲动). 2> 谁retain,谁release (简单的来说就是要负责嘛,不能retain后不管了). 3> 说明下苹果在这方面做的很严谨,后面的学习会接触到ARC,Xcode会帮我们自动管理相应的内存. 二.基本原理 1> 每个对象的内部都封装的一个与之相关的整数,称之为引用计数器. 2> 当使用alloc.new或者copy创建对象时,计数器被设置为1