一、@property基本作用
@property(nonatomic,retain)NSString *str;
@property关键字提供了外界对成员变量的访问接口,其本质是为某一个成员变量提供set和get操作(并不会为你在.m文件中生成可见代码)。
上面代码声明了一个NSString *类型的对象str,并会自动生成str的set和get方法,这跟在java、C#中我们自己定义的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)
2、setter语意:(assign/retain/copy)
3、原子性:(atomic/nonatomic)
4、引用强弱:(strong/week)
上面介绍过了@property为成员变量提供外部访问的接口,而参数就将这种访问更具体化了,默认会自带3个参数:readwrite、assign、atomic。
1、readwrite
默认值,表示生成set/get方法
2、readonly
只生成get方法
3、assign
默认值,表示直接赋值,先看看会生成怎样的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操作。
基础数据类型时:
如short、int、double、long等就不会出现上面的问题,因为他们不在【堆】中,可能在【全局区】也可能在【栈】中,根据他们定义的位置而定,而这些内存都是由系统自动管理的,不同于【堆】需要手动释放。所以基本数据类型可以使用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帮我们完成,但内存管理机制没改变。