-----------android培训、java培训、iOS培训、.Net培训、期待与您交流!------------
不同类型的变量的生命周期是不同的,而这不同是取决于变量在内存中存放的位置。值类型,比如int,double是存放在栈中的,系统会自动管理,而引用类型,比如对象会被分配在堆中,需要程序负责申请及回收内存。在OC中是利用指针来操纵对象的,指针与内存是息息相关的。不合理的操作将会导致:
僵尸对象:所占用的内存已经被回收的对象,僵尸对象不能再被使用
野指针:是指向被释放的或者访问受限的垃圾内存的指针
空指针:在OC中就是一个指针变量指向nil
关于移动设备的内存管理是指某个App占用的内存较多时,需要回收一些不再被使用的对象,它的管理范围是任何继承自NSObjct的对象,不包括基本数据类型(int,char,struct等)。
每个OC对象都拥有一个整数类型的引用计数器,用来表示有多少个人正在使用这个OC对象,而每个OC对象内部专门使用4个字节的存储空间来存储引用计数器。
每次使用alloc,new,或者copy创建一个新对象时,引用计数器默认为1,当一个对象的引用计数器为0时,对象占用的内存就会被回收,反之,在整个程序的运行过程中,所占用的内存就不能被回收。
引用计数器的主要操作有retain方法,可以使引用计数值 + 1,同时返回对象本身,release方法可以使引用计数器值-1,通过retainCount方法可以知道当前对象的引用计数器值,当对象的引用计数器为0时,系统会自动调用该对象的dealloc消息,dealloc方法可以被重写,但不要直接调用。
为了让开发者专注于感兴趣的代码和对象关系,所以提供了ARC。ARC的全称是Automatic Reference Counting,即自动引用计数,它是一个编译器特性,提供了对OC对象自动管理内存。一旦开启了ARC的功能,就不能向对象发送release,retain这种操作内存的相关代码了。
1.ARC的开启与关闭
2.ARC关闭情况下的内存管理
- 谁创建对象,谁负责release:你通过alloc,new或者[mutable]copy创建了对象,就由你负责调用release或[autorelease]
- 谁retain,谁release:你用retain,让对象的引用计数器+1,就由你负责release
//
// main.m
#import <Foundation/Foundation.h>
#import "Phone.h"
#import "Person.h"
int main(int argc, const char * argv[])
{
Person *p1 = [Person new];
NSUInteger c = [p1 retainCount];
NSLog(@"生成对象时的引用计数器值 = %ld",c);
c = [[p1 retain] retainCount];
NSLog(@"发送retain后对象的引用计数器值 = %ld",c);
[p1 release];
c = [p1 retainCount];
NSLog(@"发送release后对象的引用计数器值 = %ld",c);
[p1 release];
NSLog(@"断点一");
// 初始化Person与Phone对象
Person *p2 = [[Person alloc] init];
Phone *iphone = [[Phone alloc] init];
Phone *samsung = [[Phone alloc] init];
// p2拥有了一部iphone;
p2.phone = iphone;
NSLog(@"iphone对象的引用计数器为:%ld,samsung对象的引用计数器为:%ld",[iphone retainCount],[samsung retainCount]);
// p2换成了三星手机
p2.phone = samsung;
NSLog(@"iphone对象的引用计数器为:%ld,samsung对象的引用计数器为:%ld",[iphone retainCount],[samsung retainCount]);
NSLog(@"断点二");
[samsung release];
[iphone release];
[p2 release];
// autorelease的作用
@autoreleasepool {
Person *p3 = [[[Person alloc] init] autorelease];
p3.age = 23;
}
NSLog(@"断点三");
return 0;
}
// 手机类
// Phone.h
#import <Foundation/Foundation.h>
@interface Phone : NSObject
// 打电话给某人
- (void) callSomeone;
// 上网
- (void) sulfTheInternet;
@end
//
// Phone.m
#import "Phone.h"
@implementation Phone
// 打电话给某人
- (void) callSomeone
{
NSLog(@"打电话");
}
// 上网
- (void) sulfTheInternet
{
NSLog(@"上网");
}
// 重写dealloc方法
- (void)dealloc
{
NSLog(@"Phone对象被回收");
[super dealloc];
}
@end
// 人类
// Person.h
#import <Foundation/Foundation.h>
@class Phone;
@interface Person : NSObject
{
Phone *_phone; // 人拥有一部手机
int _age; // 人的年龄
}
// 成员变量的setter && getter方法
- (void) setPhone:(Phone *)phone;
- (Phone *)phone;
- (void) setAge:(int)age;
- (int)age;
@end
//
// Person.m
#import "Person.h"
@implementation Person
// age的setter && getter方法
- (void) setAge:(int)age
{
_age = age; // < 1 >
}
- (int)age
{
return _age;
}
// Phone的setter && getter方法
- (void) setPhone:(Phone *)phone
{
// _phone = phone; < 2 >
// _phone = [phone retain]; < 3 >
if (phone != _phone)
{
// 将旧手机丢掉
[_phone release];
// 换上新手机
_phone = [phone retain];
}
}
- (Phone *)phone
{
return _phone;
}
// 重写dealloc
- (void) dealloc
{
[_phone release]; // < 4 >
NSLog(@"Person对象被回收");
[super dealloc];
}
@end
第一个断点的运行结果:
可以得出在生成OC对象时,引用计数器确实为1,retain能将引用计数器+1,release能将引用计数器-1,当引用计数器为0时,会调用类的dealloc方法回收对象
第二个断点的运行结果:
分析:
Phone *iphone = [[Phone alloc] init];这个对象被iphone指针所指向,所以引用计数器为1,后面又被p2对象的成员变量_iphone所指向,所有引用计数器为应该要为2,而samsung指针所指向的对象只有一个引用者,所以引用计数器应该为1,在换手机后,samsung对象的引用计数器就应该为2,而iphone对象的引用计数器应该要减去1,变成1,以上打印是符合结果的。看看Person的setPhone方法做了什么?
在Person.m的setPhone方法中,如果采用< 2 >的方法,没有调用retain方法,那么iphone指针所指向的对象引用计数是不会为2的,如果采用< 3 >的写法,那么在换手机的过程中,iphone指针所指向的对象的引用计数不会减少
第三个断点的运行结果:
分析:在断点2前,phone对象被两个指针所引用,而Person对象被一个p2指针所引用,所以,Phone对象要被release两次,根据,谁retain,创建,就负责release的原则,两次release分别要在Phone对象的dealloc方法及Person对象的dealloc方法。所以< 4 > 处才写上 [ _phone release],对于p3对象,没有显式的调用release方法,但是使用@autorelease代码块中使用autorelease方法,就会再代码块结束后调用一个release,所以效果是一致的。因为内存管理是针对OC对象的,所以< 1 >处的 age变量自然是不需要retain的。
3.开启ARC后的内存管理
ARC的判断标准是只有没有强指针指向对象,就会释放对象,而指针默认情况下是都是强指针,最明显的变化是不需要写autorelease,release,retain这些代码了,而且也不能调用[super dealloc]。
//
// main.m
#import <Foundation/Foundation.h>
#import "Phone.h"
#import "Person.h"
#import "Cat.h"
int main(int argc, const char * argv[])
{
Cat *cat = [[Cat alloc] init];
cat = [[Cat alloc] init];
NSLog(@"断点1");
return 0;
}
//
// Cat.m
#import "Cat.h"
@implementation Cat
// 重写dealloc方法
- (void) dealloc
{
NSLog(@"Cat对象被回收了");
}
@end
运行结果:
分析: main.m中使用了两次alloc代表有两个Cat对象被创建,但是cat指针指指向了其中一个,也就意味着没有被(强)指针指向的那个,根据ARC的判断标准这个对象需要被回收。即使代码中没有看到release。