·您现在的位置: 云翼网络 >> 文章中心 >> 网站建设 >> app软件开发 >> IOS开发 >> 黑马程序员--Objective-C内存管理我之见解

黑马程序员--Objective-C内存管理我之见解

作者:佚名      IOS开发编辑:admin      更新时间:2022-07-23

------<a href="http://www.itheima.com" target="blank">java培训、Android培训、iOS培训、.Net培训</a>、期待与您交流! -------

内存管理

为什么要进行内存管理? 因为设备的内存空间是有限的,如果一直占用,而不回收空间,内存就会被一直占用,导致内存不足,  系统就会就会报警,严重的可能直接退出程序,因此,在软件开发过程中,需要进行内存管理,以保证高效快速的分配内存,并且在适当的时候释放和回收内存资源。   内存管理的范围? 任何继承了NSObject的对象,对基本数据 类型无效,主要对堆区的内存进行管理。   为什么要对堆区的内存空间进行管理,基本数据类型不需要管理呢? 因为基本数据类型的数据会被放入栈中,他们依次紧密排列,遵循先进后出的原则,系统会自动进行管理。 而对象类型会被放到堆中,堆中的内存是不连续的,无法自动释放,所以需要我们管理。   内存管理的原理? (1)对象的所有权及引用计数器       对象的所有权:任何对象都有一个或多个拥有者,只要一个对象至少还有一个拥有者,他就不会被释放 (2) 任何自己创建的对象都归自己所有,可以使用alloc和new开头或名字中包含copy的方法创建对象,使用return来获得一个对象的所有权。 (3) OC中都有专门8个字节的存储空间来存储引用计数器,用整数表示对象被引用的次数。 引用计数器是判断对象是否回收内存空间的依据。 (4) 给对象发送消息          return  计数器+1,方法返回对象本身          release  计数器-1,但不代表释放,autorelease 也可使引用计数器-1.          retaincount消息  用来获得当前引用计数器的值    引用计数器为0,系统像对象发送dealloc消息,(调用[super dealloc]方法,一定要写在后面)释放相关资源,对象被回收后将不能在被使用(僵尸对象),僵尸对象不可复活,否则会导致程序崩溃,导致野指针错误   如何防止野指针错误,在对象被释放后赋值为nil.   内存管理的分类? MRC 手动内存管理 ARC 自动内存管理 垃圾回收   iOS不支持   内存管理的原则? 如果还有人使用某个对象,这个对象就不会被回收。 如果有人想使用这个对象 那么就应给让对象的引用计数器+1 如果不想使用这个对象,就有那个该让对象的引用计数器-1 谁创建谁relesase   谁retain 谁release.有始有终 有加有减。   内存研究的内容? 野指针 内存泄露 野指针 定义的指针变量没有初始化。指向的空间已经被释放 内存泄露 :如果栈区的的空间已经被释放了,而堆区的空间还没与被释放,堆区的空间就被泄露了。 

 

 

 单个对象内存管理

  1. 野指针错误:

      访问了一块坏的内存

  2. 僵尸对象:

      如果一个对象已经被释放,这个对象被称为僵尸对象

  3. 空指针:

      没有指向任何东西的指针,给空指针发送消息不会报错

  4. nil和NULL的区别

     nil  对象指针

     NULL 类对象指针

   5. 避免使用僵尸对象的方法:

      对象释放了以后,给对象赋值为nil

   6. 单个对象的内存泄露

      情况1: 创建完成,使用之后,没有release

 

情况2: 没有遵守内存管理原则

 

        Dog *d = [[Dog alloc] init]

 [d retain];

 

      情况3: 不当的使用了nil

 

Dog *d = [[Dog alloc] init]

 

   d = nil;

 

      情况4:  在方法中对传入的对象进行了retain

 

Dog *d = [[Dog alloc] init]

 

[d compareColorWithOther: d];

 

 

基本数据类型set方法的写法    直接赋值

  

     -(void)setSpeed:(int)speed

{

 _speed = speed;

}

 

 

