实例变量和属性

实例变量和属性

声明

Person 文件中

@interface Person : NSObject {

    NSString *_name; //实例变量

}

    @property(copy) NSString *firstName; //属性

    @property(copy) NSString *lastName;

@end 

_name 是实例变量,实例变量是类私有的变量,其他类对象无法直接访问;

写在头文件中的 firstName是属性,public,其他类对象可以直接访问;写在m文件中的属性是private,其他类对象无法直接访问。

赋值

在初始化方法中,应该直接访问实例变量,代码如下

- (id)initWithFirstName:(NSString *)aFirstName lastName:(NSString *)aLastName {

    self = [super init];

 

    if (self) {

        _firstName = aFirstName;  // 直接访问

        _lastName = aLastName;

    }

 

    return self;

 }

访问:

存取方法

初始化时,应该直接访问实例变量,因为在那个时候,实例变量可能还没有被正确赋值。如果通过存取方法访问,会引起问题。

存取方法实例:

// 存方法

-(void)setName:(NNString *)name

{

    _name=name;

}

// 取方法

-(NNString *)name

{

    return _name;

}

声明一个属性,等于隐式地为响应名称的实例变量声明一对存取方法,更加简便。也就说在头文件写在这一行代码

@property NNString *name 

编译器会自动生成实例变量_name,取方法name,和存方法setName

当默认的存取方法无法满足需求怎么办?比如,某个在赋值后还需要其他操作,则需要自定义属性的存取方法。

-(void)setName:(NNString *)name

{

    _name=name;

    // 其他操作

    ```

}

  

但是此时,编译器就不会为name属性创建存方法,不过仍然会创建取方法。如果我们同时覆盖了存取方法(或者只读属性覆盖了取方法),那么编译器就不会自动创建相应的实例变量_name。有些时候,我们不需要编译器为属性默认生成实例变量,可以同时覆盖属性的读取方法.

比如,有个需求:当某个属性第一次访问的时候,才对它进行初始化:

懒访问

- (XYZObject *)someImportantObject {

    if (!_someImportantObject) {

        _someImportantObject = [[XYZObject alloc] init]; // 直接访问实例变量

    }

    return _someImportantObject;

}

  

合成

其实,在头文件声明属性时,只会生成存取方法声明,为了让属性生成实例变量并实现存取方法,属性必须被合成(synthesized).而通常情况下,编译器会自动合成属性并生成默认的实例变量和存取方法。 我们可以在文件中用@synthesize指令自定义属性合成方式

@synthesize name = _name;

这行代码和编译器自动合成的效果相同,左边的name表示创建存取方法setName和name,右边的name 表示创建name实例变量。

点句法

运行时和存取方法运行时是没有区别的,它们都会调用之前实现的存取方法。但是点句法的可读性更好,Apple官方也坚持使用点句法存取实例变量。

 NSString *firstName = somePerson.firstName;

 

  somePerson.firstName = @"Johnny";

  • NSString *firstName = somePerson.firstName 相当于[somePerson firstName]
  • somePerson.firstName = @"Johnny"相当于[ somePerson setFirstName:@"Johnny"];

注意:用点句法操作只读属性,会报编译错误。

关系

实例变量,在对象(object)的生命周期中会一直存在,它所占用的内存是在对象首次创建(alloc)的时候被分配的;而对象销毁时,它占用的内存会被释放。默认情况下,属性被编译器自动合成的时候,会生成相应实例变量和读取方法。

Apple 官方推荐使用property。

属性的特性

任何属性都有三个特性,每个特性有多种类型,用于描述相关存取方法的行为。这三种特性分别是多线程、读写特性、内存管理特性。

多线程特性

多线程特性有两种选择类型,atomic和nonatomic。默认是atomic。这意味着,访问必须是原子操作,不能被其他操作打断。比如,声明一个属性

@property NSObject *implicitAtomicObject; 

即使从两个不同的线程同时请求访问implicitAtomicObject(一个存一个取),每次访问(存或取)不能被打断,先存完再取,或者先取完再存。

读写属性

读写属性也有两个特性,readwrite和readonly。编译器会为readwrite特性的属性生成存取方法,如果只有readonly特性,只为该属性生成取方法

内存管理

内存管理属性有四个特性,strong,weak,copy,unsafe_unretained .默认strong.

unsafe_unretained:

对于不需要做内存管理的属性,比如int objProperty 不指向任何对象,不需要做内存管理,可以直接用unsafe_unretained ,它表示存取方法会直接为实例变量赋值。

@property (unsafe_unretained) int objProperty

unsafe_unretained类型的指针指向的对象被销毁时,指针不会自动设为nil,而是成为空指针,因此不安全。但是当处理非对象属性时,是不会出现空指针问题的。

copy

当某个属性是指向其他对象的指针,而且该对象的类有可修改的子类,(比如NSString/NSMutableString)应该将该属性的内存管理特性设置为copy。 比如firstName属性,用了copy属性后,存方法改类似以下:

-(void)setFirstName:(NNString *)firstName

{

    _firstName=[firstName copy ];

}

至于所有权问题:copy方法返回的是拥有强引用特性的指针,而收到copy消息的NSString对象不会发生任何 变化;该对象不会获得也不会失去拥有者。

只有可变对象应该设置为copy,而复制不可变对象会浪费内存空间。为了避免不必要的复制,向不可变对象发送copy消息时,会返回指向不可变对象自己的指针。

原文地址:https://www.cnblogs.com/sueZheng/p/4890661.html