iOS基础之属性@property

本篇寄语:
我们还有很多事情要做, 该来的挡也挡不住 。 会越来越好的, 还有什么不开心的呢? 该工作的工作, 该睡觉的睡觉, 还是一样的啊, 是你的自然不会走。

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
  1. Attention:
    assignunsafe_unretained 修饰对象时,如果当所赋的新值引用计数为0对象被销毁时属性并不知道,编译器不会将该属性置为nil,指针仍旧指向之前被销毁的内存,这时访问该属性会产生野指针错误并崩溃,所以慎用着两个属性
  2. 关于copy 主要用来修饰子类有可变子类的对象(NSString/NSMutableString,NSArray/NSMutableArray,NSDictionary/NSMutableDictionary)。该类对象需要遵守NSCopyingNSMutableCopying
  3. 如果用copy 修饰可变对象,会让可变对象变为不可变对象。如果用copy 修饰可变对象,会让可变对象变为不可变对象。
  4. 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
  1. 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
  1. 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
  1. 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

  1. 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

  1. 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)

  1. 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?

你可能感兴趣的:(iOS基础之属性@property)