当前位置: 首页 > news >正文

【OC】属性关键字

文章目录

  • 前言
  • 属性关键字
    • @property
    • @synthesize
    • @dynamic
    • 原子操作
      • atomic
      • nonatomic
    • 读写权限
      • readwrite
      • readonly
    • 内存管理
      • weak
      • assign
      • retain
      • strong
      • copy
      • copy和strong对比
  • 深浅拷贝
    • 容器类对象的深拷贝
      • 完全深拷贝
      • 递归深拷贝
  • 总结

前言

属性关键字是oc中基础且相当重要的知识点,之前学的时候有所接触但学的没有那么深入,这次便深入介绍一下其相关内容

属性关键字

@property

用来声明属性,编译器会默认:

  1. 自动生成一个实例变量(ivar),名字为**_属性名**
  2. 自动生成对应的getter/seter方法的声明与实现

@synthesize

以前必须写这个才能生成该实例变量与对应的方法,现在现在只需要经过@property声明,编译器便可以自动合成实例变量和方法。

  • 现在可以通过这个来自定义实例变量的名字
@synthesize name = _myName; // 属性name对应的ivar叫_myName

@dynamic

这个是让我们的编译器不用自动进行@synthesize,其不会影响到@property所自动生成的getter,setter方法的声明。

编译器会自动帮我们生成实例变量和存取方法,但是还有几个得写@synthesize的例外:

  • 如果我们自己实现了getter和setter,就不会自动帮我们生成,若我们想要一个ivar,就需要@synthesize
  • 若该属性为readonly,且我们自己写了getter,若我们想要一个ivar,就需要@synthesize
  • 在协议里的属性,协议只声明,不会生成ivar,所以在遵循协议的类里,若我们要ivar,则必须要@synthesize,不写@synthesize,就必须自己实现setter与getter,否则编译报错

原子操作

属性是否有原子性可以理解为线程是否安全

atomic

原子性,加同步锁,是默认的修饰符,用这个会消耗性能,仅可以保证单词访问是原子的,且不一定能保证线程安全,若想保证线程安全,可以用其他锁的机制(此处涉及到了多线程的部分知识,以后学习之后再进行补写)

nonatomic

非原子性,无同步保护,声明属性时基本为这个,可以提高访问性能

读写权限

默认为readwrite

readwrite

可读可写,属性拥有setter和getter方法

readonly

只读,仅提供了getter方法

内存管理

weak

只可用来修饰对象类型,ARC下才可以使用来修饰弱引用,不增加所修饰对象的引用计数,主要可以用来避免循环引用,所修饰的对象被释放后,会自动将指针指向nil,不会产生悬垂指针(即指向已被释放或者无效的指针)

assign

一般用于修饰基本数据类型(也可以用来修饰对象,但是在销毁时可能会产生悬垂指针,从而出现crash,即程序崩溃),setter方法是实现直接赋值,修饰如NSInteger,BOOL,int,float等的基本数据类型,且使用其修饰对象时时也不增加引用计数

retain

在MRC下使用,现在ARC下基本使用strong,主要用来修饰强引用,会增加引用计数,原理是用release释放之前的旧值,并调用retain,引用计数+1,最后再设置新值把原指针指向新的对象,现在基本不用所以不给出详解

strong

在ARC下使用,用来修饰强引用,原理与retain一样,但是ARC下编译器帮我们做了retain+release的操作

在修饰block时,strong相当于copy,retain相当于assign,原理如下:

block在内存中区域比较独特,在创建时其位于栈区,是临时的,函数返回后即会被销毁,但是在copy或者ARC下的strong修饰后会把block从栈区拷贝到堆区,类似于普通对象一样,可受到引用计数的管理,且只要强引用持有,其就会一直存在

copy

指定属性为拷贝对象的引用,而不是引用所拷贝的原始的对象

copy和strong对比

copy:深拷贝,内存地址不同,指针地址不同,release旧值,copy新值

strong:浅拷贝,内存地址不变指针地址不同

若声明的属性用的是copy,在合成方法时会使用类的copy方法,所以在此时,即便用的是可变的实例变量,生成的也会是一个不可变的副本,确保了对象不会无意间的被改动

深浅拷贝

对象复制及其中的深浅拷贝的内容在之前已经有所讲解,【oc】Foudation框架–字符串,对象复制详解,但是讲解的有很多当时没有学习的东西所以在这里进行部分补充,这里先展示之前的介绍的相当全面的一张表格:

被拷贝的对象类型拷贝方法是否新容器/新对象是否复制内部元素拷贝类型
非容器类的不可变对象copy否(返回自身)-浅拷贝
非容器类的不可变对象mutableCopy是(新对象)深拷贝
非容器类的可变对象copy是(不可变新对象)是(新对象)深拷贝
非容器类的可变对象mutableCopy是(可变新对象)是(新对象)深拷贝
容器类的不可变对象copy否(返回自身)❌(不复制元素)浅拷贝
容器类的不可变对象mutableCopy是(新容器)❌(不复制元素)深拷贝(容器里元素为浅层)
容器类的可变对象copy是(不可变新容器)❌(不复制元素)深拷贝(容器里元素为浅层)
容器类的可变对象mutableCopy是(新容器)❌(不复制元素)深拷贝(容器里元素为浅层)