对于对象作为另外一个类的实例变量

  

  -(void)setDog:(Dog *)dog{

if(_dog!=dog){   //先判断是不是原来的对象

[_dog release];  //不是原来的对象做一次release,

_dog = [dog retain];

}

}
 

@PRoperty 参数
格式:@property (参数1,参数2) 数据类型 方法名 

1.是否要生成set方法

readwrite : 同时生成setter和getter的声明、实现

readonly :只会生成getter的声明和实现

2.多线程管理

nonatomic :性能高(一般用这个)

atomic :性能低(默认)

3.set管理相关参数

retain :release旧值,return新值(适用于oc对象类型)

assign : 直接赋值(默认,适用于非OC对象)

copy : release旧值,copy新值   (1)  assign 直接赋值

  -(void)setCar:(Car *)car{

_car =car;

}

(2)  retain  release旧值,再retain新值

在一个类中有关联其他对象的时候,这个对象的      @property(nonatomic,assign)  数据类型  方法名

     -(void)setCar:(Car *)car{

if(_car!=car){   //先判断是不是原来的对象

[_car release];  //不是原来的对象做一次release,

_car = [car retain];

}

}

 

 

(3) 是否要生成set方法(若只读,则不生成)

readonly :只读,只会生成getter的声明和实现

readwrite:默认的,同时生成setter和getter的声明和实现

(4)多线程管理

    @property(nonatomic,assign)  数据类型  方法名

   高性能,一般使用nonatomic

(5)  set和get方法的名称

修改set和get方法的名称,主要用于布尔类型。因为返回布尔类型的方法名一般以is开头,修改名称一般用 在布尔类型中的getter 

 

//替换set方法名称  @property(nonatomic,assign ,setter= isVip:) 

//替换get方法名称  @property(nonatomic,assign ,getter = isVip)

//替换set,get方法名称  @property(nonatomic,assign ,setter=isVip,getter = isVip:) 

 

 

 

 

 

循环retain问题

会导致两个对象都会内存泄露

防止方法:

1) 让某一个对象多释放一次(注意顺序) 

2) 推荐方法: 一端使用assign 一端使用retain

Dog.h
#import <Foundation/Foundation.h>
//#import "Person.h"
@class Person;
@interface Dog : NSObject
//狗有个主人
@property (nonatomic,retain) Person *owner;//这里用retain
@end

Dog.m
#import "Dog.h"
#import "Person.h"

@implementation Dog
- (void)dealloc
{
    [_owner release];  //
    NSLog(@"Dog dealloc");
    [super dealloc];
}
@end

Person.h

#import <Foundation/Foundation.h>
//#import "Dog.h"
@class Dog;
@interface Person : NSObject
//人拥有一条狗
@property (nonatomic,assign) Dog *dog;//这里用assign
@end

Person.m
#import "Person.h"
#import "Dog.h"

@implementation Person
- (void)dealloc
{
    
//    [_dog release];  //nil这里不在需要
    NSLog(@"Person dealloc");
    [super dealloc];
}
@end

main.m    

#import <Foundation/Foundation.h>
#import "Dog.h"
#import "Person.h"


int main(int argc, const char * argv[]) {
    @autoreleasepool {
        
        Person *p = [Person new];  //1
        Dog *d = [Dog new];   //1
        
        //人有一条狗
        p.dog = d;    //  d   1
        d.owner = p;  //  p   2
        
        [p release];  //0
        [d release];  //
        
//        [d release];
       
//        [p release];
        
    }
    return 0;
}

                                                                                                                                                                                            

 

 

类方法不需要管理内存

autorelease基本使用

一个对象调用autorelease,将这个对象放到位于栈顶得的释放池

@autoreleasepool

{  //   

}

概念介绍:  一种支持引用计数器的内存管理方式,可以暂时保存某个对象,然后再内存池自己的排干的时候对其中的每个对象发送release消息

好处:  不用担心对象释放时间,也不用担心什么时候调用release

