OC 属性修饰符

getter / setter
readwrite / readonly
atomic / nonatomic
assign / strong / weak / retain / copy
其它临时变量



getter、setter

@property (nonatomic, assign, getter=isLoading ,setter=loading:) BOOL loading;

重命名了loading的getter方法。所以可以通过下面方法来得到loading的值。

    if (self.isLoading) {
    }

    if ([self isLoading]) {
    }

    if (self.loading) {
    }

重命名了loading的setter方法。所以可以通过下面方法来赋值。

- (void)loadingIs:(BOOL)loading{

    _loading= loading;

}

readwrite 、 readonly

读写性修饰符:

readwrite:表明这个属性是可读可写的,系统为我们生成这个属性的setter和getter方法以及下划线开头的成员变量。

• readonly:表明这个属性只能读不能写,系统只为我们生成一个getter方法下划线开头的成员变量,不会创建setter方法


atomic 、nonatomic

atomic 和 nonatomic 有什么区别? 传送门

原子性修饰符atomic 、nonatomic。在定义的时候不写atomic或者nonatomic,默认是atomic
atomic可以保证读/写安全。二者均不能保证线程安全,例如release。

对比

atomic 和 nonatomic 的区别在于,系统自动生成的 getter/setter 方法不一样。
如果你自己写 getter/setter,那atomic/nonatomic/retain/assign/copy (setter相关修饰符)这些关键字只起提示作用,写不写都一样。
但是我们不可能每个属性都自己写setter getter或许还是要看看的。

多线程管理(苹果在一定程度上屏蔽了多线程操作)
Nonatomic:高性能,一般使用这个
Atomic:低性能

对于atomic的属性,系统生成的 getter/setter 会保证 get、set 操作的完整性,不受其他线程影响。比如,线程 A 的 getter 方法运行到一半,线程 B 调用了 setter:那么线程 A 的 getter 还是能得到一个完好无损的对象。

而nonatomic就没有这个保证了。所以,nonatomic的速度要比atomic快。

不过atomic可并不能保证线程安全。如果线程 A 调了 getter,与此同时线程 B 、线程 C 都调了 setter——那最后线程 A get 到的值,3种都有可能:可能是 B、C set 之前原始的值,也可能是 B set 的值,也可能是 C set 的值。同时,最终这个属性的值,可能是 B set 的值,也有可能是 C set 的值。

保证数据完整性——这个多线程编程的最大挑战之一——往往还需要借助其他手段。


Atomic

1.是默认的
2.原子属性
3.为setter方法加锁 (默认) (类似多线程中的互斥锁)
4. 速度不快,因为要保证操作整体完成
5.线程安全(不是绝对的),但需要消耗大量资源。会保证 CPU 能在别的线程来访问这个属性之前,先执行完当前流程

Non-Atomic

1.不是默认的
2.非原子属性
3.不为setter方法加锁
4.更快
4.非线程安全,适合内存小的移动设备。如有两个线程访问同一个属性,会出现无法预料的结果

例子1

假设有一个 atomic 的属性 “name”,如果线程 A 调[self setName:@"A"],线程 B 调[self setName:@"B"],线程 C 调[self name],那么所有这些不同线程上的操作都将依次顺序执行——也就是说,如果一个线程正在执行 getter/setter,其他线程就得等待。因此,属性 name 是读/写安全的。

但是,如果有另一个线程 D 同时在调[name release],那可能就会crash,因为 release 不受 getter/setter 操作的限制。也就是说,这个属性只能说是读/写安全的,但并不是线程安全的,因为别的线程还能进行读写之外的其他操作。线程安全需要开发者自己来保证。

如果 name 属性是 nonatomic 的,那么上面例子里的所有线程 A、B、C、D 都可以同时执行,可能导致无法预料的结果。如果是 atomic 的,那么 A、B、C 会串行,而 D 还是并行的。

例子2

nonatomice代码:

//@property(nonatomic, retain) UITextField *userName;
//系统生成的代码如下:

- (UITextField *) userName {
    return userName;
}

- (void) setUserName:(UITextField *)userName_ {
    [userName_ retain];
    [userName release];
    userName = userName_;
}

atomic代码:

//@property(retain) UITextField *userName;
//系统生成的代码如下:

- (UITextField *) userName {
    UITextField *retval = nil;
    @synchronized(self) {
        retval = [[userName retain] autorelease];
    }
    return retval;
}

- (void) setUserName:(UITextField *)userName_ {
    @synchronized(self) {
      [userName release];
      userName = [userName_ retain];
    }
}

简单来说,就是 atomic 会加一个锁来保障线程安全,并且引用计数会 +1,来向调用者保证这个对象会一直存在。假如不这样做,如有另一个线程调 setter,可能会出现线程竞态,导致引用计数降到0,原来那个对象就释放掉了。

要注意那个锁并不能『保证线程安全』。


assign / strong / weak / retain / copy

assign

用于 ‘基本数据类型’、‘枚举’、‘结构体’ 等非OC对象类型。 eg:int、bool


strong

OC对象类型(NSArray、NSDate、NSNumber、模型类)

一个对象只要有强指针引用着,就不会被销毁 。


weak

弱引用

在弱引用的时候使用weak。例如代理、block(防止循环引用)。

