前两篇介绍了C与C++的内存管理,这一篇介绍一下Object-C的内存管理。
Object-C是C的超集,所有C语言的特性在Object-C都可以实现。
然而在内存管理上还是存在一些不同的地方。
Object-C即面向对象C语言,其大部分的类型基于Cocoa框架,常见的有NS开头类型。
所以Object-C中大部分类型也都是以类为基础的。
Object-C中类存放于堆而非栈,故一般类对象定义都以指针形式,如:
NSString * str = @"test";
NSString test; //error
按照C的管理规则,这些类型在局部代码块中也属于自动变量的范畴。
Object-C是C的超集,可能由于我看的资料比较少,在目前看过的Object-C资料看来,比较少提到类似C所提到的内存管理关键字,
内存区分类等。不过既然是C的超集,那么不管是关键字还是内存区分类,应该都是大同小异,这一点有哪位大神熟知的麻烦在下面评论区告知我一下。
下面引用前一篇的内存区分类图片来说明Object-C的内存分区:
由于在前一篇中介绍过各区的作用,这里不再赘述。
Object-C中的const存在常量区,extern,static还是存储于静态存储区,C中的五大存储类中的静态变量依然可以在Object-C中延续使用,如:
static CGFloat coordinatorX = 0.6;
extern NSInteger sum = 2;
//函数中
static Bool flag = 1;
static const float = 2.0;
non-Object 的自动变量还是存储于栈,如:
viewDidLoad {
NSInteger num = 1;
CGFloat x = 2;
CGFloat y = 3;
}
而类变量存储于堆中。
Object-C在早期曾使用类似GarbegeCollection的垃圾回收机制来管理内存,不过后来逐渐被MRC取代,现在又被ARC取代。
上面提到Object-C中的对象都是放堆里,也是动态生成,这一点是Object-C与C在内存管理上最大的区别和Object-C 的特点。
而GarbegeCollection,MRC,ARC,便是用来对对象的管理,生成和释放,就如C语言中的malloc()-free()以另一种形式管理。
MRC与ARC
Object-C的内存管理中引入了引用计数retainCount的概念,顾名思义就是有多少个指针指向当前对象,当引用计数为0时,对象被释放。
MRC与ARC都基于Runtime动态实现,引用计数+1、-1,对象释放消息的发送都由Runtime来实现。
MRC(Manual Reference Counting)
MRC翻译成中文就是手动引用计数,在MRC中和C语言一样也有一些关键字,一般用于属性声明。
主要有几个,retain,assign,copy,release。
注意,下面举例代码都是在MRC环境下的!!!
Retain
当用retain 声明属性时,如:
@property (retain) NSString *name;
那么当我们把一个字符串对象赋给name时,字符串的引用计数retainCount 会 +1。
也可以在生成对象时直接用,如:
NSString *temp = [[NSString stringWithString:@"hello world"] retain];
在accessMethor中的实现是这样的:
- (void)name:(NSString *)name {
if(self.name != name) {
[self.name release];
self.name = [name retain];
}
}
Assign
assign与retain相反,用于属性声明时,只是简单的赋值,引用计数不变,如:
@property (assign) NSInteger *age;
且一般也只用于简单数据类型,如NSInteger,NSUInteger等。
在accessMethor中的实现是这样的:
- (void)age:(NSInteger *)age {
self.age = age;
}
需要注意的是,assign所指向对象被释放时assign的对象并不知道,这是如果使用的话会crash,称野指针。
copy
copy顾名思义就是赋值,在进行对象赋值时,复制新的对象进行赋值,原对象不变,如:
@Property (copy) NSString *str;
NSString *temp = @"hello world";
self.str = temp;
在accessMethor中的实现是这样的:
- (void)str:(NSString *)str {
if(self.str != str) {
[self.str release];
self.str = [str copy];
}
}
也可和retain一样在生成对象时使用:
NSString *temp = [[NSString stringWithString:@"hello world"] copy];
这样原来的对象将被自动释放,新对象赋值给temp。
release
release是引用计数减一,将一个对象从一个指针处释放使用release,如上述中的:
[self.str release];
当retainCount为0时对象自动销毁。
MRC是引用计数的手动管理方式,对于程序猿来说比较麻烦,稍不注意会出现野指针和内存泄露等问题。
MRC相比起C的内存管理方式的好处是使用引用计数简化了释放过程,更加安全。
比如在C语言中如果多处引用同一个对象,一旦在某一处释放了之后,其他都变成野指针,这样是比较危险的。
而MRC的好处在于,假如使用妥当,那么对象只有在各处释放之后,引用计数减少到0时才释放,这样子便不容易出现野指针。
然而MRC在操作上还是比较困难,需要程序猿在每时每刻清除知道哪些对象该释放哪些不该释放。苹果在iOS5之后推出了ARC自动应用计数,大大方便了程序猿的工作。
ARC(Auto Reference Counting)
ARC的引入进一步方便了OC中对象的内存管理。其引入了三个新的关键字,strong,weak,unsafe_unretained。
使用这三个新的关键字,我们不需要再去手动管理引用计数,只需在声明变量的时候附上关键字就可以了。
下面介绍这三个关键字:
strong
strong和MRC中的retain作用类似,称强引用。
应用于属性声明:
@property (strong) NSString *name;
在赋值时对象引用计数自动+1,当name指向其他对象时,原来的对象引用计数自动减一,不用手动release。
在临时变量也可以使用,如:
__strong NSString *str;
NSString *str1 = @"hello world";
str = str1;
这时字符串对象引用计数+1。
值得注意的是临时变量默认为strong类型。
weak
weak和MRC中的assign作用相似,称弱引用。
应用于属性声明:
@property (weak) NSString *name;
在赋值时对象引用计数不变,当name指向其他对象时,原来的对象自动释放,不用手动release。
在临时变量也可以使用,如:
__weak NSString *str;
NSString *str1 = @"hello world";
str = str1;
这时字符串对象引用计数不变。
当str1设为nil时,str不会变成野指针,这一点和assign不同,str自动设置为nil,因此比较安全。
weak多用于消除ARC中的循环引用,比如block中的循环引用,或用于delegate。
unsafe_unretained
unsafe_unretained与weak十分相似,都只是赋值,引用计数不变,不过unsafe_unretained顾名思义是不安全,如:
@property (unsafe_unretained) NSString *name;
也可以在局部变量中加以声明,如:
__unsafe_unretained NSString *str;
NSString *str1 = @"hello world";
str = str1;
当str1释放时,str就变成野指针了,这是不安全的。
值得注意的是类属性默认为unsafe_unretained类型。
相比MRC,ARC更加方便,但也存在循环引用导致内存泄露的问题,在使用时需多加留意。
小结:
Object-C是C的超集,内存管理上也存在栈,堆,静态存储区,常量区等,只不过Object-C有object变量和non-object变量之分。non-Object变量和C中的自动变量大致上相同,在局部代码块上属于自动变量,存放于栈中。而object变量便和C有一些不同,都是动态生成的,类似C中的malloc()-free(),存放于堆中。Object-C由此引入gabegeCollection,MRC,ARC等动态基于runtime的管理机制,以引用计数为基础,以retain,weak,copy,unsafe_unretained,strong,assign等关键字来管理引用计数。ARC相对MRC使用方便,不用程序猿手动释放,但也存在循环引用等缺点。
下一篇介绍Eventloop和autoreleasepool以及C,C++,Object-C的对比总结。