黑马程序员——OC基础---内存管理

来源:互联网 发布:sql server 2008r2 编辑:程序博客网 时间:2024/05/16 06:19

——Java培训、Android培训、iOS培训、.Net培训、期待与您交流! ——-
第一讲 内存管理的基本概念

为什么要内存管理?c语言中内存管理,函数在内存中堆区申请一些内存空间去(程序运行过程动态分配,链表的方式由程序员自己去管理)。
内存五大区域:1栈区 2堆区(程序运行) 3bss段 (没有初始化的全局变量和局部变量) 4数据区(已经初始化的) 5代码段。
移动设备的内存设备是极其有限的,必须考虑到内存的问题,如果一个app使用超过20M时候,会产生一个警告。

oc内存管理的范围
管理范围:管理任何继承NSObject的对象,对其他的基本数据类型无效。(因为基本数据类型数据占用的存储空间是固定的,一般存放在栈区)
对象类型是程序运行过程中的动态分配的,存储在堆区内存管理主要是对堆区中对象的内存管理。

第二讲 内存管理的原理和分类

内存不管理就会发生“内存泄漏”这里就会涉及到所有者,对象所有权的问题,任何对象都有可能一个或者多个所有者,只要一个对象拥有至少一个所有者,就会继续存在。
任何自己创建的对象都归自己所有,可以使用名称以“alloc”,“new”开头或者名称中包含“copy”的方法创建对象,可以使用retain来获取一个对象的所有权。或者使用“对象引用计数器”,每个oc对象都有自己的引用计数器,是一个整数表示对象被引用的次数。

第三讲 内存管理的原则
内存管理的原则:
1 只要还有人在使用这个对象,那么这个对象就不会被回收。
2 只要你想使用这个对象,那么就应该让这个对象的引用计数器+1
当你不想使用这个对象,应该让这引用计数器-1

注意事项:谁创建,谁release,如果你通过alloc,new.copy来创建一个对象,那么你就必须调用release或者autorelease方法
,不是你创建的就不用你负责。

第四讲 单个对象的内存管理

单个对象的野指针问题:
先看一个实例:

  //用Person类实例化一个对象        Person *p = [Person new];//此时对象的所有者是自己        //证明该对象有一个所有者        NSInteger count = [p retainCount];        NSLog(@"count1 = %lu",count);[p eat];        //此时该对象的堆区内存空间已经释放,称为【僵尸对象】        [p release];        //验证当对象在堆区的空间已经释放了,那么还能在使用p么?        //这种情况默认不会报错,如果要检测,需要开启僵尸对象检测        [p eat];//野指针访问

为了避免使用僵尸对象的方法就是对象释放完以后,给对象赋值为nil(对象空值)。
单个对象内存泄漏问题:
1 创建完成,使用之后,没有release
2 没有遵守内存管理的原则
3 不当的使用nil
4 在方法中对传入的对象进行了retain

第五讲 多个对象的内存管理

一句话代码引发的bug:

Person *p=[Person new];Car *car=[Car new];[p setCar:car];[p driver];[car release];[p driver];[p release];

p对象在没有销毁之前,可以任意多次调用自己的对象方法,但是此时会报错(注意僵尸对象检测)

第六讲 set方法内存管理

在OC中,每一个对象都有一个引用计数,来判断有多少个单位正在使用该对象,当引用计数为0时,说明没有单位再使用这片空间了,就会调用该对象的dealloc方法,将其抹掉,有的时候会出现这样一种情况,一个对象是另一个对象的属性(一般是用SET方法设置),这时我们便有必要理顺他们在内存之中的关系了。比如说一个Human类,一个Hands类,Hands对象又是Human对象的一个变量,注意,这个时候会出现一个问题:如果Human对象被释放的话,连带着Hands对象也会被释放,而如果在main中之后我们还需要使用Hands对象的话,我们就不得不再new一个,但很多人注意不到这点,总是不知道问题出现在哪里。为了解决这个问题,我们需要在Human类对象引用Hands类对象时,手动增加Hands类对象的一个引用计数

#import "Human.h"  @implementation Human  -(void)setHand:(Hands *)newHand  {      [newHand retain];      hand=newHand;  }  @end 

