OC加强(一)之内存管理
1.为什么要进行内存管理?
由于移动设备的内存极其有限,所以每个APP所占的内存也是有限制的,当app所占用的内存 较多时,系统就会发出内存警告,一个app可用的内存是被限制的,如果一个app使用的内存超 过20M,则系统会向该app发送Memory Warning消息。收到此消息后,需要回收一些不需要再 继续使用的内存空间,比如回收一些不再使用的对象和变量等,否则程序会崩溃。
这里首先要解释一个常识概念:内存,存储空间,各自的功能?
内存:我们一般说的手机内存其实是指运行内存,,简称运存,即 RAM-全称是RamdomAccessMemory:随机存取存储器
RAM 的大小直接决定手机后台能开多少程序,能运行多少软件,RAM越大,就表示可以运行的软件就越多
存储空间:存储空间就是下载的软件或各种文件的容器,即ROM-全称是Read Only Memory:只读存储器
ROM的大小决定了手机能装多少软件存多少文件,歌曲,电影,小说等..
所以我们知道,手机内存是很小的,一个APP运行如果占用过多内存,苹果规定如果单个APP运行内存超过20M,就会报内存警告,所以必须对内存进行管理,否则会引起程序崩溃
软件被强制闪退,关闭
2.OC 内存管理的范围:
OC管理所有继承自NSObject类的对象,不去管理其他基本数据类型的对象,(只针对堆区)
为什么?
本质原因是因为对象和其他数据类型在系统中的存储空间不一样,其它局部变量主要存放于 栈中,而对象存储于堆中,当代码块结束时这个代码块中涉及的所有局部变量会被回收,指 向对象的指针也被回收,此时对象已经没有指针指向,但依然存在于内存中,造成内存泄 露。
通俗讲就是:栈区存的是一些基本类型的局部变量,指针变量也是局部变量,这些变量在程序结束会自动被系统回收,
但堆区是不会被系统管理的,如果我们也不去管理,就像永远不清理的垃圾,越来越多,占着内存空间,就相当于你有100块钱,被偷了50块,也就是内存泄漏的概念了
这里又想到一个小的概念:
为什么定义堆区时说堆区是程序运行过程中动态分配的内存空间?
可以想一下,堆区是存储对象的,而只有创建对象时调用类方法alloc时才会在堆区申请内存空间,所以显而易见堆区就是只有当实例化对象时才会被分配内存空间
显而易见:定义油然而生
3.内存管理的原理和分类:
3.1引用计数器
引用计数器是一个整数,是用来计算对象被引用的次数的
对象创建时向计算机申请了一块内存空间,并在这一块内存空间中分出了一个8个字节的空间用来存放引用计数器
3.2对引用计数器的操作
给对象发送消息,进行相应的计数器操作。
retain消息:使计数器+1,该方法返回对象本身
release消息:使计数器-1(并不代表释放对象)
retainCount消息:获得对象当前的引用计数器值%lu %tu
作用就是给对象发消息(每天干的活)
又想到一个小概念:
我们玩苹果手机时通常会按home键,让程序”看似退出”,但实际上程序只是放在后台运行了,并没有退出,怎么看到后台程序呢?双击home键即可看到
所以程序退出(双击home键,按住应用程序往上滑),但有个问题微信,qq,我让程序退出后还是能够接受消息,这是不是说明程序在后台是运行着呢?并
没有退出,但我明明已经双击home把程序滑退了啊,这个怎么解释呢?
其实,程序确实是退出了,这些消息其实就是推送消息,而推送消息是由苹果的服务器推送给你的,并不是APP推送的,(这项技术成为手机推送开发技术)
应该是涉及服务器和数据库的,这也是学习iOS的弊端,好多都不能深入了解,要想清楚,还是要往全栈上发展和努力.
Objective-C提供了三种内存管理方式:
MannulReference Counting(MRC,手动管理,在开发 iOS4.1之前的版本的项目时我们要
自己负责使用引用计数来管理内存,比如要手动 retain、release、autorelease 等,而在其后
的版本可以使用 ARC,让系统自己管理内存。)
automatic reference counting(ARC,自动引用计数,iOS4.1 之后推出的)
garbage collection(垃圾回收)。iOS不支持垃圾回收;
ARC作为苹果新提供的技术,苹果推荐开发者使用ARC技术来管理内存;
开发中如何使用:需要理解MRC,但实际使用时尽量用ARC
当p被释放时,如果不让car释放,就会造成内存泄漏,所以必须先让[_car release];
有人会有疑问:_car 和car是同一个吗?
答案:是同一个,为什么呢,因为下图所示,set方法会给_car赋值为car,这是必然的,_car相当于全局变量,不赋值是没有任何意义的,
赋值了才能代表一个对象car,才能执行release操作,属性其实是不会执行也不能执行release操作的,除非这种情况下:给属性赋值为对象(刺激吧!!!)
注意
1)永远不要直接通过对象调用dealloc方法(实际上调用并不会出错) 一旦对象被回收了, 它占用的内存就不再可用, 坚持使用会导致程序崩溃(野指针错误)为 了防止调用出错,可以将“野指针”指向nil(0)。 这句话也就直接说明了,调用delloc方法就是回收了对象的内存空间,释放了内存.
一定要[super dealloc],而且要放到最后,意义是:你所创建的每个类都是从父类, 根类继承来的,有很多实例变量也会继承过来,这部分变量有时候会在你的程序内使用,它 们不会自动释放内存,你需要调用父类的 dealloc方法来释放,然而在此之前你需要先把自 己所写类中的变量内存先释放掉,否则就会造成你本类中的内存积压,造成泄漏
-(void)dealloc
{
[_car release];
[super dealloc];
}
1)空指针:没有指向任何东西的指针,给空指针发送消息不会报错
2)nil和Nil及NULL、NSNull的区别:
nil:是一个对象值;如果我们要把一个对象设置为空的时候就用nil;
A null pointer to an Objective-C object. ( #define nil ((id)0) )
Nil:是一个类对象的值,如果我们要把一个Class类型的对象设置为空的时候就用Nil ;
A null pointer to an Objective-C class.
NULL 是一个通用指针;
A null pointer to anything else. ( #define NULL ((void *)0) )
NSNull 是一个对象,它用在不能使用nil的场合;A class defines a singleton object used to represent null values in collection objects (which don\’t allow nil values).
[NSNull null]: The singleton instance of NSNull.
[NSNull null]是一个对象,他用在不能使用nil的场合。
@property的修饰关键字
1> 控制set方法的内存管理
* assign: 直接赋值, 不做任何内存管理(默认, 用于非OC对象类型)
* retain: release旧值,retain新值(用于OC对象),要配合nonatomic使用
* copy: release旧值, copy新值(一般用于NSString *)
2> 控制是否需要生成set方法
* readwrite: 同时生成set方法和get方法(默认)
* readonly: 只会生成get方法
用于 ‘基本数据类型’、‘枚举’、‘结构体’ 等非OC对象类型
eg:int、bool等
后续见下一波……………
1)基本数据类型:直接赋值
int float double long struct enum -(void)setAge:(int)age { _age=age; }
2)OC对象类型
-(void)setCar:(Car*)car{ //判断_car 存放的是否是 形参对象,如果不是,则执行 [_car realease]; if (_car!=car) { [_car release];//先释放上一个对象,(注意第一次是向nil发送release消息) _car = [car retain]; } }
8.@class的使用以及和#import的区别:
作用
可以简单地引用一个类
简单使用
@class Dog; //类的引入
仅仅是告诉编译器: Dog是一个类; 并不会包含Dog这个类的所有内容
具体使用
在.h文件中使用@class引用一个类 在.m文件中使用#import包含这个类的.h文件
如下面代码:
如下面代码: A.h文件
#import "B.h" @interface A : NSObject { B *b; } @end
这两种的方式的区别在于:
1)#import方式会包含被引用类的所有信息,包括被引用类的变量和方法;@class方式只是告诉编译器在 A.h文件中 B *b 只是类的声明,具体这个类里有什么信息,这里不需要知道,等实现文件中真正要用到 时,才会真正去查看B类中信息;
2)使用@class方式由于只需要只要被引用类(B类)的名称就可以了,而在实现类由于要用到被引用类中 的实体变量和方法,所以需要使用#import来包含被引用类的头文件;
3)通过上面2点也很容易知道在编译效率上,如果有上百个头文件都#import了同一 个文件,或者这些文 件依次被#improt(A->B, B->C,C->D…),一旦最开始的头文件稍有改动,后面引用到这个文件的所有类 都需要重新编译一遍,这样的效率也是可想而知的,而相对来 讲,使用@class方式就不会出现这种问题 了;
所以:我们实际开发中尽量在.h头文件中使用@class