block也会经常导致循环引用,所以通常的做法就是,在外部创建一个weakSelf(用__weak修饰的self),来防止循环引用。这里最好在block内部再声明一个strongSelf(用__strong来修饰weakSelf).这是因为保证代码在执行block期间,self不会被释放,当block执行完后,会自动释放该strongSelf;

UI控件

在创建UI空间的时候,准确的来说是使用weak修饰的。但是如果我们的操作稍有不慎,控件容易释放。这也是为什么大部分人都使用strong进行修饰的原因。
但是使用strong就会使空间的引用计数多+1,导致释放不及时,但是在父试图消失的时候,空间还是会释放的。所以对内存要求不大,没有强迫症的人可以使用strong,简单粗暴。

对于为什么UI控件使用weak修饰 传送门

UI控件使用weak or strong 传送门


reatin

retain:针对对象类型进行内存管理(非ARC)。
当给对象类型使用此修饰符时,setter方法会先将旧的对象属性release掉,再对新的对象进行一次赋值并进行一次retain操作

retain是指针的复制。


copy

修饰符介绍 传送门

copy是内容的复制。
copy语法的作用:产生副本。 且copy返回的是不可变的副本,mutableCopy返回的是可变的副本。

修改了副本并不会影响源对象,修改了源对象,并不会影响副本。

1. 一般用在NSString*类型、block类型上。

1.1 NSString
注意:并不是所有情况下我们的string都必须使用copy,因为如果我们的需求是希望string是随着我的改变而改变的,那么这个时候应该使用strong。

在setter方法中会判断 传入的字符串 是否是可变的。
如果是可变的就分配新的内存再赋值
如果是不可变的就直接赋值地址
而实际上开发中其实大量使用的是不可变的字符串   所以最好使用 strong 修饰字符串,可以提升性能(减少一次判断)。

NSString用copy or strong? 传送门

对源头是NSMutableString的字符串,retain仅仅是指针引用,增加了引用计数器,这样源头改变的时候,用这种retain方式声明的变量(无论被赋值的变量是可变的还是不可变的),它也会跟着改变;
而copy声明的变量,它不会跟着源头改变,它实际上是深拷贝。

对源头是NSString的字符串,无论是retain声明的变量还是copy声明的变量,当第二次源头的字符串重新指向其它的地方的时候,它还是指向原来的最初的那个位置,也就是说其实二者都是指针引用,也就是浅拷贝。

另外说明一下,这两者对内存计数的影响都是一样的,都会增加内存引用计数,都需要在最后的时候做处理。

其实说白了,对字符串为啥要用这两种方式?我觉得还是一个安全问题,比如声明的一个NSString *str变量,然后把一个NSMutableString *mStr变量的赋值给它了,如果要求str跟着mStr变化,那么就用retain;如果str不能跟着mStr一起变化,那就用copy。而对于要把NSString类型的字符串赋值给str,那两都没啥区别。不会影响安全性,内存管理也一样。

1.2 block

内存分为五个区:栈区、堆区、常量区、代码区、静态区(全局区)。
Block用copy修饰可以拷贝到堆区,以便我们程序员管理。
它本身默认是在栈区,由系统管理,什么时候释放不确定,为了避免访问野指针,所以应该把它放在堆区。

用copy修饰Block时—->首先要知道Block在非ARC和ARC下的区别。

非ARC环境下:

 
block访问外部局部变量,block存放栈里面
只要block访问变量,而且是整个app都存在的变量,那么肯定在全局区
在非ARC中.不能使用retain引用block,因为不会放在堆里面,在非ARC中只能使用copy,才会把block放在堆里面。

ARC环境下:

  
  只要block访问了外部局部变量,block就会存放到堆里面
  可以使用strong去引用  因为本身就已经是存放在堆区了
  也可以用copy,但是用stong性能更好
  

注意:在ARC下,声明出来的block属性,不管用strong还是copy,编译器都会对这个block进行copy操作。即便如此,还是推荐使用copy来修饰,这样也能提醒你,编译器会对block进行copy操作。

2. 类的copy:

如果我们想实现类的copy,必须实现一个方法:-(id)copyWithZome:(NSZone*)zone;这是为什么呢?

我们去 NSString 中去寻找答案,那么我们会发现其实 NSString 已经遵守了 NSCopying 与 NSMutableCopying 的协议,我们主要看 NSCopying ,进入这个协议之后你会发现 -(id)copyWithZome:(NSZone*)zone这个方法,也就是说NSString 已经遵守了协议的这个方法,所以才能直接实现 copy 的方法。所以如果想实现自定义类的 copy 方法,我们是需要先遵守 NSCopying 协议,然后实现-(id)copyWithZome:(NSZone*)zone的方法:

-(id)copyWithZone:(NSZone *)zone{

        Mitchell*copyMit = [[Mitchell allocWithZone:zone] init];

        copyMit.name = self.name;
        return copyMit;

}

zone:系统返回给我们 copy 对象的内存空间

注意:必须在初始化方法中给属性赋值,才能让 copy 出的对象和原来的对象有相同的属性。

3. 再说一下 copy 类中的 set 方法

如果属性是 copy 的,那么系统默认只会在 set 方法中调用 copy 的方法:

-(void)setName:(Mitchell*)name{ 

        _name = [name copy];

}

其它临时变量

strong,weak, unsafe_unretained往往都是用来声明属性的,
如果想声明临时变量就得用__strong, __weak, __unsafe_unretained, __autoreleasing。

unsafe_unretained 传送门

nullable 传送门

null_resettable 传送门

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值