美文网首页
使用@property时发生了什么

使用@property时发生了什么

作者: 54番茄 | 来源:发表于2018-04-02 15:17 被阅读5次

            @property 其实就是在编译阶段由编译器自动帮我们生成ivar成员变量getter方法,setter方法。使用“自动合成”( auto synthesis)这个过程由编译器在编译阶段执行自动合成,所以编译器里看不到这些“合成方法”(synthesized method)的源代码。除了生成getter、setter方法之外,编译器还要自动向类中添加成员变量(在属性名前面加下划线,以此作为实例变量的名字)。

    实际流程:
    每次增加一个属性,系统都会在 ivar_list 中添加一个成员变量的描述,在 method_list 中增加 setter 与 getter 方法的描述,在 prop_list 中增加一个属性的描述,计算该属性在对象中的偏移量,然后给出 setter 与 getter 方法对应的实现。
            在 setter 方法中从偏移量的位置开始赋值,在 getter 方法中从偏移量开始取值,为了能够读取正确字节数,系统对象偏移量的指针类型进行了类型强转。
    详细底层流程看这个《@property深入代码理解》

    尝试手工创建存取器

    @interface Person : NSObject
    {
        NSString *_name;    //成员变量
        NSUInteger _age;    
    }
    
    - (void)setName:(NSString*)name;
    - (NSString*)name;
    
    - (void)setAge:(NSUInteger)age;
    - (NSUInteger)age;
    
    @end
    

    上面的代码中_name和_age就是Person的实例变量,默认生成与原名一样的带下划线的成员变量,并且可以看到分别对这两个实例变量声明了get/set方法,即存取器。存取器就是对实例变量进行赋值和取值。按照约定赋值方法以set开头,取值方法以实例变量名命名。

    实现方法:

    @implementation Person
    
    - (void)setName:(NSString*)name {
        ###注意:必须使用_name来赋值,使用self.name来设置值时编译器会自动转为调用该setter函数,会导致无限递归
        ###使用_name则是直接访问底层的存储属性,不会调用该方法来赋值
        ###这里使用copy是为了防止NSMutableString多态
        _name = [name copy];
    }
    
    - (NSString*)name {
      ###必须使用_name来访问属性值,使用self.name来访问值时编译器会自动转为调用该getter函数,会造成无限递归
        return _name;
    }
    
    - (void)setAge:(NSUInteger)age {
        _age = age;
    }
    
    - (NSUInteger)age {
        return _age;
    }
    
    @end
    

    上述代码就是手动创建变量的getter和setter的实现,getter和setter本质就是符合一定命名规范的实例方法。
    实例方法与点语法的调用:

    Person *p = [[Person alloc] init];
    //函数调用name的setter
     [p setName:@"番茄"];
     //函数调用age的setter
    [p setAge:100];
    //函数调用name和age的getter
    NSLog(@"%@ %ld", [p name], [p age]);
    
     *打印结果*
    输出: 番茄 22
    

    通过调用方式可以看出,setter和getter本质就是实例方法,可以通过函数调用的方式来使用。但是这种方式还是很费事的,所以为了方便使用,Objective-C允许使用点语法来访问getter和setter。

    p.name = @"番茄";
    p.age = 110;
    

    使用点语法访问的方式本质还是调用了我们手动创建的setter和getter。 当有很多变量需要设置时,这样手工创建setter和getter的方式很操蛋了,因此合成存取方法就诞生了。
    合成存取方法:

    @interface Person : NSObject
    @property (nonatomic, copy) NSString* name;
    @property (nonatomic, assign) NSUInteger age;
    @end
    
    @implementation Person
    //编译器会帮我们自动生成_name和_age这两个实例变量,下面代码就可以正常使用这两个变量了
    //也可以自定义命名例如:name = _myName;这样用下划线方式使用只能是_myName,不会再是_name了
    @synthesize name = _name;
    @synthesize age = _age;
    @end
    

    属性用@property声明默认是原子性(Atomicity)的,这会导致一些花销。当你不仅仅有一个线程, 那么getter和setter可能会在同一时间去调用,这就意味着getter/setter可能会被另一个方法打扰,很有可能造成数据错误。
    在iOS中使用同步锁的开销比较大, 这会损耗性能。一般情况下并不要求属性必须是原子性(Atomicity),因为这并不能保证“线程安全”(thread safety),若要实现“线程安全”的操作,还需采用更为深层的锁定机制才醒。因此,几乎所有代码的属性设置都会使用nonatomic,这样能够提高访问性能。

    @property (nonatomic) NSString*model;  //设置非原子性
    @property NSString * name1;  //默认是atomic  默认使用的strong关键字修饰
    @property()NSString* nothing2; //默认是atomic
    @property(copy) NSString * name3;
    @end
    
    NSMutableString * muString = [[NSMutableString alloc]initWithString:@"ceshi"];
    self.name1 = muString;
    self.name2 = muString;
    self.name3 = muString;
    
    //修改元数据
    [muString appendString:@"123"];
    NSLog(@"self.name1  : %@",self.name1);
    NSLog(@"self.name2  : %@",self.name2);
    NSLog(@"self.name3  : %@",self.name3);
    *打印结果:*
    self.name1  : ceshi123
    self.name2  : ceshi123
    self.name3  : ceshi
    

    @property还有一些关键字,它们都是有特殊作用的,在声明属性(property)时候,常用关键字能够传递出相关行为的额外信息,比如上述代码中的nonatomic、strong、copy:

    @property(nonatomic,strong) NSString *carName;
    @property(nonatomic,strong) NSString *carType;
    我把它们分为三类,分别是:原子性,存取器控制,内存管理。
    原子性:atomic nonatomic
    存取器控制:readwrite readonly
    内存管理:assign、strong、weak、copy、retain
    详细查看:《IOS 常用关键字基础大全》

    还有点儿要注意的:
    属性的setter方法和getter方法是不能同时进行重写的,这是因为,一旦你同时重写了这两个方法,那么系统就不会帮你生成这个成员变量了,所以会报错,


    要同时重写setter与getter方法会报错.png

    如果真的就想非要重写这个属性的setter和getter方法的话,就要手动生成成员变量,然后就可以重写了。或者是用 @synthesize name = _name;

    相关文章

      网友评论

          本文标题:使用@property时发生了什么

          本文链接:https://www.haomeiwen.com/subject/puqvcftx.html