黑马程序员——OC内存管理
2015-04-14 10:06
274 查看
——Java培训、Android培训、iOS培训、.Net培训、期待与您交流! ——-
1、移动设备的内存极其有限,每个app所能占用的内存是有限制的。
2、当app所占用的内存较多时,系统会发出内存警告,这时得回收一些不需要再使用的内存空间。比如回收一些不需要使用的对象、变量等。
3、管理范围:任何继承了NSObject的对象,对其他基本数据类型(int、char、float、double、struct、enum等)无效。
对象的基本结构
1、每个OC对象都有自己的引用计数器,是一个整数,表示“对象被引用的次数”,即有多少人正在使用这个OC对象。
2、每个OC对象内部专门有4个字节的存储空间来存储引用计数器。
引用计数器的作用及操作
1、当使用alloc、new或者copy创建一个新对象时,新对象的引用计数器默认就是1。
2、当一个对象的引用计数器值为0时,对象占用的内存就会被系统回收。换句话说,如果对象的计数器不为0,那么在整个程序运行过程,它占用的内存就不可能被回收,除非整个程序已经退出。
操作引用计数器的方法基本使用:
1、retain :计数器+1,会返回对象本身
2、release :计数器-1,没有返回值
3、retainCount :获取当前的计数器
对象销毁
1、当一个对象的引用计数器值为0时,那么它将被销毁,其占用的内存被系统回收。
2、当一个对象被销毁时,系统会自动向对象发送一条dealloc消息。
3、一般会重写dealloc方法,在这里释放相关资源,dealloc就像对象的遗言。如下所示:
当对象被回收后,会出现几个概念:
1、僵尸对象 :所占用内存已经被回收的对象,僵尸对象不能再使用。
2、野指针 :指向僵尸对象(不可用内存)的指针,给野指针发送消息会报错。所报的错误为(EXC_BAD_ACCESS),这个是常见的错误,只要我们看到这个错误信息就知道是野指针问题。
*EXC_BAD_ACCESS : 访问了一块坏的内存(已经被回收、已经不可用的内存。
3、空指针 :没有指向任何东西的指针(存储的东西是nil、NULL、0),给空指针发送消息不会报错。
1、你想使用(占用)某个对象,就应该让对象的计数器+1(让对象做一次retain操作)。
2、你不想再使用(占用)某个对象,就应该让对象的计数器-1(让对象做一次release)。
3、谁retain,谁release。
4、谁alloc,谁release。
如下程序创建了人和书,人拥有书时一系列的计数器操作:
main函数
Book.h
Book.m
Person.h
Person.m
这是对于OC对象,如果是基本数据类型,setter方法不需要进行修改:
同时在代码中,只要是调用了alloc,也就是分配存储空间,在代码的下方必须要有release(autorelease),但是如果对象不是通过alloc产生的,比如说
此时在- (void)dealloc方法中,对self(当前)所拥有的其他对象做一次release,每一个对象对应一个release,如下:
书写格式:
参数分为四类:
1、set方法内存管理相关的参数
* retain : release旧值,retain新值(适用于OC对象类型)
* assign : 直接赋值(默认,适用于非OC对象类型)
* copy : release旧值,copy新值
2、是否要生成set方法
* readwrite : 同时生成setter和getter的声明、实现(默认)
* readonly : 只会生成getter的声明、实现
3、多线程管理
* nonatomic : 性能高 (一般就用这个)
* atomic : 性能低(默认)
4、setter和getter方法的名称
* setter : 决定了set方法的名称,一定要有个冒号 :
* getter : 决定了get方法的名称(一般用在BOOL类型)
对于OC对象类型,采用这种方式:
对于非OC对象类型,比如说int\float\enum\struct等,采用这种方式:
采用这种方式可以很好的简化代码,将更多的精力投入与功能上。
这种代码编译会报错。当使用@class在两个类相互声明,就不会出现编译报错。使用
和#import的区别:
1、#import方式会包含被引用类的所有信息,包括被引用类的变量和方法;@class方式只是告诉编译器在A.h文件中 B *b 只是类的声明,具体这个类里有什么信息,这里不需要知道,等实现文件中真正要用到时,才会真正去查看B类中信息。
2、如果有上百个头文件都#import了同一个文件,或者这些文件依次被#improt,那么一旦最开始的头文件稍有改动,后面引用到这个文件的所有类都需要重新编译一遍,这样的效率也是可想而知的,而相对来 讲,使用@class方式就不会出现这种问题了。
3、在.m实现文件中,如果需要引用到被引用类的实体变量或者方法时,还需要使用#import方式引入被引用类。
循环retain
即使我们的retain和release都没有问题,但是在程序运行中任然会有内存泄露,这时我们可以将其中一个retain改为assign就可以解决这个问题:
1、会将对象放到一个自动释放池中
2、 当自动释放池被销毁时,会对池子里面的所有对象做一次release操作
3、 会返回对象本身
4、 调用完autorelease方法后,对象的计数器不变
autorelease的好处
1、不用再关心对象释放的时间
2、不用再关心什么时候调用release
autorelease的使用注意
1、占用内存较大的对象不要随便使用autorelease
2、占用内存较小的对象使用autorelease,没有太大影响
错误写法
1、alloc之后调用了autorelease,又调用release
2、连续调用多次autorelease
自动释放池
1、在iOS程序运行过程中,会创建无数个池子。这些池子都是以栈结构存在(先进后出)
2、当一个对象调用autorelease方法时,会将这个对象放到栈顶的释放池
自动释放池的创建方式
1、iOS 5.0前
2、iOS 5.0 开始
注意点
1、系统自带的方法里面没有包含alloc、new、copy,说明返回的对象都是autorelease的
2、开发中经常会提供一些类方法,快速创建一个已经autorelease过的对象,创建对象时不要直接用类名,一般用self
之后的版本中,打开你的工程,点击目录的工程文件,最顶端蓝色的,然后选择project下你的工程,还是蓝色那项,然后build Settings,然后往下拉,在Apple LLVM 5.0 - Language - Objective C 里有一个选项,Objective-C Automatic Reference Counting 选择NO,就可以了。如图所示:
——Java培训、Android培训、iOS培训、.Net培训、期待与您交流! ——-
基本原理
什么是内存管理1、移动设备的内存极其有限,每个app所能占用的内存是有限制的。
2、当app所占用的内存较多时,系统会发出内存警告,这时得回收一些不需要再使用的内存空间。比如回收一些不需要使用的对象、变量等。
3、管理范围:任何继承了NSObject的对象,对其他基本数据类型(int、char、float、double、struct、enum等)无效。
对象的基本结构
1、每个OC对象都有自己的引用计数器,是一个整数,表示“对象被引用的次数”,即有多少人正在使用这个OC对象。
2、每个OC对象内部专门有4个字节的存储空间来存储引用计数器。
引用计数器的作用及操作
1、当使用alloc、new或者copy创建一个新对象时,新对象的引用计数器默认就是1。
2、当一个对象的引用计数器值为0时,对象占用的内存就会被系统回收。换句话说,如果对象的计数器不为0,那么在整个程序运行过程,它占用的内存就不可能被回收,除非整个程序已经退出。
操作引用计数器的方法基本使用:
1、retain :计数器+1,会返回对象本身
2、release :计数器-1,没有返回值
3、retainCount :获取当前的计数器
对象销毁
1、当一个对象的引用计数器值为0时,那么它将被销毁,其占用的内存被系统回收。
2、当一个对象被销毁时,系统会自动向对象发送一条dealloc消息。
3、一般会重写dealloc方法,在这里释放相关资源,dealloc就像对象的遗言。如下所示:
- (void)dealloc { // super的dealloc一定要调用,而且放在最后面 [super dealloc]; }
当对象被回收后,会出现几个概念:
1、僵尸对象 :所占用内存已经被回收的对象,僵尸对象不能再使用。
2、野指针 :指向僵尸对象(不可用内存)的指针,给野指针发送消息会报错。所报的错误为(EXC_BAD_ACCESS),这个是常见的错误,只要我们看到这个错误信息就知道是野指针问题。
*EXC_BAD_ACCESS : 访问了一块坏的内存(已经被回收、已经不可用的内存。
3、空指针 :没有指向任何东西的指针(存储的东西是nil、NULL、0),给空指针发送消息不会报错。
内存管理规则
在一个程序中,我们会创建多个对象,对象之间也互有联系,但是我们只要按照计数器+1必-1,使计数器数值恒定即可。1、你想使用(占用)某个对象,就应该让对象的计数器+1(让对象做一次retain操作)。
2、你不想再使用(占用)某个对象,就应该让对象的计数器-1(让对象做一次release)。
3、谁retain,谁release。
4、谁alloc,谁release。
如下程序创建了人和书,人拥有书时一系列的计数器操作:
main函数
int main() { Book *b = [[Book alloc] init]; Person *p1 = [[Person alloc] init]; [p1 setBook:b]; [p1 release]; p1 = nil; [b release]; b = nil; return 0; }
Book.h
#import <Foundation/Foundation.h> @interface Book : NSObject { int _price; } - (void)setPrice:(int)price; - (int)price; @end
Book.m
#import "Book.h" @implementation Book - (void)setPrice:(int)price { _price = price; } - (int)price { return _price; } - (void)dealloc { NSLog(@"Book对象被回收"); [super dealloc]; } @end
Person.h
#import <Foundation/Foundation.h> #import "Book.h" @interface Person : NSObject { Book *_book; } - (void)setBook:(Book *)book; - (Book *)book; @end
Person.m
#import "Person.h" @implementation Person - (void)setBook:(Book *)book { _book = [book retain]; } - (Book *)book { return _book; } - (void)dealloc { [_book release]; NSLog(@"Person对象被回收"); [super dealloc]; } @end
set方法内存管理
当采用上述代码的set方法设置时,如果我们从书1换成书2,就会出现内存泄露的情况,因为我们的书1并没有release,没有回收对象。这时我们就需要对set方法重写,需要判断是否为新对象、并对就对象进行release、对新对象进行retain:- (void)setCar:(Car *)car { // 1.先判断是不是新传进来对象 if ( car != _car ) { // 2.对旧对象做一次release [_car release]; // 3.对新对象做一次retain _car = [car retain]; } }
这是对于OC对象,如果是基本数据类型,setter方法不需要进行修改:
- (void)setAge:(int)age { _age = age; }
同时在代码中,只要是调用了alloc,也就是分配存储空间,在代码的下方必须要有release(autorelease),但是如果对象不是通过alloc产生的,比如说
NSString *str = @"Tang";,此时就不需要release。
此时在- (void)dealloc方法中,对self(当前)所拥有的其他对象做一次release,每一个对象对应一个release,如下:
- (void)dealloc { [_age release]; [super dealloc]; }
@property参数
set方法的内存管理虽然很好的控制了内存的回收,但是却也造成了代码重复、繁琐等缺点,这时候OC利用@peoperty加参数来控制set方法的内存管理。书写格式:
@property (参数) 类名 *属性名;
参数分为四类:
1、set方法内存管理相关的参数
* retain : release旧值,retain新值(适用于OC对象类型)
* assign : 直接赋值(默认,适用于非OC对象类型)
* copy : release旧值,copy新值
2、是否要生成set方法
* readwrite : 同时生成setter和getter的声明、实现(默认)
* readonly : 只会生成getter的声明、实现
3、多线程管理
* nonatomic : 性能高 (一般就用这个)
* atomic : 性能低(默认)
4、setter和getter方法的名称
* setter : 决定了set方法的名称,一定要有个冒号 :
* getter : 决定了get方法的名称(一般用在BOOL类型)
对于OC对象类型,采用这种方式:
@property ( nonatomic, retain) 类名 *属性名;
对于非OC对象类型,比如说int\float\enum\struct等,采用这种方式:
@property ( nonatomic, assign) 类型名称 属性名;
采用这种方式可以很好的简化代码,将更多的精力投入与功能上。
循环引用
当代码中两个类互相引用时:#import "B.h" @interface A : NSObjcet { B *b; } @end
#import "A.h" @interface B : NSObject { A *a; } @end
这种代码编译会报错。当使用@class在两个类相互声明,就不会出现编译报错。使用
@class 类名;就可以引用一个类,说明一下它是一个类。
和#import的区别:
1、#import方式会包含被引用类的所有信息,包括被引用类的变量和方法;@class方式只是告诉编译器在A.h文件中 B *b 只是类的声明,具体这个类里有什么信息,这里不需要知道,等实现文件中真正要用到时,才会真正去查看B类中信息。
2、如果有上百个头文件都#import了同一个文件,或者这些文件依次被#improt,那么一旦最开始的头文件稍有改动,后面引用到这个文件的所有类都需要重新编译一遍,这样的效率也是可想而知的,而相对来 讲,使用@class方式就不会出现这种问题了。
3、在.m实现文件中,如果需要引用到被引用类的实体变量或者方法时,还需要使用#import方式引入被引用类。
循环retain
p.card = c; c.person = p;
即使我们的retain和release都没有问题,但是在程序运行中任然会有内存泄露,这时我们可以将其中一个retain改为assign就可以解决这个问题:
@property (nonatomic, retain) Card *card;
@property (nonatomic, assign) Person *person;。
autorelease
autorelease的基本用法1、会将对象放到一个自动释放池中
2、 当自动释放池被销毁时,会对池子里面的所有对象做一次release操作
3、 会返回对象本身
4、 调用完autorelease方法后,对象的计数器不变
autorelease的好处
1、不用再关心对象释放的时间
2、不用再关心什么时候调用release
autorelease的使用注意
1、占用内存较大的对象不要随便使用autorelease
2、占用内存较小的对象使用autorelease,没有太大影响
错误写法
1、alloc之后调用了autorelease,又调用release
@autoreleasepool { // 1 Person *p = [[[Person alloc] init] autorelease]; // 0 [p release]; }
2、连续调用多次autorelease
@autoreleasepool { Person *p = [[[[Person alloc] init] autorelease] autorelease]; }
自动释放池
1、在iOS程序运行过程中,会创建无数个池子。这些池子都是以栈结构存在(先进后出)
2、当一个对象调用autorelease方法时,会将这个对象放到栈顶的释放池
自动释放池的创建方式
1、iOS 5.0前
NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init]; [pool release];
2、iOS 5.0 开始
@autoreleasepool { }
注意点
1、系统自带的方法里面没有包含alloc、new、copy,说明返回的对象都是autorelease的
2、开发中经常会提供一些类方法,快速创建一个已经autorelease过的对象,创建对象时不要直接用类名,一般用self
+ (void)person { return [[[self alloc] init] autorelease]; }
附
在Xcode中如何取消ARC,在5.1以前的版本中在创建项目的时候不要勾选ARC即可,如图所示:之后的版本中,打开你的工程,点击目录的工程文件,最顶端蓝色的,然后选择project下你的工程,还是蓝色那项,然后build Settings,然后往下拉,在Apple LLVM 5.0 - Language - Objective C 里有一个选项,Objective-C Automatic Reference Counting 选择NO,就可以了。如图所示:
——Java培训、Android培训、iOS培训、.Net培训、期待与您交流! ——-
相关文章推荐
- 黑马程序员—OC内存管理
- 黑马程序员_OC内存管理
- 黑马程序员------ios培训 oc内存管理(二)
- 黑马程序员——IOS基础(OC内存管理)
- 黑马程序员——零基础学习iOS开发——11 OC内存管理
- 黑马程序员-oc内存管理(Q)
- 黑马程序员—IOS加强视频—oc内存管理
- 黑马程序员——OC内存管理
- 黑马程序员-OC内存管理
- 黑马程序员——IOS基础——OC内存管理
- 黑马程序员-----OC内存管理(一)
- ios黑马程序员--oc内存管理
- 黑马程序员——OC内存管理
- 18、黑马程序员-OC内存管理
- 黑马程序员-----OC内存管理(二)
- 黑马程序员——OC内存管理(一)
- 黑马程序员--ios基础--oc内存管理
- 黑马程序员-----------ios培训 oc内存管理(一)
- 黑马程序员-----OC内存管理(三)
- 黑马程序员——OC学习小结------OC内存管理