原理:

      autorelease实质上只是把对release的调用延迟了,对于每一个autorelease,该Object放入了当前的Autoreleasepool中,当该pool释放时,该pool中的所有Object都会被调用release;

 

自动释放池: 特殊的栈结构(数据结构),和内存的栈区结构不同

特点: 对象可以加入到自动释放池中,自动释放池结束的时候,会给释放池中的每个对象发送一条release消息

使用:

1)自动创建释放池

autoreleasepool{

}

2)加入自动释放池,

在自动释放池中

[对象 autorelease]

加入到自动释放池中以后,引用计数器不会变化

 

基本用法总结

1.会将对象放到一个自动释放池中

2.当自动释放池被销毁时,会对尺子里的所有对象做一次release操作

3.会返回对象本身

4.调用完autorelease方法后,对象的计数器不变。

 

int main(int argc, const char * argv[]) {
    //1 创建自动释放池
    Person *p = [Person new];  // p  1
    @autoreleasepool {//自动释放池开始
        
        [p run];
 
        NSLog(@"%lu",p.retainCount); // 1
        
        // [p autorelease] 把对象p加入到自动释放池中
        // 注意:加入到自动释放池中以后, 引用计数不会变化
        [p autorelease];  //加入自动释放池,
        NSLog(@"%lu",p.retainCount); // 1
        
        [p run];
        
    }//自动释放池结束   [p release];
    [p run];
    return 0;
}

 

 

 

注意及错误用法:

1)并不是放到自动释放池中的代码产生的对象就会自动释放,如果需要释放,必须自动加入到自动释放池

2)如果对象调用了autorelease但是调用autorelease的时候,没有在任何一个自动释放池中,此时该对象也不会被加入到自动释放池

3)我们只需要在自动释放池代码块中调用autorelease就可以把对象加入到自动释放池

4)内存较大尽量不要使用autorelease

 

自动释放池嵌套使用

 

为什么内存管理只管理内存对象?

堆中内存不连续,无法自动释放!

 

我们如何对内存对象进行管理!

通过操作对象的引用计数器

 

autorelease的应用场景 

经常用来在类方法中快速创建一个对象

 

  ARC 指针分类 (1)强指针:默认情况下所有指针都是强指针  关键字__strong (2)弱指针:__weak关键字修饰的指针   原则 :当开启ARC时,编译器将自动在代码合适的地方插入retain、release、autorelease.           永远不要写retain、release、autorelease;   判断准则:只要没有强指针,对象就会被释放   ARC机制:

1)判断是否是ARC机制

查看项目信息,不能使用retain release和autorelease,retainCount ,不能调用[super dealloc]

2)使用

正常创建对象,不用手动释放对象

ARC下单对象内存管理

  ARC机制下,对象没有被强指针指向,对象会立即释放空间

(两个下划线)__strong 修饰的指针是强指针,可以不写 

                 __weak 修饰的指针是弱指针

 1) 强指针指向了其他内容,对于对象来说就没有强指针指向了

 2) 弱指针赋值为nil

 

ARC下多对象的内存管理 

 

ARC机制下不能使用retain 应该使用strong 和 weak

ARC下循环引用问题

循环引用时一端使用strong , 一端使用weak.

 

ARC下set方法内存和@property参数

  原子性/读写 和MRC下一样

 MRC            ARC

assign         assign 适用于非oc对象

retain         strong(强指针) OC的其他对象  weak (成员变量是弱指针适用于oc对象)

copy            copy

 

ARC使用特点及注意事项

  特点: 

1) 不允许调用retain release、retainCount

2) 允许重写dealloc,但是不能调用[super dealloc] 

 

注意事项:

  1)ARC中,只要弱指针对象不存在,直接把弱指针清空(赋值nil)操作

  2)__weak Person *p = [Person new]

弱指针指向空间销毁过程: (1)释放对象空间  (2)指针赋值nil

 

 

 

 

MRC转换为ARC

edit->Refactor->convert to Objective-C ARC…简单代码

ARC兼容非ARC的类

 转变为非ARC   -fno-objc-arc

 转变为ARC的    -f-objc-arc