【iOS】——属性关键字

属性关键字的类型

在iOS中属性关键字分为四种类型:

  • 可访问性: readonly ,readwrite
  • 原子性 : atomic ,nonatomic
  • 内存管理 : retain/strong/copy, assign/unsafe_unretained,weak
  • 方法命名:setter,getter

1.可访问性

readonly:表示只读属性,只会生成getter方法 ,不会生成setter方法,无法通过 setter 方法进行修改。

readwrite:与 readonly 相对,表示读写属性,会生成getter和setter方法,可以通过 setter 方法修改属性值。

2.原子性

atomic:提供了一定程度的线程安全性,确保对属性的读取和写入操作是原子的,即在同一时间只有一个线程可以访问属性。这是通过给属性的访问器方法(getter 和 setter)加锁来实现的。

nonatomic:属性的访问是非原子的,不提供线程安全保护。在多线程环境下,多个线程同时访问和修改属性可能会导致数据不一致的情况。nonatomic 通常具有更高的性能,因为不需要额外的加锁和同步操作。

atomic只针对属性的 getter/setter 方法进行加锁,所以安全只是针对getter/setter方法来说,并不是整个线程安全,因为一个属性并不只有 setter/getter 方法,例:(如果一个线程正在getter 或者 setter时,有另外一个线程同时对该属性进行release操作,如果release先完成,会造成crash)

3.内存管理:

weak

用于表示对对象的弱引用,不会增加对象的引用计数。当所引用的对象被释放时,弱引用会自动置为 nil。

在ARC环境下,为避免循环引用,往往会把delegate属性用weak修饰;在MRC下使用assign修饰。当一个对象不再有strong类型的指针指向它的时候,它就会被释放,即使还有weak型指针指向它,那么这些weak型指针也将被清除。

assign

经常用于非指针变量,用于基础数据类型 (例如NSInteger)和C数据类型(int, float, double, char, 等),另外还有id类型。用于对基本数据类型进行复制操作,不更改引用计数。

也可以用来修饰对象,但是,被assign修饰的对象在释放后,指针的地址还是存在的,也就是说指针并没有被置为nil,成为野指针

之所以可以修饰基本数据类型,因为基本数据类型一般分配在栈上,栈的内存会由系统自动处理,不会造成野指针。MRC下的delegate往往assgin,此操作是为了deletage和self等自身产生循环引用。

weak 和 assign 的区别:

修饰的对象:weak修饰oc对象类型的数据,assign用来修饰是非指针变量。

引用计数:weak 和 assign 都不会增加引用计数。

释放:weak 修饰的对象释放后,指针地址自动设置为 nil,assign修饰的对象释放后指针地址依然存在,成为野指针。

修饰delegate 在MRC使用assign,在ARC使用weak。

strong

修饰一些OC对象类型的数据如:(NSNumber,NSString,NSArray、NSDate、NSDictionary、模型类等),strong是强引用,在ARC下等于retain,这一点区别于weak。

strong 是我们通常所说的指针拷贝(浅拷贝),内存地址保持不变,只是产生了一个新的指针,新指针和引用对象的指针指向同一个内存地址,没有生成新的对象,多了一个指向该对象的指针。

由于使用的是一个内存地址,当该内存地址存储的内容发生变更的时候,会导致属性也跟着变更

copy

用于修饰OC对象类型的数据,在调用setter方法给成员变量赋值时,会将被赋值的对象生成一个副本,然后将该副本赋值给成员变量。

在MRC,用来修饰block,因为block需要从栈区copy到堆区,在ARC,系统自动给我们做了这个操作,所一现在使用strong或者copy来修饰block都是可以的。

copy和strong都是属于强引用,都会让属性的引用计数加一,但是copy和strong不同点在于,它所修饰的属性当引用一个属性值时,是内存拷贝(深拷贝),就是在引用是,会生成一个新的内存地址和指针地址,和引用对象完全没有相同点,因此它不会因为引用属性的变更而改变。

copy与strong的区别(深拷贝 浅拷贝):

copy:内存拷贝-深拷贝,内存地址不同,指针地址也不同。
storng: 指针拷贝-浅拷贝,内存地址不变,指针地址不同

声明两个copy属性,两个strong属性,分别为可变和不可变类型:

