[objective-c] 08 - 内存管理

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循环中无释放池,这会造成,大量无用的对象无法立即释放。

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

[代码展示]

1.

======Test类的声明======

#import <Foundation/Foundation.h>

@interface Test : NSObject

@end

======Test类的实现======

#import "Test.h"

@implementation Test

//对象被释放之前要调用的方法

-(void)dealloc

{

NSLog(@"Test被释放。");

}

@end

======main======

#import <Foundation/Foundation.h>

#import "Test.h"

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

//__strong 强引用,它会造成对象的引用计数器的变化(+1)

@autoreleasepool {

__strong Test *t2;

{

Test *t = [[Test alloc]init];

t2 = t;

t=nil;

NSLog(@"2");

}

NSLog(@"1");

}

NSLog(@"3");

return 0;

}

======运行结果======

2

1

Test被释放。

3

2.

======Test类的声明======

#import <Foundation/Foundation.h>

@interface Test : NSObject

@end

======Test类的实现======

#import "Test.h"

@implementation Test

-(void)dealloc

{

NSLog(@"Test被释放");

}

@end

======main======

#import <Foundation/Foundation.h>

#import "Test.h"

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

//    @autoreleasepool {

//        //__weak 弱引用,不会造成引用计数器的变化,同时也不能阻止对象的释放,对象被释放后自动指向了nil

//        __weak Test *t2;

//        {

//            __strong Test *t = [[Test alloc]init];

//            t2 = t;

//            NSLog(@"%p",t2);

//            NSLog(@"1");

//        }

//        NSLog(@"%p",t2);

//        NSLog(@"2");

//    }

@autoreleasepool {

//__unsafe_unretained 与__weak相同,别在对象被释放后不会指向nil

__unsafe_unretained Test *t2;

{

__strong Test *t = [[Test alloc]init];

t2 = t;

NSLog(@"%p",t2);

NSLog(@"1");

}

NSLog(@"%p",t2);

NSLog(@"2");

}

return 0;

}

======运行结果======

0x100300220

1

Test被释放

0x100300220

2

时间: 2024-10-17 08:26:08

[objective-c] 08 - 内存管理的相关文章

iOS核心语言Objective C语言 —— 内存管理

本分享是面向有意向从事iOS开发的伙伴以及苹果产品的发烧友们,或者已经从事了iOS的开发者,想进一步提升者.如果您对iOS开发有极高的兴趣,可以与我一起探讨iOS开发,一起学习,共同进步.如果您是零基础,建议您先翻阅我之前分享的iOS开发分分钟搞定C语言系列,然后在开始Objective C语言的学习,如果您遇到问题也可以与我探讨,另外将无偿分享自己整理出来的大概400G iOS学习视频及学习资料,都是干货哦!可以新浪微博私信?关注极客James,期待与您的共同学习和探讨!!由于时间有限,每天在

08.内存管理

手动内存管理什么是内存管理 .进程空间-代码区:只读 -堆:自己创建.自己回收释放,对象是保存在堆区的.-全局区:进程启动时候分配,进行结束时释放.-栈:局部变量,自动创建,自动释放空间.什么是内存管理-只要对堆内进行管理,所谓的管理是值内存的分配(创建)和释放(回收). 引用计算器为1(alloc,copy,new).引用计算器为1(alloc,copy,new)-每个对象都有自己的引用计数器,引用计算器是用来计算对象被引用的次数-发送消息时(alloc,copy,new),将引用计数器置1.

Objective -C Memory Management 内存管理 第一部分

Objective -C Memory Management??内存管理??第一部分 Memory management is part of a more general problem in programming called resource management. 内存管理是资源管理的一部分. Every computer system has finite resources for your program to use. These include memory, open fi

Objective C 内存管理[转]

1  配对原则 alloc – release new – release retain - release copy – release 2  new和alloc-init的区别 (1)区别只在于alloc分配内存的时候使用了zone. 这个zone是个什么呢? 它是给对象分配内存的时候,把关联的对象分配到一个相邻的内存区域内,以便于调用时消耗很少的代价,提升了程序处理速度. (2)为什么不推荐使用new 因为若用了new,则初始化方法只能是init.这样,假如你想调用initWithFram

objective C 内存管理及属性方法详解

oc为每个对象提供一个内部计数器,这个计数器跟踪对象的引用计数,当对象被创建或拷贝时,引用计数为1,每次保持对象时,调用retain接口,引用计数加1,如果不需要这个对象时调用release,引用计数减1,当对像的引用计数为0时,系统就会释放掉这块内存,释放对象调用dealloc 当对象包含其他对象时,就得在dealloc中自己释放他们 NSObject是IOS所有类的基类 有两个基本函数,alloc和dealloc alloc类似于C++的new,dealloc类似于delete 当对象的re

Objective-C(内存管理)

引用计数器 每个OC对象都有一个占4个字节存储空间的引用计数器 当使用或创建一个对象时,新对象的引用计数器默认是1 retain:可以使引用计数器+1 release:可以是引用计数器-1 retainCount:获得当前的引用计数器的值 当对象被销毁时,会重写dealloc方法 -(void)dealloc { // 这句必须放在最后面 [super dealloc]; } 僵尸对象:所占内存已经被回收的对象,僵尸对象不能再使用 野指针:指向僵尸对象(不可用的内存)的指针 错误:EXC_BAD

IOS学习笔记3—Objective C—简单的内存管理

今天简述一下简单的内存管理,在IOS5.0以后Apple增加了ARC机制(Automatic Reference Counting),给开发人员带来了不少的方便,但是为了能更好的理解IOS内存管理机制,还是需要对其比较了解. 1.在OC中,每个对象都有一个保留计数,创建时每个对象都有一个初始值为1的保留计数,释放时,保留计数都为0 2.创建自动释放的对象 要求以一个方法创建对象时,以自动释放的形式返回该对象是一个很好的编程实践 +(Car *)car { Car *myCar = [[Car a

(转)从内存管 理、内存泄漏、内存回收探讨C++内存管理

http://www.cr173.com/html/18898_all.html 内存管理是C++最令人切齿痛恨的问题,也是C++最有争议的问题,C++高手从中获得了更好的性能,更大的自由,C++菜鸟的收获则是一遍一遍的检查代码和对 C++的痛恨,但内存管理在C++中无处不在,内存泄漏几乎在每个C++程序中都会发生,因此要想成为C++高手,内存管理一关是必须要过的,除非放弃 C++,转到Java或者.NET,他们的内存管理基本是自动的,当然你也放弃了自由和对内存的支配权,还放弃了C++超绝的性能

iOSDay17之内存管理

1.内存管理的方式 1> iOS应用程序出现Crash(闪退),90%的原因是因为内存问题. 2> 内存问题 野指针异常:访问没有所有权的内存,如果想要安全的访问,必须确保空间还在 内存泄露:空间使用完之后没有及时释放 过度释放:对同一块存储空间释放多次,立刻crash 内存溢出:所有存储空间被占用 3> 管理内存的三种方式 垃圾回收机制:程序员只需要开辟存储空间,系统会自动回收内存.java采用的该机制 MRC:手动引用计数机制,由开发人员开辟空间,手动添加影响引用计数增加或减少的方法