oc基础

OC属性的修饰符

2020-07-01  本文已影响0人  相思的忆

属性修饰符是什么?有什么作用?

属性修饰符,顾名思义就是对属性进行修饰的符号。不同修饰符修饰的属性会表现出不一样的属性特性,其表现形式并不是那么显而易见。
那它的作用是什么呢?从上面的定义上面我们就能发现,被不同修饰符修饰的属性会有不同的特性,根据这些特性,我们能更好的优化自己的代码逻辑,更简单的实现效果。
接下来我们就逐个详细分析一下。

属性修饰符有哪些?

这里我把常用的属性修饰符分为三大类

还有其他的例如

现在我们来详细说一下常用的三大类属性修饰符。

1、线程安全类 nonatomic/atomic

  1. nonatomic 非原子属性。它的特点是多线程并发访问性能高,但是访问不安全;与之相对的就是atomic,特点就是安全但是是以耗费系统资源为代价,所以一般在工程开发中用nonatomic的时候比较多。
  2. 系统默认的是atomic,为setter方法加锁,而nonatomic 不为setter方法加锁。
  3. 如1所述,使用nonatomic要注意多线程间通信的线程安全。

根据上述描述,项目中我们基本上只使用nonatomic,因为他的访问性能高,对于多线程处理的时候,也建议自己去实现加锁的处理。一是因为atomic的加锁占用系统资源量大,二是因为atomic只是在setter/getter方法中进行了加锁处理,在其他操作中是没有的,这里可能会出现遗漏。

2、读写权限类 readwrite/readonly
这个就是访问权限的控制,决定该属性是否可读和可写,默认是readwrite,所以我们定义属性的时候,一般不需要这个修饰。只有只读属性才需要加上readonly的修饰。
readonly来控制读写权限的方式就是只生成getter方法,不生成setter方法。

3、内存管理类 assign/copy/strong/weak
这一类的的修饰符是该文档中最重要的部分了,这也是我们项目编码中,经常会混淆,理解错误的地方。不知道这些修饰符都要什么时候时候使用。

总结:可以说属性修饰符就是对setter/getter方法的一些功能性扩展,使其定义简单的同时能够满足更多的功能要求。

栈区、常量区、全局区用 assign
堆区持有用 strongcopy,不持有用 weak
什么叫不持有?
就是一个对象别其他变量持有了,那该变量就指向该对象,如果别的变量都不持有该对象了,那该变量也不需要指向该对象了,这就叫不持有。

注意点

接下来我们说明一下在实际使用过程中比较常见的一些误区和知识点。

  1. NSString 为什么要用 copy?为什么不能用strong
    如果是简单的就是 NSString 类型的字符串,它们都是存放在常量区里面 的,就不需要进行内存管理,assign,copy,strong 都是可以修饰的,它们实际上都不会去改变任何东西。
@property (nonatomic, assign) NSString *str1;
@property (nonatomic, copy) NSString *str2;
@property (nonatomic, strong) NSString *str3;
    self.str1 = @"str1";
    self.str2 = @"str2";
    self.str3 = @"str3";
    dispatch_async(dispatch_get_main_queue(), ^{
        NSLog(@"str1:%@ %p", self.str1, self.str1);
        NSLog(@"str2:%@ %p", self.str2, self.str2);
        NSLog(@"str3:%@ %p", self.str3, self.str3);
    });
2020-07-01 14:37:16.465702+0800 Test_UI[76089:3507003] str1:str1 0x10c1d5150
2020-07-01 14:37:16.465822+0800 Test_UI[76089:3507003] str2:str2 0x10c1d5170
2020-07-01 14:37:16.465888+0800 Test_UI[76089:3507003] str3:str3 0x10c1d5190

但是为什么只能用 copy 呢?因为 NSString 的子类 NSMutableString,他是存放在堆区的。子类也是可以用父类来接收的,这中情况下 assign 就不能使用了,它无法持有 NSMutableString 会导致被提前释放。不使用 strong 而使用 copy 是为了在赋值的时候去除可变的特性,优化内存管理,让存放在堆区的 NSMutableString 对象及时得到释放。

@property (nonatomic, assign) NSString *str1;
@property (nonatomic, copy) NSString *str2;
@property (nonatomic, strong) NSString *str3;
    self.str1 = [@"str1" mutableCopy];
    self.str2 = [@"str2" mutableCopy];
    self.str3 = [@"str3" mutableCopy];
    dispatch_async(dispatch_get_main_queue(), ^{
        NSLog(@"str1:%@ %p", self.str1, self.str1);
        NSLog(@"str2:%@ %p", self.str2, self.str2);
        NSLog(@"str3:%@ %p", self.str3, self.str3);
        NSLog(@"str class: %@", [@"str123" class]);
        NSLog(@"mutable str copy class : %@", [[[@"str123" mutableCopy] copy] class]);
    });
2020-07-01 14:38:10.159028+0800 Test_UI[76124:3508299] str1:str3 0x6000012d9e00
2020-07-01 14:38:10.159150+0800 Test_UI[76124:3508299] str2:str2 0xaffe863aa45e1a58
2020-07-01 14:38:10.159220+0800 Test_UI[76124:3508299] str3:str3 0x6000012d9e00
2020-07-01 14:38:10.159310+0800 Test_UI[76124:3508299] str class: __NSCFConstantString
2020-07-01 14:38:10.159393+0800 Test_UI[76124:3508299] mutable str copy class : NSTaggedPointerString

