IOS开发 @property中assign、copy 、retain等关键字的理解

一、@property基本作用


@property(nonatomic,retain)NSString *str;

@property关键字提供了外界对成员变量的访问接口其本质是为某一个成员变量提供setget操作并不会为你在.m文件中生成可见代码

上面代码声明了一个NSString *类型的对象str,并会自动生成strsetget方法,这跟在javaC#中我们自己定义的set/get方法是一样的,这里object c帮我们省了点代码量,在外部访问的时候先实例化该类,然后 [类实例 str]; 就是获取该变量的值,[类实例setStr:@"newvalue"];就是设置该变量的值,也可以直接类实例.str = @“newvalue”;给成员变量str赋值,类实例.str 直接获取该变量的值。


1、关于私有变量

使用@property声明的成员变量都可以在类外部访问,其本质就是想让外界访问到,那想要私有变量就需要在如下位置声明

//.m文件中
@implementation WWViewController
{
    NSString *str1;
   @protected NSString *str2;
   @private NSString *str3;
   @public NSString *str4;
}

str1:默认为@protected,表示该类和所有的子类中的方法可以直接访问这样的变量。

str2:str1

str3:表示仅自己可以直接访问,子类也不可以。

str4:表示在任何类中都可以通过类实例直接访问,但是需要写在.h文件的相同位置上,调用的时候不能使用类实例.str4,而应该这样类实例->str4来调用,虽然将成员变量声明成为public的可以达到在外部直接访问的目的,但是使用@property关键字将str4声明成一个属性才是正确的,因为@property还有其他好处,后面再说。

 

总结:@property声明的是属性,而类后面加{}声明的是成员变量,属性跟成员变量是一回事,只是 @property将我们的成员变量公开出去让外部类可以调用,不允许外部访问的统统写在{}内作为成员变量供自己内部使用。

 

@property的参数作用


@property 参数分4类

1、读写属性:readwrite/readonly

2setter语意:(assign/retain/copy

3、原子性:atomic/nonatomic

4、引用强弱:(strong/week)

上面介绍过了@property为成员变量提供外部访问的接口,而参数就将这种访问更具体化了,默认会自带3个参数:readwrite、assign、atomic。

1readwrite

     默认值,表示生成set/get方法

2readonly

     只生成get方法

3assign

     默认值,表示直接赋值,先看看会生成怎样的set方法

-(void)setStr:(NSString *)newstr
{
    str = newstr;
}

        习惯了JAVA编程的人可能有疑惑,难道不是这样?实际上却是有很大不同,主要是内存的管理上,JAVA这样写没问题,因为JVM会帮我们管理内存,Object c不行,开辟了内存就要释放内存,否则就内存泄露,Object C管理内存是通过retain/release方法完成的,每个对象被alloc/new创建之后都有一个retaincount属性,其值自动为1。

        如:NSString *strtest = [[NSStringalloc]initWithFormat:@"test"];

        strtest指向了一个字符串对象,而这个字符串对象在被alloc创建之后它的retaincount自动为1,表示有一个引用指向了自己(strtest),当新的引用指向这个对象时

         如:NSString *strtest2 = [strtest retain];

        strtest2 也指向了这个字符串对象,这时必须调用strtest的retain方法,然后返回给新的引用者,字符串对象的retaincount加1, 此时字符串对象的retaincount为2,表示有2个引用指向了自己,当strtest不需要再使用时,调用其release方法,字符串对象的retaincount就会减1,只剩下strtest2在引用,当strtest2不需要在使用时也要调用release方法,字符串的引用再减1,一旦对象的retaincount为0,系统就会自动向对象发送一个dealloc消息,将其占有的资源释放掉,内存被回收。这就是Object C的内存管理机制,直接赋值会导致旧的对象没有被计数减1,新的对象又没有被加1,旧的无法释放,新的没有记录本次引用,会使当前的引用指向一个已经被回收的内存。

       注意:在IOS 5(Xcode 4.2)以后加入了ARC机制,不需要再调用retain/release方法管理内存了,但这并不是说ARC会自动回收内存,它只是自动加入了retain/release的代码,OC的内存管理机制依然是计数机制。assign生成的set方法中依然不会被自动加入retain/release代码。


当成员变量为包装类型时非基本数据类型/对象类型):

        这类数据一般都在【堆】中被创建,引用计数不正确,可能会导致严重的内存问题。如上面使用assign生成的set中 str = newstr,这是赤裸裸的对Object C内存管理机制的挑战,这样做会使str直接指向newstr所指向的对象,既不对str旧对象进行release操作,也不对新对象retain操作。


基础数据类型时:  

        如shortintdoublelong等就不会出现上面的问题,因为他们不在【堆】中,可能在【全局区】也可能在【栈】中,根据他们定义的位置而定,而这些内存都是由系统自动管理的,不同于【堆】需要手动释放。所以基本数据类型可以使用assign来生成set方法直接进行赋值,我想这可能是出于效率的考虑,免去了基本数据类型没有必要的release/retain的过程。


4、retain

        先释放旧的对象,新对象的计数加1,并返回地址给引用者,会生成如下代码

-(void)setStr:(NSString *) newValue
{
    if (str != newValue) {
        [str release];
        str = [newValue retain];
    }
}

        从生成的代码中反映出了assign和retain的区别,assign只简单的赋值,retain先释放旧的对象,新对象的retain count 加1并返回地址给str引用,旧对象没有造成内存泄露,新对象引用计数正确,不会产生内存问题。

         所以基本数据类型使用assign,非基本数据类型使用retain。

   

 5、copy

       在【堆】中复制一个值一样的对象, 旧的值release掉,指向新的复制的对象,与retain不同之处在于copy会在内存中创建一个与旧对象相同的对象,然后release旧的,重新指向这个新的。

    

6、atomic

        默认值,保持数据的原子性访问,类似锁。

7、 nonatomic

        非原子性访问,没加锁,但是提高访问速度,非多线程时访问数据时建议使用。 

8、strong

        强引用,ARC模式下与retain同作用,对象的retaincount自动加1

9、week

        弱引用,ARC模式下与assign同作用,非对象类型使用。

总结:@property 声明的成员变量是为了在外部可以被访问到的,如果只允许内部访问,则在类后{}里面声明,自动生成set/get方法,其参数决定生成的set方法是什么样的,也就决定成员变量的访问方式,是从新指向一个新的对象,还是单纯的赋值,或是指向复制的新的对象,或只读,或线程安全的,非线程安全的,由其是retain/assign/copy这3个参数有点抽象,这完全是OC语言的内存管理机制导致的,分配了就要去销毁,没有人自动帮着回收。现在不用写这些命令了,ARC帮我们完成,但内存管理机制没改变。

这就是我对@property的理解不对的地方也请指出,共同学习。

 

 

 

你可能感兴趣的:(IOS学习笔记)