objective-c 复制代码
@property(nonatomic,strong)NSString * Strstrong;
@property(nonatomic,copy)NSString * Strcopy;
@property(nonatomic,copy)NSMutableString * MutableStrcopy;
@property(nonatomic,strong)NSMutableString * MutableStrstrong;`

用不可变对象对属性进行赋值:

objective-c 复制代码
```
NSString * OriginalStr = @"我已经开始测试了";
//对 不可变对象赋值 无论是 strong 还是 copy 都是原地址不变,生成一个新指针指向对象(浅拷贝)
self.Strcopy = OriginalStr;
self.Strstrong = OriginalStr;
self.MutableStrcopy = OriginalStr;
self.MutableStrstrong = OriginalStr;
NSLog(@"rangle=>%@\n normal:copy=>%@=====strong=>%@\nMutable:copy=>%@=====strong=>%@",OriginalStr,_Strcopy,_Strstrong,_MutableStrcopy,_MutableStrstrong);
NSLog(@"rangle=>%p\n normal:copy=>%p=====strong=>%p\nMutable:copy=>%p=====strong=>%p",OriginalStr,_Strcopy,_Strstrong,_MutableStrcopy,_MutableStrstrong);
NSLog(@"rangle=>%p\n normal:copy=>%p=====strong=>%p\nMutable:copy=>%p=====strong=>%p",&OriginalStr,&_Strcopy,&_Strstrong,&_MutableStrcopy,&_MutableStrstrong);
```

strong修饰的对象和copy修饰的对象都是在引用一个对象的时候,内存地址是一样的,只有指针地址不同。

所以对于不可变对象进行赋值,使用strong和copy关键字都是进行的浅拷贝,即对象内存地址不变,指针地址改变

用可变对象对属性进行赋值:

objective-c 复制代码
```
NSMutableString * OriginalMutableStr = [NSMutableString stringWithFormat:@"我已经开始测试了"];
self.Strcopy = OriginalMutableStr;
self.Strstrong = OriginalMutableStr;
self.MutableStrcopy = OriginalMutableStr;
self.MutableStrstrong = OriginalMutableStr;
```

strong修饰的属性内存地址依然没有改变,但是copy修饰的属性内存值产生了变化

对可变对象赋值 strong 是原地址不变,引用计数+1(浅拷贝)。 copy是生成一个新的地址和对象,生成一个新指针指向新的内存地址(深拷贝)

此时修改一下OriginalMutableStr的值,看看结果:

objective-c 复制代码
```
[OriginalMutableStr appendFormat:@"改变了"];
```

当改变原来的值后strong修饰的属性的内容也跟着改变了,而copy修饰的属性的内容没有发生改变。

由于OriginalMutableStr是可变类型,是在原有内存地址上进行修改,无论是指针地址和内存地址都没有被改变,只是当前内存地址所存放的数据进行改变。

由于 strong 修饰的属性虽然指针地址不同,但是指针是指向原内存地址的,所以会跟着 OriginalMutableStr 的改变而改变。

copy修饰的类型不仅指针地址不同,而且指向的内存地址也和OriginalMutableStr 不一样,所以不会跟着 OriginalMutableStr 的改变而改变。

使用self.Strcopy 和 _Strcopy 来赋值也是两个不一样的结果,因为后者没有调用 set 方法,而 copy 和 strong 之所以会产生差别就是因为在 set 方法中,copy修饰的属性: 调用了 _Strcopy = [Strcopy copy] 方法。

多种copy模式:copy 和 mutableCopy 对容器对象 进行操作

在对容器对象(NSArray)进行copy操作时,分为多种:

  • copy:仅仅进行了指针拷贝
  • mutableCopy:进行内容拷贝这里的单层指的是完成了NSArray对象的深copy,而未对其容器内对象进行处理使用(NSArray对象的内存地址不同,但是内部元素的内存地址不变)
objective-c 复制代码
    [array copy];
    [array  mutableCopy];
  • 双层深拷贝:这里的双层指的是完成了NSArray对象和NSArray容器内对象的深copy(为什么不是完全,是因为无法处理NSArray中还有一个NSArray这种情况)使用:
objective-c 复制代码
    [[NSArray alloc] initWithArray:arr copyItems:YES]
  • 完全深拷贝:完美的解决NSArray嵌套NSArray这种情形,可以使用归档、解档的方式可以使用:

    objective-c 复制代码
        [NSKeyedUnarchiver unarchiveObjectWithData:[NSKeyedArchiver archivedDataWithRootObject:testArr]];

问题总结

  1. NSMutableArray用copy修饰会出现什么问题?

出现调用可变方法不可控问题,会导致程序崩溃。对于可变对象使用copy关键字会进行深拷贝,返回一个不可变的对象,对不可变的对象调用可变方法就会crash

2.copy关键字影响了对象的可变和不可变属性吗?

  • 可变对象(mutable)copy和mutableCopy都是深拷贝
  • 不可变对象(immutable)的copy是浅拷贝,mutableCopy是深拷贝
相关推荐
HarderCoder3 小时前
iOS 知识积累第一弹:从 struct 到 APP 生命周期的全景复盘
ios
goodSleep7 小时前
更新Mac OS Tahoe26用命令恢复 Mac 启动台时不小心禁用了聚焦搜索
macos
叽哥14 小时前
Flutter Riverpod上手指南
android·flutter·ios
用户092 天前
SwiftUI Charts 函数绘图完全指南
ios·swiftui·swift
YungFan2 天前
iOS26适配指南之UIColor
ios·swift
权咚2 天前
阿权的开发经验小集
git·ios·xcode
用户092 天前
TipKit与CloudKit同步完全指南
ios·swift
小溪彼岸2 天前
macOS自带截图命令ScreenCapture
macos
法的空间3 天前
Flutter JsonToDart 支持 JsonSchema
android·flutter·ios
2501_915918413 天前
iOS 上架全流程指南 iOS 应用发布步骤、App Store 上架流程、uni-app 打包上传 ipa 与审核实战经验分享
android·ios·小程序·uni-app·cocoa·iphone·webview