先回顾一下浅拷贝本质指的就是创建一个存放与所复制对象的地址相同的指针变量(固也称为指针拷贝),深拷贝指的是创建一个与被复制对象的值完全相同的对象,且他们的地址也不同,所以也叫做内容拷贝

容器类对象的深拷贝

在上面的表格中我们能看出,对容器类进行深拷贝时都只是进行了单层的深拷贝,即只是对容器进行了深拷贝了一份新的内存,但是里面的元素都是浅拷贝,未复制元素本身的内容,于是就有了下面这两种真正的深拷贝

完全深拷贝

对容器本身拷贝一份,且容器里的元素也一一拷贝,使用initWithXxxx: copyItems: YES方法

代码示例:

NSMutableString *str = [NSMutableString stringWithFormat:@"deepcopy"];NSArray *array = @[str];NSArray *arraycopy = [[NSArray alloc] initWithArray: array copyItems: YES];NSLog(@"%p - %p", array[0], arraycopy[0]);NSLog(@"%p - %p", array, arraycopy);

运行结果:

请添加图片描述

但是,当容器里面的元素仍然是容器的时候,我们使用这个方法,在里面的容器的里面的元素仍然是浅拷贝,里面的容器地址是深拷贝后的新地址

代码示例:

NSMutableString *str = [NSMutableString stringWithFormat:@"deepcopy"];NSArray *array = @[str, @[@"nested copy"]];NSArray *arraycopy = [[NSArray alloc] initWithArray: array copyItems: YES];NSLog(@"%p - %p", array, arraycopy);NSLog(@"%p - %p", array[0], arraycopy[0]);NSLog(@"%p - %p", array[1][0], arraycopy[1][0]);

运行结果:

请添加图片描述

显然,里面的容器的里面的元素仍然是浅拷贝,里面容器里的元素的地址仍然是原里面容器的元素的地址,这个时候,就要用到下面的递归深拷贝

递归深拷贝

在容器中嵌套容器时,需要递归的拷贝所有子容器及其内部元素,即要用到归档/解档的方法来进行递归深拷贝,这才是最彻底的深拷贝

代码示例:

NSArray *array01 = @[@[@"1", @"2"], @"3"];//进行安全归档NSData *data = [NSKeyedArchiver archivedDataWithRootObject: array01 requiringSecureCoding: YES error: nil];
//进行安全解档NSArray *array02 = [NSKeyedUnarchiver unarchivedObjectOfClass: [NSArray class] fromData: data error: nil];NSLog(@"%p - %p", array01[0][1], array02[0][1]);

运行结果:

请添加图片描述

总结

这次算是学习了当时没学到的部分属性关键字和深浅拷贝的更全面的内容,以后学到新的还会继续补充

http://www.xdnf.cn/news/1448731.html

相关文章:

  • vtk资料整理
  • Linux arm64 PTE contiguous bit
  • linux可以直接用指针操作物理地址吗?
  • torch学习 自用
  • python类的内置属性
  • AI重塑SaaS:从被动工具到智能角色的技术演进路径
  • 【面试题】OOV(未登录词)问题如何解决?
  • Leetcode_202.快乐数_三种方法解决(普通方法解决,哈希表解决,循环链表的性质解决_快慢指针)
  • 简述:普瑞时空数据建库软件(国土变更建库)之一(变更预检查部分规则)
  • PyTorch 中训练语言模型过程
  • 利用 Java 爬虫获取淘宝商品详情 API 接口
  • 嵌入式学习day41-硬件(2)
  • ansible总结2
  • 代码随想录算法训练营第一天 | 704.二分查找 27. 移除元素 977.有序数组的平方
  • python中`__annotations__` 和 `inspect` 模块区别??
  • 两个子进程之间使用命名pipe
  • 从月薪5K到年薪60W!API自动化测试如何让你突破职业瓶颈
  • K8S 部署 NFS Dynamic Provisioning(动态存储供应)
  • 【STM32】STM32F103系列USB大坑 二
  • 具身智能让人形机器人 “活” 起来:懂语言、能感知、会行动,智能进化再提速
  • 使用langgraph创建工作流系列4:人机回环
  • 面试复习题-Flutter
  • 论文介绍:“DUSt3R”,让 3D 视觉从“繁琐”走向“直观”
  • Swift 解法详解:LeetCode 370《区间加法》
  • 《网络安全实战:CC攻击(应用层)与DDoS攻击(网络层)的底层逻辑与防御体系》​
  • 分发饼干——很好的解释模板
  • 从“看见”到“行动”:一场机器视觉与机器人的软硬件共舞
  • 把本地win11系统打包成镜像并安装到vmware中
  • Springboot3+SpringSecurity6Oauth2+vue3前后端分离认证授权-授权服务
  • FastVLM:高效视觉编码助力视觉语言模型突破高分辨率效率瓶颈