我们来分析一下上面输出的情况可以看到 str1str3 的地址变成一样的了,str1 并没有变成野指针崩溃,然后我同时也打印了一下内容,发现 str1 的内容变成了 str3的内容。这是为什么呢?
因为我们使用 assign 修饰的 str1 ,在赋值之后没有持有导致马上就被释放掉了,而该属性指向的内容也没有了,而在之后 str3 创建的对象刚好也是从这个地址开始创建的,所以就造成了上面的那种现象。这里我们把后面 str2,str3 的代码删除之后运行会发现, str1的内容就变成空了。

2020-07-01 15:13:42.852822+0800 Test_UI[92383:3561873] str1:<__NSMallocBlock__: 0x60000089e220> 0x60000089e220

接下来我们发现 str2 的地址不是在常量区的,这是为什么呢?
最后我们也打印了一下两种字符串的类型,发现它们是不一样的,为什么呢?同样都是不可变字符串,还都是 NSString
这是因为 NSString 是另一个类蔟,类蔟的定义和表现我们之后再讲。__NSCFConstantString 该类型的会直接存放在常量区,这是代码编译时就能够决定的,然后直接存放在常量区。NSTaggedPointerString 这种类型的可以说是动态生成的,编译的时候无法判断,所以在他产生的时候 动态添加到栈区里面。

  1. 可变类型不能使用 copy
    可变类型的属性如果使用 copy 来修饰,在赋值的时候,该值会进行一次 copy 操作,导致属性指向的是一个不可变的对象,如果用该属性去调用可变对象的方法,会产生崩溃。

  2. Block到底使用什么来修饰?
    block有一个特性,当它访问了外部局部变量(注意:这里是外部的局部变量,全局变量不受影响),就会存放在堆区。

@property (nonatomic, assign) void(^block1)(void);
@property (nonatomic, copy) void(^block2)(void);
@property (nonatomic, strong) void(^block3)(void);
    void(^block1)(void) = ^() {
        NSLog(@"1");
    };
    void(^block2)(void) = ^() {
        NSLog(@"2");
    };
    void(^block3)(void) = ^() {
        NSLog(@"3");
    };
    NSLog(@"block1: %p", block1);
    NSLog(@"block2: %p", block2);
    NSLog(@"block3: %p", block3);
    self.block1 = block1;
    self.block2 = block2;
    self.block3 = block3;
    dispatch_async(dispatch_get_main_queue(), ^{
        NSLog(@"block1: %p", self.block1);
        NSLog(@"block2: %p", self.block2);
        NSLog(@"block3: %p", self.block3);
    });
2020-07-01 16:39:06.271597+0800 Test_UI[24869:3706043] block1: 0x10a984108
2020-07-01 16:39:06.271695+0800 Test_UI[24869:3706043] block2: 0x10a984128
2020-07-01 16:39:06.271759+0800 Test_UI[24869:3706043] block3: 0x10a984148
2020-07-01 16:39:06.294224+0800 Test_UI[24869:3706043] block1: 0x10a984108
2020-07-01 16:39:06.294314+0800 Test_UI[24869:3706043] block2: 0x10a984128
2020-07-01 16:39:06.294391+0800 Test_UI[24869:3706043] block3: 0x10a984148

所以这样的block所有修饰符都可以修饰。

@property (nonatomic, assign) void(^block1)(void);
@property (nonatomic, copy) void(^block2)(void);
@property (nonatomic, strong) void(^block3)(void);
    int num = 2;

    void(^block1)(void) = ^() {
        NSLog(@"%d", num);
    };
    void(^block2)(void) = ^() {
        NSLog(@"%d", num);
    };
    void(^block3)(void) = ^() {
        NSLog(@"%d", num);
    };
    NSLog(@"block1: %p", block1);
    NSLog(@"block2: %p", block2);
    NSLog(@"block3: %p", block3);
    self.block1 = block1;
    self.block2 = block2;
    self.block3 = block3;
    dispatch_async(dispatch_get_main_queue(), ^{
        NSLog(@"block1: %p", self.block1);
        NSLog(@"block2: %p", self.block2);
        NSLog(@"block3: %p", self.block3);
    });
2020-07-01 17:08:27.093104+0800 Test_UI[25619:3736522] block1: 0x600003fb1950
2020-07-01 17:08:27.093214+0800 Test_UI[25619:3736522] block2: 0x600003fb12f0
2020-07-01 17:08:27.093287+0800 Test_UI[25619:3736522] block3: 0x600003fb08d0
2020-07-01 17:08:27.102584+0800 Test_UI[25619:3736522] block1: 0x600003fb1950
2020-07-01 17:08:27.102696+0800 Test_UI[25619:3736522] block2: 0x600003fb12f0
2020-07-01 17:08:27.102785+0800 Test_UI[25619:3736522] block3: 0x600003fb08d0

这种block就不能使用 assign 来修饰,如果及时释放的话,访问 self.block1 的时候会产生崩溃。

报错

而同样的我们也可以看到,不管是 copy 还是 strong,block的地址都没有变,所以它们是等价的,而使用 strong 更加直接,性能会更好,而同样的,对已经自动管理的block类型而言,我们所有修饰符都可以使用,所以为了通用,我们在ARC下使用 strong 来修饰所有的block,当然也可以用 copy,可以说 copy 的修饰是从MRC中继承过来的。

上一篇下一篇

猜你喜欢

热点阅读