• 实例变量和属性


    实例变量和属性

    声明

    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消息时,会返回指向不可变对象自己的指针。

  • 相关阅读:
    Apache Beam入门及Java SDK开发初体验
    fetch的请求
    Spring Cache 带你飞(一)
    存储技术发展过程
    Redis 高阶数据类型重温
    Redis 基础数据类型重温
    [源码解析] PyTorch 分布式(1)------历史和概述
    [源码解析] PyTorch 如何实现后向传播 (4)---- 具体算法
    [源码解析] Pytorch 如何实现后向传播 (3)---- 引擎动态逻辑
    [源码解析] Pytorch 如何实现后向传播 (2)---- 引擎静态结构
  • 原文地址:https://www.cnblogs.com/sueZheng/p/4890661.html
Copyright © 2020-2023  润新知