本篇寄语:
我们还有很多事情要做, 该来的挡也挡不住 。 会越来越好的, 还有什么不开心的呢? 该工作的工作, 该睡觉的睡觉, 还是一样的啊, 是你的自然不会走。
atomic与nonatomic
关于这个问题,儒君拜读过bbum的观点,综合官方文档:
atomic
atomic 是默认的属性,即如果声明一个变量时候,如果不修饰,就默认是atomic。用atomic修饰属性后,系统合成的setter/getter 方法就会加上读写的保护锁,以保证线程安全,官方的给出的解释是:
[_internal lock]; // lock using an object-level lock
id result = [[value retain] autorelease];
[_internal unlock];
return result;
nonatomic
只是对数据进行一个简单的返回,如果很多线程都对一个数据进行存取,得到的数据并不一定是最真实的,最终的那一个数据。所以说是“非线程安全”。
但为什么平时都是使用nonatomic 呢? 因为在大多是情况下不需要保证线程安全。而且nonatomic 因为少了加锁和开锁的步骤,性能自然更高。
getter=getterName 与 setter=setterName
我们可以对getter 和setter 方法重命名,例:
@interface RCTest : NSObject
@property(nonatomic,strong,getter=reNameGet,setter=reNameSet:)NSString * s;
@end
@implementation RCTest
@end
@interface ViewController ()
@end
@implementation ViewController
- (void)viewDidLoad {
[super viewDidLoad];
RCTest * test = [[RCTest alloc] init];
test.s = @"123";
NSLog(@"%@",test.s);
[test reNameSet:@"456"];
NSLog(@"%@",test.reNameGet);
}
@end
tips:如果设置了readonly 则不能再设置setter=setterName
readwrite/readonly
readwrite
系统默认属性,指定改成员变量可以被读写,系统会自动生成getter/ getter 方法
readonly
指定该成员变量只能读,不能写(不能赋值),如果尝试写,就会报编译错误。
属性重写
私有变量重写
如果对属性定义了readonly
,就不能调用改对象的setter 方法
.h
#import
@interface RCTest : NSObject
@property (nonatomic, readonly) NSString *s;
@end
.m
#import "RCTest.h"
@implementation RCTest
- (instancetype)init
{
self = [super init];
if (self) {
self.s = @"1";
}
return self;
}
@end
此时self.s = @"xxx";
这一行就会报错Assignment to readonly property
,但是如果.m 文件对改属性进行重写
#import "RCTest.h"
@interface RCTest()
@property (nonatomic, readwrite) NSString *s;
@end
@implementation RCTest
- (instancetype)init
{
self = [super init];
if (self) {
self.s = @"1";
}
return self;
}
@end
此时.m 文件就可以正常调用setter方法了。但对外仍然时只读属性
扩展重写
//
// RCTest+reWrite.h
// TestApp
//
#import "ViewController.h"
#import "RCTest.h"
@interface RCTest ()
@property (nonatomic, readwrite) NSString *s;
@end
通过扩展重写后,外部也可以调用setter方法了
子类重写
#import "ViewController.h"
#import "RCTest.h"
@interface RCSubTest : RCTest
@property (nonatomic, readwrite) NSString *s;
@end
setter语义(strong,weak,copy,assign, ,unsafe_unretained,retain)
Semantics | 作用 | 描述 | 引用计数 |
---|---|---|---|
assign | 修饰标量(CGRect,NSInter) | 如果用来修饰对象,会因为对象的属性没有被释放,访问时候会导致cash | +0 |
unsafe_unretained | 修饰对象,不能用来修饰标量 | +0 | |
copy | 修饰有对应可变类型子类的对象(NSString),但不用用来修饰不可变对象(NSMutableNString) | 会在内存里拷贝一份对象,两个指针指向不同的内存地址,确保这些不可变对象因为可变子类对象影响,因为多态导致属性值被修改 | +1 |
strong | 用于修饰对象 | 表示一种引用关系,拥有关系 | +1 |
weak | 用于修饰对象 | 表示弱引用关系,非拥有关系,所赋的值在引用计数为0被销毁后,weak修饰的属性会被自动置为nil能够有效防止野指针错误。 | +0 |
retain | 在MRC情况下,同Strong | 同Strong | +1 |
-
Attention:
assign
和unsafe_unretained
修饰对象时,如果当所赋的新值引用计数为0对象被销毁时属性并不知道,编译器不会将该属性置为nil,指针仍旧指向之前被销毁的内存,这时访问该属性会产生野指针错误并崩溃,所以慎用着两个属性 - 关于
copy
主要用来修饰子类有可变子类的对象(NSString/NSMutableString,NSArray/NSMutableArray,NSDictionary/NSMutableDictionary
)。该类对象需要遵守NSCopying
和NSMutableCopying
。 - 如果用copy 修饰可变对象,会让可变对象变为不可变对象。如果用copy 修饰可变对象,会让可变对象变为不可变对象。
- block 在ARC中默认分配到栈中,MRC默认分配到堆中,使用通过
copy
拷贝到栈中防止野指针错误。block 在ARC中默认分配到栈中,MRC默认分配到堆中,使用通过copy
修饰来防止野指针错误。
分析代码:
@implementation ViewController
- (void)viewDidLoad {
[super viewDidLoad];
}
-(void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event{
NSMutableString * temp = [[NSMutableString alloc] initWithString:@"new"];
NSLog(@"初始化时 内容:temp:%@,s:%@",temp,self.s);
NSLog(@"初始化时 地址:temp:%p,s:%p",temp,self.s);
NSLog(@"初始化时 引用计数:temp:%@,s:%@",[temp valueForKey:@"retainCount"],[self.s valueForKey:@"retainCount"]);
self.s = temp;
NSLog(@"赋值后 内容:temp:%@,s:%@",temp,self.s);
NSLog(@"赋值后 地址:temp:%p,s:%p",temp,self.s);
NSLog(@"赋值后 引用计数:temp:%@,s:%@",[temp valueForKey:@"retainCount"],[self.s valueForKey:@"retainCount"]);
[temp appendFormat:@"temp"];
NSLog(@"修改temp后 内容:temp:%@,s:%@",temp,self.s);
NSLog(@"修改temp后 地址:temp:%p,s:%p",temp,self.s);
NSLog(@"修改temp后 引用计数:temp:%@,s:%@",[temp valueForKey:@"retainCount"],[self.s valueForKey:@"retainCount"]);
[self.s appendFormat:@"s"];
NSLog(@"修改s后 内容:temp:%@,s:%@",temp,self.s);
NSLog(@"修改s后 地址:temp:%p,s:%p",temp,self.s);
NSLog(@"修改s后 引用计数:temp:%@,s:%@",[temp valueForKey:@"retainCount"],[self.s valueForKey:@"retainCount"]);
temp = nil;
NSLog(@"置nil后 内容:temp:%@,s:%@",temp,self.s);
NSLog(@"置nil后 地址:temp:%p,s:%p",temp,self.s);
NSLog(@"置nil后 引用计数:temp:%@,s:%@",[temp valueForKey:@"retainCount"],[self.s valueForKey:@"retainCount"]);
}
@end
- assign
@interface ViewController ()
@property (nonatomic, assign) NSMutableString *s;
@end
日志
2018-11-25 23:42:56.369195+0800 TestApp[2260:132430] 初始化时 内容:temp:new,s:(null)
2018-11-25 23:42:56.369420+0800 TestApp[2260:132430] 初始化时 地址:temp:0x1c0240e40,s:0x0
2018-11-25 23:42:56.369877+0800 TestApp[2260:132430] 初始化时 引用计数:temp:1,s:(null)
2018-11-25 23:42:56.369984+0800 TestApp[2260:132430] 赋值后 内容:temp:new,s:new
2018-11-25 23:42:56.370084+0800 TestApp[2260:132430] 赋值后 地址:temp:0x1c0240e40,s:0x1c0240e40
2018-11-25 23:42:56.370245+0800 TestApp[2260:132430] 赋值后 引用计数:temp:1,s:2
2018-11-25 23:42:56.370351+0800 TestApp[2260:132430] 修改temp后 内容:temp:newtemp,s:newtemp
2018-11-25 23:42:56.370448+0800 TestApp[2260:132430] 修改temp后 地址:temp:0x1c0240e40,s:0x1c0240e40
2018-11-25 23:42:56.370581+0800 TestApp[2260:132430] 修改temp后 引用计数:temp:1,s:2
2018-11-25 23:42:56.370680+0800 TestApp[2260:132430] 修改s后 内容:temp:newtemps,s:newtemps
2018-11-25 23:42:56.370775+0800 TestApp[2260:132430] 修改s后 地址:temp:0x1c0240e40,s:0x1c0240e40
2018-11-25 23:42:56.378547+0800 TestApp[2260:132430] 修改s后 引用计数:temp:1,s:2
(lldb) cash
- unsafe_unretained
@interface ViewController ()
@property (nonatomic, unsafe_unretained) NSMutableString *s;
@end
日志
2018-11-25 23:45:58.820453+0800 TestApp[2274:133275] 初始化时 内容:temp:new,s:(null)
2018-11-25 23:45:58.820664+0800 TestApp[2274:133275] 初始化时 地址:temp:0x1c044c540,s:0x0
2018-11-25 23:45:58.821120+0800 TestApp[2274:133275] 初始化时 引用计数:temp:1,s:(null)
2018-11-25 23:45:58.821224+0800 TestApp[2274:133275] 赋值后 内容:temp:new,s:new
2018-11-25 23:45:58.821321+0800 TestApp[2274:133275] 赋值后 地址:temp:0x1c044c540,s:0x1c044c540
2018-11-25 23:45:58.821460+0800 TestApp[2274:133275] 赋值后 引用计数:temp:1,s:2
2018-11-25 23:45:58.821563+0800 TestApp[2274:133275] 修改temp后 内容:temp:newtemp,s:newtemp
2018-11-25 23:45:58.821658+0800 TestApp[2274:133275] 修改temp后 地址:temp:0x1c044c540,s:0x1c044c540
2018-11-25 23:45:58.821790+0800 TestApp[2274:133275] 修改temp后 引用计数:temp:1,s:2
2018-11-25 23:45:58.821889+0800 TestApp[2274:133275] 修改s后 内容:temp:newtemps,s:newtemps
2018-11-25 23:45:58.821982+0800 TestApp[2274:133275] 修改s后 地址:temp:0x1c044c540,s:0x1c044c540
2018-11-25 23:45:58.830222+0800 TestApp[2274:133275] 修改s后 引用计数:temp:1,s:2
(lldb) cash
- strong
@interface ViewController ()
@property (nonatomic, strong) NSMutableString *s;
@end
日志
2018-11-25 23:46:32.635171+0800 TestApp[2278:133685] 初始化时 内容:temp:new,s:(null)
2018-11-25 23:46:32.637636+0800 TestApp[2278:133685] 初始化时 地址:temp:0x1c0443510,s:0x0
2018-11-25 23:46:32.638161+0800 TestApp[2278:133685] 初始化时 引用计数:temp:1,s:(null)
2018-11-25 23:46:32.638266+0800 TestApp[2278:133685] 赋值后 内容:temp:new,s:new
2018-11-25 23:46:32.638364+0800 TestApp[2278:133685] 赋值后 地址:temp:0x1c0443510,s:0x1c0443510
2018-11-25 23:46:32.638503+0800 TestApp[2278:133685] 赋值后 引用计数:temp:2,s:3
2018-11-25 23:46:32.638606+0800 TestApp[2278:133685] 修改temp后 内容:temp:newtemp,s:newtemp
2018-11-25 23:46:32.638703+0800 TestApp[2278:133685] 修改temp后 地址:temp:0x1c0443510,s:0x1c0443510
2018-11-25 23:46:32.638834+0800 TestApp[2278:133685] 修改temp后 引用计数:temp:2,s:3
2018-11-25 23:46:32.638934+0800 TestApp[2278:133685] 修改s后 内容:temp:newtemps,s:newtemps
2018-11-25 23:46:32.639028+0800 TestApp[2278:133685] 修改s后 地址:temp:0x1c0443510,s:0x1c0443510
2018-11-25 23:46:32.646944+0800 TestApp[2278:133685] 修改s后 引用计数:temp:2,s:3
2018-11-25 23:46:32.647069+0800 TestApp[2278:133685] 置nil后 内容:temp:(null),s:newtemps
2018-11-25 23:46:32.647167+0800 TestApp[2278:133685] 置nil后 地址:temp:0x0,s:0x1c0443510
2018-11-25 23:46:32.647281+0800 TestApp[2278:133685] 置nil后 引用计数:temp:(null),s:2
- retain
@interface ViewController ()
@property (nonatomic, retain) NSMutableString *s;
@end
日志
2018-11-25 23:48:04.844746+0800 TestApp[2289:134485] 初始化时 内容:temp:new,s:(null)
2018-11-25 23:48:04.844952+0800 TestApp[2289:134485] 初始化时 地址:temp:0x1c0456fe0,s:0x0
2018-11-25 23:48:04.847484+0800 TestApp[2289:134485] 初始化时 引用计数:temp:1,s:(null)
2018-11-25 23:48:04.847652+0800 TestApp[2289:134485] 赋值后 内容:temp:new,s:new
2018-11-25 23:48:04.847753+0800 TestApp[2289:134485] 赋值后 地址:temp:0x1c0456fe0,s:0x1c0456fe0
2018-11-25 23:48:04.847909+0800 TestApp[2289:134485] 赋值后 引用计数:temp:2,s:3
2018-11-25 23:48:04.848014+0800 TestApp[2289:134485] 修改temp后 内容:temp:newtemp,s:newtemp
2018-11-25 23:48:04.848109+0800 TestApp[2289:134485] 修改temp后 地址:temp:0x1c0456fe0,s:0x1c0456fe0
2018-11-25 23:48:04.848242+0800 TestApp[2289:134485] 修改temp后 引用计数:temp:2,s:3
2018-11-25 23:48:04.848341+0800 TestApp[2289:134485] 修改s后 内容:temp:newtemps,s:newtemps
2018-11-25 23:48:04.848435+0800 TestApp[2289:134485] 修改s后 地址:temp:0x1c0456fe0,s:0x1c0456fe0
2018-11-25 23:48:04.858893+0800 TestApp[2289:134485] 修改s后 引用计数:temp:2,s:3
2018-11-25 23:48:04.859017+0800 TestApp[2289:134485] 置nil后 内容:temp:(null),s:newtemps
2018-11-25 23:48:04.859115+0800 TestApp[2289:134485] 置nil后 地址:temp:0x0,s:0x1c0456fe0
2018-11-25 23:48:04.859229+0800 TestApp[2289:134485] 置nil后 引用计数:temp:(null),s:2
- weak
@interface ViewController ()
@property (nonatomic, weak) NSMutableString *s;
@end
2018-11-25 23:48:42.074899+0800 TestApp[2293:134817] 初始化时 内容:temp:new,s:(null)
2018-11-25 23:48:42.075094+0800 TestApp[2293:134817] 初始化时 地址:temp:0x1c4250b60,s:0x0
2018-11-25 23:48:42.075557+0800 TestApp[2293:134817] 初始化时 引用计数:temp:1,s:(null)
2018-11-25 23:48:42.075742+0800 TestApp[2293:134817] 赋值后 内容:temp:new,s:new
2018-11-25 23:48:42.075844+0800 TestApp[2293:134817] 赋值后 地址:temp:0x1c4250b60,s:0x1c4250b60
2018-11-25 23:48:42.075985+0800 TestApp[2293:134817] 赋值后 引用计数:temp:1,s:2
2018-11-25 23:48:42.076091+0800 TestApp[2293:134817] 修改temp后 内容:temp:newtemp,s:newtemp
2018-11-25 23:48:42.076188+0800 TestApp[2293:134817] 修改temp后 地址:temp:0x1c4250b60,s:0x1c4250b60
2018-11-25 23:48:42.076321+0800 TestApp[2293:134817] 修改temp后 引用计数:temp:1,s:2
2018-11-25 23:48:42.076422+0800 TestApp[2293:134817] 修改s后 内容:temp:newtemps,s:newtemps
2018-11-25 23:48:42.076518+0800 TestApp[2293:134817] 修改s后 地址:temp:0x1c4250b60,s:0x1c4250b60
2018-11-25 23:48:42.077905+0800 TestApp[2293:134817] 修改s后 引用计数:temp:1,s:2
2018-11-25 23:48:42.078011+0800 TestApp[2293:134817] 置nil后 内容:temp:(null),s:(null)
2018-11-25 23:48:42.078105+0800 TestApp[2293:134817] 置nil后 地址:temp:0x0,s:0x0
2018-11-25 23:48:42.078195+0800 TestApp[2293:134817] 置nil后 引用计数:temp:(null),s:(null)
- copy
@interface ViewController ()
@property (nonatomic, copy) NSMutableString *s;
@end
日志
2018-11-25 23:49:28.029404+0800 TestApp[2298:135220] 初始化时 内容:temp:new,s:(null)
2018-11-25 23:49:28.029613+0800 TestApp[2298:135220] 初始化时 地址:temp:0x1c4255540,s:0x0
2018-11-25 23:49:28.030087+0800 TestApp[2298:135220] 初始化时 引用计数:temp:1,s:(null)
2018-11-25 23:49:28.030216+0800 TestApp[2298:135220] 赋值后 内容:temp:new,s:new
2018-11-25 23:49:28.030316+0800 TestApp[2298:135220] 赋值后 地址:temp:0x1c4255540,s:0xa0000000077656e3
2018-11-25 23:49:28.030591+0800 TestApp[2298:135220] 赋值后 引用计数:temp:1,s:18446744073709551615
2018-11-25 23:49:28.030705+0800 TestApp[2298:135220] 修改temp后 内容:temp:newtemp,s:new
2018-11-25 23:49:28.030801+0800 TestApp[2298:135220] 修改temp后 地址:temp:0x1c4255540,s:0xa0000000077656e3
2018-11-25 23:49:28.030941+0800 TestApp[2298:135220] 修改temp后 引用计数:temp:1,s:18446744073709551615
2018-11-25 23:49:28.031328+0800 TestApp[2298:135220] -[NSTaggedPointerString appendFormat:]: unrecognized selector sent to instance 0xa0000000077656e3
2018-11-25 23:49:28.040986+0800 TestApp[2298:135220] *** Terminating app due to uncaught exception 'NSInvalidArgumentException', reason: '-[NSTaggedPointerString appendFormat:]: unrecognized selector sent to instance 0xa0000000077656e3'
*** First throw call stack:
(0x1860cbd38 0x1855e0528 0x1860d91f8 0x1860d16e4 0x185fb70dc 0x100076084 0x18f657ae0 0x18f657978 0x18f50e38c 0x18f509a70 0x18f4db078 0x1a4dabb58 0x18fe1af98 0x18fe1d408 0x18fe16574 0x186074358 0x1860742d8 0x186073b60 0x186071738 0x185f922d8 0x187e23f84 0x18f53e880 0x1000764f0 0x185ab656c)
libc++abi.dylib: terminating with uncaught exception of type NSException
(lldb)
参考地址:
苹果官方
iOS探究
What's the difference between the atomic and nonatomic attributes?