重点知识(内存管理)
管理范围:任何继承了NSObject的对象。
预备知识内存分配 :栈区,堆区,全局区,文字常量区
一个由C编译的程序占用的内存分为以下几个部分
1、栈区(stack) 由编译器自动分配释放,存放函数的参数值,局部变量的值等
。其操作方式类似于数据结构中的栈。
2、堆区(heap) 一般由程序员分配释放若程序员不释放,程序结束时可能由系统
回收。
注意它与数据结构中的堆是两回事,分配方式倒是类似于链表。
3、全局区(静态区)(static),全局变量和静态变量的存储是放在一块的,初
始化的全局变量和静态变量在一块区域,未初始化的全局变量和未初始化的静态变量
在相邻的另一块区域。程序结束后由系统释放。
4、文字常量区 常量字符串就是放在这里的。程序结束后由系统释放
5、程序代码区 存放函数体的二进制代码。
栈:放一些局部变量。指针式局部变量放在栈里。栈里面的东西自动回收。
堆:放动态产生的变量。 对象的内存放在堆里面。堆里面的东西不能自动回收。
堆里面的东西是程序员手动释放的。
oc里尽管没有任何指针指向对象了,但是只要他在堆空间里,就不会回收。
怎样回收:
只有给对象发送一条消息,他就回收了。
什么是发送消息:
就是调用方法,调用对象的某个方法,才能销毁。
怎么判断需不需要回收:
每个oc对象都有自己的引用计数器,是一个整数,占用4个字节的存储空间。表示对
象引用的次数,当引用引用计数器为0时,把对象回收。
引用计数器的作用:
1,当使用alloc,new,copy创建对象时,新对象的引用计数器默认值是1.
2,当一个对象的引用计数器值为0时,对象占用的内存就会被系统收回。
3,如果对象的引用计数器值不为0,那么在整个程序运行过程,他占用的内存就不可
能被收回,除非整个程序已经退出。
引用计数器的操作:
1,给对象发送一条retain消息,可以使计数器值+1.
2,给对象发送一条release消息,可以使引用计数器值-1.
3,可以给对象发送retainCount消息获得当前的引用计数器值.
对象被回收的两种情况:
1,程序员通过代码在运行过程中就把对象干掉。
2,当程序员退出时,整个程序占用存储空间会自动回收。
怎么验证对象有没有人用:
1,重写对象的dealloc方法,在dealloc括号里打印,有打印说明dealloc有调用,
对象被回收。
调用的dealloc方法,一定要调用super的方法[super dealloc];一定要放到最后面
。当一个对象被回收是,系统会自动调用[super dealloc]方法。计数器值为0时,
系统会自动调用[super dealloc]方法。
对象的销毁:
1,当一个对象的引进计数器值为0时,那么他将销毁。其占用的内存被系统回收。
2,当一个对象被销毁时,系统会自动向对象发送一条dealloc消息。
3,一个般会重写dealloc的方法,在这里释放相关资源,dealloc就像对象的遗言。
4,一旦重写了dealloc方法,就必须用[super dealloc],并放在最后调用。
5,不要直接调用dealloc方法。
6,一旦对象被回收了,他占用的内存就不可用,坚持使用会导致崩溃.(野指针错误
)
野指针:指向了僵尸对象(不可用内存)的指针。
僵尸对象:所占用内存已经被回收的对象。
空指针:没有指向任何对象的指针。
oc里面给空指针发送消息,不报错。
给野指针发送消息,报错。在程序运行过程中报错,直接闪退。
什么是野指针错误:
指向了僵尸对象,或者不空用内存的指针。
EXC BAD ACCEESS -> message sent to dealloc cated instance.访问了一块不
能访问的内存空间。(野指针错误)。
Xcod 打开监测僵尸对象程序: Edit streme ->Diagnastics ->oc ->Enable
Zombieobject.
retain是有返回值的,返回值是(id),任何的oc对象。
注意:retain方法返回的是对象本身(他自己)。谁调用retain就返回谁。
set方法的内存管理:利用@property参数
1,@property(retain)Book*book
return : 生成set方法,release旧值,retain新值
2.@ property(readwrite) 可读可写。
@property(readonly)只读
修改getter和setter方法的名称:
@property(getter = abc)int weight; 修改getter成员变量的名称。
@property(getter = abc ,setter = setAbc:)int weight; set方法后一定要有
冒号。
setter方法:决定了方法名后一定要有冒号。
getter方法:决定了方法的名称。
getter方法一般用在BOOL类型。
提高性能(多线程管理):
1,nonatomic 性能高
2,atomic 性能低(默认)
内存管理的相关参数:
1,assign:直接赋值(默认适用于非oc对象类型)
2,copy:release旧值,copy新值。
内存的管理代码规范:
只要调用alloc,就必须有release或autorelease。如果对象不是alloc产生的,
就不用release。
set方法的代码规范:
1,基本数据类型:直接赋值
2,OC对象类型:
先判断是否是新对象,使用新对象,对新对象加一次retain,对旧对象减一次
release.
- (void)setCar:Car*car
{
if (_car != car)
{
[_car release]
_car = [car retain];
}
}
dealloc方法代码规范:
1,一定要调用[super dealloc],一定要放到最后面。
2,当前对象(self),所拥有对象做一次release.
多对象内存管理
当你想使用某个对象的时候,你应该对他的计数器加1,不想用了减1。(有加有
减)谁retain,谁release。谁alloc,谁release。
补充:访问成员变量的方法有:
直接调用
1,_成员变量;
2,self->成员变量;
间接调用:通过get方法
3,self.成员变量;
4,[self 成员变量]
循环引用:
URL:代表资源路径
@class 声明一个类,仅仅告诉编译器某个名称是一个类。在.h文件中用@class来声
明。
在开发中引用一个类的规范
1,在.h文件中用@class来声明类
2,在.m文件中用#import来包含类的所有东西。
例如:Car这个类被其他100个类引用着,引用这个类直接用#import
坏处;当我修改了Car其中的一点东西,其他的类就要重新拷贝,大大降低我们的编
译效率。
@class的好处:
1,解决循环引用的问题
2,提高了性能
所以为了提高编译效率,一般在头文件不用#import 特殊的是父类,因为继
承:NSObject
面试题:@class 和 @import 的区别
1,#import方式包括被引用类的所有信息,包括被引用类的变量和方法。
@class方式知识告诉编译器在.h文件中,B*b知识累的声明,具体这个类是什么意思
,这里不需要知道,等实现文件中真正要用到时,才会真正查看B类中信息。
2,如果有上百个头文件都#import了同一个文件,或者一次被#import;那么一旦最
开始的头文件稍有改动,后面引用到这个文件的所有类都需要重新编译一遍。这样的
效率也是可想而知的,而相对来讲,使用@class方式就不会出现这种问题了。
3,在.m 实现文件中,如果需要引用到被引用类的实体变量或者方法时,还需要使用
#import方式引入被引用类。
面试题:循环引用问题怎么解决
当真出现两端循环引用 解决办法是:一端用retain ,另一端用 assign.
autorelease 半自动释放
其实autorelease并不是自动释放,他只是延迟了对象被释放的时间,等释放池被销
毁,才能做release.
基本用法:
1,autorelease 会将对象放到一个自动释放池中
2,当自动释放池(autoreleasepool{ })被释放时,会对池子里的所有对象做一个
release操作。(仅仅是一次release,池子被销毁,对象不一定会销毁)
3,autorelease会返回对象本身
4,调用完autorelease方法后,对象计数器不变 ,怎么理解这句,就是autorelease不会是对象-1,
只会把对象放到池里,是池释放时对里面所有的对象做一次-1.所以说记数器不变。
自动释放池
1,在iOS程序运行过程中,会创建无数个池子,这些池子都是以栈结构存在(先进后
出)。
2,当一个对象调用autorelease方法时,会将这个对象放到栈顶的释放池。
创建释放池有两种方式:
1,@autorelease{
这是ios5.0后的方式
}
2,5.0之前这样创建
NSAutoreleasepool=[[NSAutoReleasePool alloc] init];
[pool release];
以前销毁释放池还有一个方法叫 drain (榨干的意思)
[pool drain]一般用在mac上,ios用release.
自动释放池其实是以栈的形式存在的,栈是以数据结构存放自动释放池的。
栈的特点:先进后出。创建第一个释放池会放在栈的最底部。
autorease好处:
不用关心对象释放的时刻,不用关心什么时候调用release.
autorease 缺点:
不能精确控制对象的释放时间,在开发中药做一个精确控制。
autorelease:一般只适用于占用内存比较小的对象。
什么叫占用内存比较小的对象:
就是说你对象里面的数据类型,基本上都是int float double 这些东西。如果
你的对象里面包含了很多复杂的东西,比如说@property Car 车啊,一大堆东西,
person对象就很占用内存,因此一个person对象占用了很多对象。像这种比较大型
的对象,不要随便使用autorelease。
面试题:
@autorelease pool{
Person *p = [[Person alloc]init autorelease autorelease]
池子一销毁对你的池子做了两次release.
野指针错误。
}
autorelease
1,系统自带中,如果不含alloc,new,copy,那么这些方法返回的对象都是已经
autorelease 过的了。
[NSString stringWithFomart....]
[NSDate date] 这些不需要
创建字符串对象的方式:
1,NSString *str = @"123456"
2,NSString *str = [NSString stringWithFormat:@“age is %d”,10];
换串 NSNumber *number = [[NSNumber alloc]initWithInt:10];
2,开发中经常写一些类方法快速创建一个autorelease的对象,创建对象的时候不
要使用类名,要用self.
比如:
+ (id)Person
{
return [[self alloc] init autorelease];
}
+ (id)PersonWithAge:(int)age;
{
Person *p = [self Peron];
p.age = 10;
return p;
}
- (id)dealloc
{
[super dealloc]
}
ARC机制(自动生成释放内存的代码)
oc中arc是编译器特性。
什么是编译器特性:
在编译代码的时候,编译器会自动检测,那里需要插入释放内存的代码。
ARC的判断准则:
只要没有强指针指向对象,就会释放对象。
强指针:默认情况下所有的指针都是强指针 __strong. 两个下划线组成
弱指针:决定不了对象要不要释放 __weak.
ARC好处:
只要弱指针指向对象不存在,它会自动把弱指针清空。(不清空会有野指针错误
)。
弱指针使用场合:
循环引用,在ARC情况下,当两端循环引用的时候,一端用weak,另一端用strong。
在非ARC情况下,循环引用,一端用retain,另一端用assign。
ARC特点:
1,不允许调用 release ,retain
2,允许重写dealloc,但不允许调用[super dealloc]
3,@property的参数中retain改写成strong
Xcode 的神奇功能:
能把非 ARC的东西统一改成ARC。
Edit->refactor->convert to objective-c ->ARC
重构 ARC
在有些第三方框架中不支持ARC的
怎么看项目是不是ARC的:
项目->build settings用放大镜 搜索auto->找编译器看object-c ARC Yes。
说明项目是ARC。
想让项目既存在ARC,有存在非ARC怎么做:
项目->build phases->comeples sourcesc4liens->选文件名.m回车 弹出方框->在
里面打上 -fno-objc->arc 敲回车。
想要项目默认不是ARC,我们想让其中某个文件是ARC,我们的做法前几个步骤相同,就
是在弹出的方框中打上 -f-objc-arc