【iOS】——属性关键字

news2024/11/13 15:11:02

属性关键字的类型

在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属性,分别为可变和不可变类型:

@property(nonatomic,strong)NSString * Strstrong;
@property(nonatomic,copy)NSString * Strcopy;
@property(nonatomic,copy)NSMutableString * MutableStrcopy;
@property(nonatomic,strong)NSMutableString * MutableStrstrong;`

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

```
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关键字都是进行的浅拷贝,即对象内存地址不变,指针地址改变

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

```
NSMutableString * OriginalMutableStr = [NSMutableString stringWithFormat:@"我已经开始测试了"];
self.Strcopy = OriginalMutableStr;
self.Strstrong = OriginalMutableStr;
self.MutableStrcopy = OriginalMutableStr;
self.MutableStrstrong = OriginalMutableStr;
```

在这里插入图片描述

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

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

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

```
[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对象的内存地址不同,但是内部元素的内存地址不变)
    [array copy];
    [array  mutableCopy];

  • 双层深拷贝:这里的双层指的是完成了NSArray对象和NSArray容器内对象的深copy(为什么不是完全,是因为无法处理NSArray中还有一个NSArray这种情况)使用:
    [[NSArray alloc] initWithArray:arr copyItems:YES]

  • 完全深拷贝:完美的解决NSArray嵌套NSArray这种情形,可以使用归档、解档的方式可以使用:

        [NSKeyedUnarchiver unarchiveObjectWithData:[NSKeyedArchiver archivedDataWithRootObject:testArr]];
    
    

问题总结

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

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

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

  • 可变对象(mutable)copy和mutableCopy都是深拷贝
  • 不可变对象(immutable)的copy是浅拷贝,mutableCopy是深拷贝

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1943705.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

RDMA通信4:MR(Memory Region, 内存区域)基本概念和作用

MR简介 RDMA通信中MR(Memory Region)指的是由RDMA软件层在内存中规划出的一片区域,用于存放收发的数据。IB协议中,用户在申请完用于存放数据的内存区域之后,都需要通过调用IB框架提供的API注册MR,才能让RDMA网卡访问这片内存区域…

Linux常用特殊符号

1、 > 和 >> 用法 这两个符号> 和 >> 主要区别如下: 符号>:覆盖现有文件,如果目录中不存在上述名称的文件,则创建一个文件。 符号>>:追加现有文件,如果目录中不存在上述名称的文…

Qt/C++音视频开发79-采集websocket视频流/打开ws开头的地址/音视频同步/保存到MP4文件/视频回放

一、前言 随着音视频的爆发式的增长,各种推拉流应用场景应运而生,基本上都要求各个端都能查看实时视频流,比如PC端、手机端、网页端,在网页端用websocket来接收并解码实时视频流显示,是一个非常常规的场景&#xff0c…

达梦数据库迁移DTS表对比

达梦数据库迁移DTS表对比 背景 达梦数据库迁移mysql2dm,使用dts迁移后的表数据精度不对,需要修正。网上很难搜到,特此记录。 DTS对比数据 1.创建对比 2.设置源库和目标库 3.选择对比选项 4.指定模式 5.指定对象 选择添加源确定 点击配置…

AI学习记录 -使用react开发一个网页,对接chatgpt接口,附带一些英语的学习prompt

实现了如下功能(使用react实现,原创) 实现功能: 1、对接gpt35模型问答,并实现了流式传输(在java端) 2、在实际使用中,我们的问答历史会经常分享给他人,所以下图的 copy …

OCC 扫掠

目录 一、概述 1、扫掠类型分类 2、扫掠方式分类 二、详细介绍 1. 直线扫掠(Linear Sweep) 2. 旋转扫掠(Revolution) 3. 一般路径扫掠(General Path Sweep) 三、总结 一、概述 OpenCASCADE (OCC) 中的扫掠(Sweeping)操作是一种常用的几何建模技术,用于通过沿着…

Java文件读取 jar包内文件读取

全文详见个人博客:https://www.coderli.com/jar-classloader/ Java文件读取 jar包内文件读取最近遇到一些Jar包内外配置文件读取的问题。索性自己测试总结一下,与大家分享。 主要是关于ClassLoader.getResource和Class.getResource方法路径区别的问题。…

python:切片

Python 中的切片(slicing)是一种强大的功能,它允许你获取序列(如列表、元组、字符串等)的一个子集。切片操作通过指定序列的起始索引、结束索引(可选)和步长(可选)来实现…

IOCTLance:一款针对x64 WDM驱动程序的漏洞检测工具

关于IOCTLance IOCTLance是一款针对x64 WDM驱动程序的漏洞检测工具,该工具来源于CODE BLUE 2023上展示的一个名为“使用符号执行和污点分析增强 WDM 驱动程序漏洞检测 ”的项目。该工具能够有效增强检测Windows驱动程序模型(WDM)驱动程序中各…

数模·插值和拟合算法

插值 将离散的点连成曲线或者线段的一种方法 题目中有"任意时刻任意的量"时使用插值,因为插值一定经过样本点 插值函数的概念 插值函数与样本离散的点一一重合 插值函数往往有多个区间,多个区间插值函数样态不完全一样,简单来说就…

动态住宅IP和静态住宅IP主要区别是什么?

在互联网连接的世界中,IP地址是我们识别和访问网络资源的关键。住宅IP地址,特别是动态住宅IP和静态住宅IP,是两种不同类型的IP分配方式,它们在使用和功能上存在显著差异。 1. IP地址的稳定性 动态住宅IP:这种IP地址是…

redis命令超详细

redis数据结构介绍 redis是一个key-value的数据库,key一般是String类型,但是value的类型有很多: 基本类型:String,Hash,List,Set,SortedSet(可排序的不能重复的集合) 特殊类型:GEO,BitMap,HyperLog等 文档官网&…

CSS3雷达扫描效果

CSS3雷达扫描效果https://www.bootstrapmb.com/item/14840 要创建一个CSS3的雷达扫描效果,我们可以使用CSS的动画(keyframes)和transform属性。以下是一个简单的示例,展示了如何创建一个类似雷达扫描的动画效果: HTM…

Python -numpy 基础-------1

NumPy(Numerical Python)是Python的一个开源数值计算扩展库。它支持大量的维度数组与矩阵运算,此外也针对数组运算提供大量的数学函数库。NumPy的数组(ndarray)对象是一个快速且灵活的多维数组对象,用于存储…

power bi-all 相关函数

power bi-all 相关函数 1. all 函数2. allselected 函数3. allexcept()函数4. allnoblankrow() 函数 1. all 函数 作用: 清除筛选返回:清除筛选后的表格或列总结 当all参数为表时,忽略所有的筛选条件,无论…

github连接不上的解决方案(持续更新)

大家好,我是herosunly。985院校硕士毕业,现担任算法研究员一职,热衷于大模型算法的研究与应用。曾担任百度千帆大模型比赛、BPAA算法大赛评委,编写微软OpenAI考试认证指导手册。曾获得阿里云天池比赛第一名,CCF比赛第二名,科大讯飞比赛第三名。授权多项发明专利。对机器学…

鸿蒙仓颉语言【模块module】

module 模块 模块配置文件,这里指项目的modules.json 文件,用于描述代码项目的基础元属性。 {"name": "file name", //当前项目的名称"description": "项目描述", //项目描述"version": "1.0…

十、Docker版Redis集群搭建

目录 一、3主3从Redis集群配置 1、新建6个docker容器实例 2、进入容器redis-node-1并为6台机器构建集群关系 3、以6381为切入点,查看集群状态 二、主从容错切换迁移案例 1、数据读写存储 2、假如6381宕机了,他的从库6386会不会切换 三、主从扩容案…

(C语言) 文件读写基础

文章目录 🗂️前言📄ref📄访问标记🗃️文件访问标记 🗂️Code📄demo📄分点讲解🗃️打开/关闭🗃️写🗃️读 🗂️END🌟关注我 &#x1f…

Linux-socket详解

前言 OSI七层模型和TCP/IP四层模型在这里就不说了。 套接字(socket)是一个抽象层,应用程序可以通过它发送或接收数据,可对其进行像对文件一样的打开、读写和关闭等操作。套接字允许应用程序将IO插入到网络中,并与网络…