但是这时又会出现一个问题,Hands可以有多个对象,比如leftHand,rightHand,如果我先以leftHand为参数,这样leftHand会在内存中有两个引用,一个main生成的,一个Human类对象生成的,如果我再次调用setHand方法,这次以rightHand方法为参数,同样rightHand有两个引用计数,一个main生成的,一个Human类对象生成的。这时问题就出现了,leftHand在内存中是占有空间的,它在main方法中的计数会被释放,但在Human类对象中永远都释放不了了,因为Human类对象释放的话只会释放rightHand的引用计数了。这样被遗忘的leftHand会一直在那个内存的角落里默默流泪。于是有的同学便会说,那先把leftHand释放了啊,于是就有了下面这样的代码:

#import "Human.h"  @implementation Human  -(void)setHand:(Hands *)newHand  {      [hand release];      [newHand retain];      hand=newHand;  }  @end 

第七讲 @property参数的介绍
首先,我们看atomic 与nonatomic的区别与用法,讲之前,我们先看下面这段代码:

@property(nonatomic, retain) UITextField *userName;    @property(nonatomic, retain,readwrite) UITextField *userName;  @property(atomic, retain) UITextField *userName;  @property(retain) UITextField *userName;  @property(atomic,assign) int i;         @property(atomic) int i;         @property int i; 

上面的代码atomic是默认,assign是默认,readwrite是默认。
但是,如果你写上@property(nontomic)NSString *name;那么将会报一个警告。因为是非gc的对象,所以默认的assign修饰符是不行的。那么什么时候用assign、什么时候用retain和copy呢?推荐做法是NSString用copy,delegate用assign(且一定要用assign,不要问为什么,只管去用就是了,以后你会明白的),
非objc数据类型,比如int,float等基本数据类型用assign(默认就是assign),而其它objc类型,比如NSArray,NSDate用retain。
在继续之前,我还想补充几个问题,就是如果我们自己定义某些变量的setter方法,但是想让编译器为我们生成getter 方法,这样子可以吗?答案是当然可以。如果你自己在.m文件里面实现了setter/getter方法的话,那么编译器就不会为你再生成相应的 getter/setter了。请看下面代码:

//代码一:@interface BaseClass : NSObject{@public    NSString *_name;}@property(nonatomic,copy,readonly) NSString *name;  //这里使用的是readonly,所以会声明geter方法-(void) setName:(NSString*)newName;//代码二:@interface BaseClass : NSObject{@public    NSString *_name;}@property(nonatomic,copy,readonly) NSString *name;   //这里虽然声明了readonly,但是不会生成getter方法,因为你下面自己定义了getter方法。-(NSString*) name;   //getter方法是不是只能是name呢?不一定,你打开Foundation.framework,找到UIView.h,看看里面的property就明白了)-(void) setName:(NSString*)newName;//代码三:@interface BaseClass : NSObject{@public    NSString *_name;}@property(nonatomic,copy,readwrite) NSString *name;  //这里编译器会我们生成了getter和setter//代码四:@interface BaseClass : NSObject{@public    NSString *_name;}@property(nonatomic,copy) NSString *name;  //因为readwrite是默认行为,所以同代码三复制代码  上面四段代码是等价的,接下来,请看下面四段代码:  复制代码//代码一:@synthesize name = _name;  //这句话,编译器发现你没有定义任何getter和setter,所以会同时会你生成getter和setter//代码二:@synthesize name = _name;  //因为你定义了name,也就是getter方法,所以编译器只会为你生成setter方法,也就是setName方法。-(NSString*) name{    NSLog(@"name");    return _name;}//代码三:@synthesize name = _name;   //这里因为你定义了setter方法,所以编译器只会为你生成getter方法-(void) setName:(NSString *)name{    NSLog(@"setName");    if (_name != name) {        [_name release];        _name = [name copy];    }}//代码四:@synthesize name = _name;  //这里你自己定义了getter和setter,这句话没用了,你可以注释掉。-(NSString*) name{    NSLog(@"name");    return _name;}-(void) setName:(NSString *)name{    NSLog(@"setName");    if (_name != name) {        [_name release];        _name = [name copy];    }}

上面这四段代码也是等价的。看到这里,大家对Property的作用相信会有更加进一步的理解了吧。但是,你必须小心,你如 果使用了Property,而且你自己又重写了setter/getter的话,你需要清楚的明白,你究竟干了些什么事。别写出下面的代码,虽然是合法 的,但是会误导别人。

0 0
原创粉丝点击