@在进行讲解属性的内存管理之前,先来看看之前我们使用实例变量时是怎样管理内存的:
我们创建两个相关联的类Person类和Pet类,来看看它们中的赋值和取值方法是怎样管理内存的:
Pet.h
#import <Foundation/Foundation.h>
@interface Pet : NSObject
{
NSString *_name;
}
// 声明赋值 取值方法
- (void)setName:(NSString *)name;
- (NSString *)name;
@end
Pet.m
#import "Pet.h"
@implementation Pet
// 实现赋值 取值方法
- (void)setName:(NSString *)name
{
if (_name != name) {
[_name release];
// 字符串一般用copy
_name = [name copy];
}
}
- (NSString *)name
{
return _name;
}
// 释放实例变量
-(void)dealloc
{
[_name release];
[super dealloc];
}
@end
Person.h
#import <Foundation/Foundation.h>
#import "Pet.h"
@interface Person : NSObject
{
// 声明三个不同类型的实例变量
Pet *_pet;
NSString *_name;
NSInteger _age;
}
// 声明赋值 取值方法
- (void)setPet:(Pet *)pet;
- (Pet *)pet;
- (void)setName:(NSString *)name;
- (NSString *)name;
- (void)setAge:(NSInteger)age;
- (NSInteger)age;
// 测试方法
- (void)test;
@end
Person.m
#import "Person.h"
@implementation Person
// 实现赋值 取值方法
- (void)setPet:(Pet *)pet
{
if (_pet != pet) {
[_pet release];
// 对象类型使用retain获得所有权,然后在dealloc方法中释放
_pet = [pet retain];
}
}
- (Pet *)pet
{
return _pet;
}
- (void)setName:(NSString *)name
{
if (_name != name) {
[_name release];
// 字符串一般使用copy获得所有权,然后在dealloc方法中释放
_name = [name copy];
}
}
- (NSString *)name
{
return _name;
}
- (void)setAge:(NSInteger)age
{
// 基本数据类型不需要管理内存,因为不涉及引用计数的变化
_age = age;
}
- (NSInteger)age
{
return _age;
}
// 实现测试方法
- (void)test
{
NSLog(@"%@:我今年%ld岁,我有只宠物叫%@", _name, _age, [_pet name]);
}
// 释放实例变量
-(void)dealloc
{
[_name release];
[_pet release];
[super dealloc];
}
@end
接下来我们来看看属性是怎么写的:大家知道使用属性系统会自动声明并实现赋值(setter)和取值(getter)方法,不需要我们再来声明和实现赋值和取值方法,并且还自动给我们声明了带下划线的实例变量,给我们开发带来了极大的方便。虽然不需要我们再来操作赋值和取值方法,那他的实现原理我们还是要做到心中明白的。
属性是怎样管理内存的首先属性的语义控制类型:
语义控制:assign、copy、retain。assign一般用于非对象类型和代理(如int, float等),retain(对象类型,如NSDictionary、NSArray)会使属性的引用计数加1,一般对象使用retain。copy如果想得到对象的副本,那么这个时候使用copy。默认是assign。
当使用assign时:就如同上面的对实例变量age的赋值方法
- (void)setAge:(NSInteger)age
{
// 基本数据类型不需要管理内存,因为不涉及引用计数的变化
_age = age;
}
当使用copy时,
就如同上面的对实例变量name的赋值方法
- (void)setName:(NSString *)name
{
if (_name != name) {
[_name release];
// 字符串一般使用copy获得所有权,然后在dealloc方法中释放
_name = [name copy];
}
}
当使用retain时就如同上面对实例变量pet的赋值方法
- (void)setPet:(Pet *)pet
{
if (_pet != pet) {
[_pet release];
// 对象类型使用retain获得所有权,然后在dealloc方法中释放
_pet = [pet retain];
}
}
对于使用实例变量的代码用属性来替换的话,代码量缩短很多,下面替换上面的代码。
Pet.h
#import <Foundation/Foundation.h>
@interface Pet : NSObject
@property (nonatomic, retain) NSString *name;
@end
Pet.m
#import "Pet.h"
@implementation Pet
// 释放属性
-(void)dealloc
{
[_name release];
[super dealloc];
}
@end
Person.h
#import <Foundation/Foundation.h>
#import "Pet.h"
@interface Person : NSObject
@property (nonatomic, retain) Pet *pet;
@property (nonatomic, copy) NSString *name;
@property (nonatomic, assign) NSInteger age;
// 测试方法
- (void)test;
@end
Person.m
#import "Person.h"
@implementation Person
// 实现测试方法
- (void)test
{
NSLog(@"%@:我今年%ld岁,我有只宠物叫%@", _name, _age, [_pet name]);
}
// 释放属性
-(void)dealloc
{
[_name release];
[_pet release];
[super dealloc];
}
@end
注意:如果使用属性,重写set和get方法时,系统会找不到相应的实例变量,这时需要重新声明实例变量,如果重写其中一个则不需要。