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 <Foundation/Foundation.h>
@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<UITouch *> *)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?

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
禁止转载,如需转载请通过简信或评论联系作者。
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 216,324评论 6 498
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 92,356评论 3 392
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 162,328评论 0 353
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 58,147评论 1 292
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 67,160评论 6 388
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 51,115评论 1 296
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 40,025评论 3 417
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 38,867评论 0 274
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 45,307评论 1 310
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,528评论 2 332
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,688评论 1 348
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,409评论 5 343
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 41,001评论 3 325
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,657评论 0 22
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,811评论 1 268
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 47,685评论 2 368
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,573评论 2 353

推荐阅读更多精彩内容