关于OC的成员变量和Property,我懂得他们的区别,但是一直有一个疑问,就是当成员变量和property都是在.m文件中,他们有什么区别,比如在有test.h和test.m两个文件,
test.h:@interface test : NSObject@end test.m:@interface test()
{
 NSString *memberStr;
}
@property (nonatomic, strong) NSString *properyStr;
@end这种情况,memberStr和propertyStr都是外部不可见,反过来说,如果对于外部不可见的变量,有两种实现方式,那么根据什么原则去选择。还有property可以指定strong特性,那么成员变量默认的特性又是什么呢。希望大家可以指点一下

解决方案 »

  1.   

    它们的区别还是很大的。区别:
    本质上,OC是没有私有方法的,这就意味着,其属性也不可能是私有的。放在.m文件中只是增强了它的隐蔽性而已。因为属性最终会被编译器转换成对应的getter、setter方法,而由于OC是基于消息的,所以无论对象是否存在某个方法,都可以向它发送。例如:id obj = [test alloc] init];
    NSString* str = [obj getPropertyStr];  // That's OK成员变量可以通过显性使用@private关键字来明确指定其私有性,这样即使在同一个.m文件中,也无法访问该实例变量。
    建议如果对象的某个状态值需要被外界访问,那么就使用属性来定义。而如果该值只是用于辅助记录内部状态的,那么使用成员变量即可。当然也有很多例外,例如可以定义一个私有的readonly属性,用于用属性的语义来获取某个经过计算/转换的状态值。内存问题
    成员变量和属性可以使用同样的内存管理语义只是使用的前缀不一样而已
    strong  -> __strong
    weak    -> __weak
    ......
    在属性所使用的语义前面加上两个下划线,即可用于声明变量所使用的内存管理语义。除了成员变量,也可用于局部变量的定义。关于KVO
    还有一点,即使使用了@private关键字进行限定,成员变量也不是严格意义上“私有”的,因为可以使用OC的KVO机制对其进行修改和访问:test* obj = [[test alloc] init];
    [obj setValue:@"haha" forKeyPath:@"memberStr"];但是具体实现细节上还是有点区别,详情请参考KVO相关的资料
      

  2.   

    更正上面的两点:
    第一个例子:id obj = nil; // 这样更具备说明性
    NSString* str = [obj getPropertyStr];  // That's OK
    如果使用@private关键字,实例变量即使在同一个.m文件中,也无法被其他类型访问,但是自身类型是可以访问的。补充:
    关于属性的使用时机,即使是内部状态量,如果该状态被同一个类型的多个地方进行修改,且具有“联动”效应——即修改该状态,同时需要改变其它状态,那么最好使用方法,或者属性来实现。否则每一个地方都会出现同样的“联动”状态同步的代码。
      

  3.   


    谢谢virtualxmars的详细回答,深入又易懂
      

  4.   

    请问,他是在Test.m中定义的属性,在接口文件.h中并没有定义,你是怎么通过[obj getPropertyStr];获得的get方法呢?
      

  5.   

    propertyStr会synthesize一个实例变量_propertyStr(内存增加八个字节),你又定义了一个实例变量memberStr,内存会再次增加八个字节,如果你想不增加额外的开销,可以通过synthesize指定propertyStr合成的实例变量为memberStr;例如:@synthesize propertyStr = memberStr
      

  6.   

    成员变量默认的内存管理是 strong。
    我补充一点,假设有一个属性:name,它有两种访问方式:
    _name
    self.name
    除了 getter、setter 这个以外,它们在编译时和运行时的访问方式也不同:
    用 _name 访问时,在编译期程序就已经知道它的内存地址了,运行时是直接去该地址访问变量;
    用 self.name 访问时,是在运行时通过消息机制动态的访问变量的。
    _name 的性能更好,但是会有一个隐患(这个隐患可能永远不会被触发),就是 OC 是非常动态的,你甚至可以在运行时添加成员变量,但是如果你添加的成员变量的内存地址在 _name 的前面,那你用 _name 这种硬编码的方式访问就必然会出错。
      

  7.   


    ”但是如果你添加的成员变量的内存地址在 _name 的前面,那你用 _name 这种硬编码的方式访问就必然会出错“,什么叫”成员变量的内存地址在 _name 的前面“????您的意思是_name的内存地址之前被占用过吗?????????
      

  8.   


    ”但是如果你添加的成员变量的内存地址在 _name 的前面,那你用 _name 这种硬编码的方式访问就必然会出错“,什么叫”成员变量的内存地址在 _name 的前面“????您的意思是_name的内存地址之前被占用过吗?????????
    不,我指的是内存布局发生了变化。看看这本书的第22页:http://vdisk.weibo.com/s/qOPFY0tDeUfa。这种情况并不常见。