iOS之属性定义和内存管理

当把属性的语义特性分别声明为assign,retain,copy时, 自动生成setter, getter方法内部实现

假定有一个车类Car.h中定义三个属性
@property (nonatomic, retain) NSString * brand; //品牌
@property (nonatomic, copy) NSString * colour;  //颜色
@property (nonatomic, assign) CGFloat  price;   //价格

以下是把属性的语义特性分别声明为assign,retain,copy时, 自动生成setter, getter方法内部实现:

    //当把属性的语义特性声明为retain,自动生成setter方法内部实现

    - (void)setBrand:(NSString *)brand

    {

        if (_brand != brand) {//如果旧值不等于新值

            [_brand release];//旧值先release

            _brand = [brand retain];//新值retain赋值给旧值

        }

    }

    

    //当把属性的语义特性声明为retain,自动生成getter方法内部实现

    - (NSString *)brand

    {

        return [[_brand retain] autorelease];

    }

    

    //当把属性的语义特性声明为copy,自动生成setter方法内部实现

    - (void)setColor:(NSString *)color

    {

        if (_color != color) {

            [_color release];

            _color = [color copy];

        }

    }

    

    //当把属性的语义特性声明为copy,自动生成getter方法内部实现

    - (NSString *)color

    {

        return [[_color retain] autorelease];

    }

    

    //当把属性的语义特性声明为assign,自动生成setter方法内部实现

    - (void)setPrice:(CGFloat)price

    {

        _price = price;

    }

    

    //当把属性的语义特性声明为assign,自动生成getter方法内部实现

    - (CGFloat)price

    {

        return _price;

    }


对于Retain和Assign属性的理解

比如我要定义一下字符串var:

  1. NSString *var;

声明Property时,

  1. @property(nonamtic,assign)NSString *var;

将属性声明为Assign,当使用@Synthsize生成Getter和Setter方法后,我们就可以用“.”操作符来为var赋值,

将属性声明为Assign时,Setter方法的实现是这样的:

  1. -(void)setter: (NSString*)str
  2. {
  3.    var=str;
  4. }
  5. var=[[NSString alloc]initWithString: @"aaa"];


当我们这样用时,没有调用Setter方法,只是将var指向目标地址,这时

NSLog(@“%d”,[var retainCount]);的值会是1。

如果我们这样写:self.var=[[NSString alloc]initWithString:"aaa"];

这时会调用setter方法,但是NSLog(@“%d”,[var retainCount]);的值仍然是1。

如果我们在设置属性时这样来设置:

  1. @property(nonamtic,retain)NSString *var;

这时,自动生成的Setter方法是这样的:

  1. -(void)Setter:(NSString*)str

  2.   [str retain];
  3.   [var release];
  4.   var=str;

这样就一目了然了,也就是被设置为retain属性的方法,在生成Setter方法时,先将要赋的值的引用计数加1,然后将var指向的对象release。再后才是将str赋值给var。这时下面的代码的输出结果会是2,因为,在Setter中[[NSString alloc]initWithString:"aaa"]所指的内存区域的retain被加了1,然后var又指向这个区域,所以var的retainCount自然就为2了:
  1. self.var=[[NSString alloc]initWithString: @"aaa"];
  2. NSLog(@“%d”,[var retainCount]);


这样做是为了防止内存被过渡释放,比如,[[NSString alloc]initWithString:"aaa"]这个内存区域不光有var指向,还有另外一个变量var2也指向这个区域,如果我们不设置成retain属性,在执行var=[[NSString alloc]initWithString:"aaa"];后,var和var1都指向一个共同的内存区域,但这个内存区域的retainCount为1。如果在另一个地方执行了[var2 release];这样[[NSString alloc]initWithString:"aaa"]所指的内存区域的retainCount为0,所以这个内存区域就被释放了,var也就成了野指针,这时再引用var将会出现内存错误。


但是,需要特别注意的是,即使我们设置var的属性为retain,如果我们在为var赋值时,使用的是如下形式:

  1. var=[[NSString alloc]initWithString: @"aaa"];

而不是:

  1. self.var=[[NSString alloc]initWithString: @"aaa"];


这样retain属性是不起作用的,前面不加self.,相当于我们定义的属性还是assign。retainCount不会加1的。

我个人对这个的理解是,如果不显示的使用"."操作符,Setter方法是不调用的,仅仅是指针的传递。

今天又有了点新的想法,补充一下:


"."操作符在OBJC中是方法的调用,比如:self.str和[self str]是一样的。如果我在.h文件中声明了一个方法:

  1. -(void)method;

那么,我调用这个方法可以用两种方式:[self method]或self.method。

这样说的话,我们为什么可以用诸如self.str这样的形式来表示一个变量呢,原因就在于OBJC中变量属性的机制。

我们前面说过,定义一个变量str,加个assign或retain之类的属性后,再用@synthesize就可以生成相应的setter和getter方法了。这样,对于一个变量,就有了相应的赋值方法,于是,对于self.str这样的写法,实际上就是调用了str对应的setter或getter方法。换句话说,也是把setter或getter消息发送给和str。str这时就是一个方法名,而不仅仅是变量名了。


所以如果我们没有对一个变量声明属性,也没有@synthesize来生成setter和getter方法,那么我们就不能用self.str这种形式,而只能用str=@"aaa",或者str1=str这样的形式来使用变量。于是也就有了我们前面的结论:用self.str这种形式,相当于调用setter或getter方法。也就会执行retain的操作。不用这种形式,就没用调用setter或getter方法,retain的操作也就无从谈起。



如果你用self._myObject = aMyObject; 或者 myObject = aMyObject; 你会得到一个错误, 为什么呢, 这里就是和Obj-c的存取方法有关了. 说白了很简单 , 大家都知道, @property (nonatomic, retain) MyObject *myObject; 是为一个属性设置存取方法, 只是平时我们用的方法名和属性名是一样的,现在你把它写成不同的名字, 就会很清楚了. _myObject是属性本身, myObject是存取方法名.


顺便说一下, 如果你想在其它类访问对象属性, 而不是通过存取方法, 你可以用myClass -> myObject来访问, 这样是直接访问对象本身, 不过你先要把myObject设成@public. 但这个是官方不提倡的


你可能感兴趣的:(iOS之属性定义和内存管理)