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

OC-属性关键字

深浅拷贝

OC对象的拷贝方式分为浅拷贝和深拷贝两种方式

440295467a24d549bea18f43f0259b18

浅拷贝

本质就是创建一个存放与复制对象相同地址的指针变量,所以也称为指针拷贝

深拷贝

创建一个与被复制对象的值完全相同的对象,但是他们的地址是不同的,所以也称为内容拷贝

对于可变类与非可变类他们的深浅拷贝总结下来就是,不可变类的copy是浅拷贝,mutablecopy是深拷贝。可变类的copy和mutablecopy都是深拷贝

整理

可变对象 -> copy :深拷贝 -> 不可变对象

可变对象 -> mutableCopy : 深拷贝 -> 可变对象

不可变对象 -> copy : 浅拷贝 -> 不可变对象

不可变对象 -> mutableCopy : 深拷贝 -> 可变对象

自定义类的深浅拷贝

  • 自定义类的拷贝必须实现NSCopying与NSMutableCopying协议

  • 自定义对象实现copy与mutableCopy都是深拷贝

NSCopying协议的标准写法

- (id)copyWithZone:(NSZone *)zone {Coffee* coffee = [[[self class] allocWithZone:zone] init];coffee.name = [self.name copy];return coffee;;
}

思考

为什么使用self.class而不是直接写Coffee类?这是为了防止如果后续有子类继承了这个类,但是没有重写copyWithZone:那么父类的实现依然会被调用,使用[self class]可以保证正确生成当前运行时类的实例。而不是Coffee实例对象

归档与解档代码实现

实现解档与归档:之前有了解过,这里再回顾一遍加深印象

关键在与为自定义类实现两个方法,如下

#import "Coffee.h"
​
@implementation Coffee
​
+ (BOOL)supportsSecureCoding {return YES;
}
​
- (void)encodeWithCoder:(NSCoder *)coder {[coder encodeObject:self.name forKey:@"name"];
}
​
- (instancetype)initWithCoder:(NSCoder *)coder {if (self = [super init]) {_name = [coder decodeObjectOfClass:[NSString class] forKey:@"name"];}return self;
}
​
- (id)copyWithZone:(NSZone *)zone {Coffee* coffee = [[[self class] allocWithZone:zone] init];coffee.name = [self.name copy];return coffee;;
}
@end
​//使用
NSData* data = [NSKeyedArchiver archivedDataWithRootObject:coffee requiringSecureCoding:YES error:nil];
​
Coffee* copyCoffee = [NSKeyedUnarchiver unarchivedObjectOfClass:[Coffee class] fromData:data error:nil];

容器类拷贝

单层深拷贝

使用initWithXxx: copyItems:YES方法

int main(int argc, const char * argv[]) {@autoreleasepool {NSMutableString* str = [NSMutableString stringWithString:@"hi"];NSArray* src = @[str];NSArray* dst = [[NSArray alloc] initWithArray:src copyItems:YES];NSLog(@"%p - %p", src[0],dst[0]);NSLog(@"%p - %p", src, dst);}return 0;
}

我们打印结果如下:

发现当容器里面没有容器时,里面一层的元素被深拷贝了,那么如果容器里面的容器的元素呢?我们试一下

int main(int argc, const char * argv[]) {@autoreleasepool {NSMutableString* s = [NSMutableString stringWithString:@"hi"];NSArray* arr = @[s, @[@"hello"]];NSArray* newArr = [[NSArray alloc] initWithArray:arr copyItems:YES];NSLog(@"%p - %p", arr[1][0], newArr[1][0]);}return 0;
}

我们打印结果如下:

显然,里面容器的元素并为被深拷贝,依旧是指针引用

完全深拷贝

当我们想要容器与所有层级的元素都变成彼此独立的新对象时,常用两种方法,一种就是上面的解档归档方法,最方便,代码上面有讲解

属性关键字

通过@property、@synthesized、@dynamic这三个关键字实现属性自动合成存取方法

@property

自动生成属性的setter与getter方法的声明,用于封装对象中的数据,属性本质是ivar + setter + getter

@synthesize

帮我们自动生成setter与getter方法的实现以及合成实例变量

@dynamic

告诉编译器不用自动进行@synthesized,会在运行时提供这些方法的实现,无需产生警告

现在的版本已经不需要在单独写@synthesize了。但是需要注意下面几点

  • 如果重写了setter与getter方法,则编译器就不会自动为这个@property添加@synthesize

  • 如果该属性是readonly,那么只要从写了getter方法,property autosynhesis就不会执行

  • 如果我们在协议中使用@property声明了一个属性,在某个类中遵循这个协议,我们就必须要在类中使用@synthesize来获取这个属性的成员变量,同时获得setter/getter的实现函数

#import <UIKit/UIKit.h>
​
@protocol Demand <NSObject>
- (void)clickButton;
@property(nonatomic, copy)NSString* name;
@end
​
@interface ViewController : UIViewController<Demand>
​
@end
​
​
#import "ViewController.h"
#import "Masonry.h"
@interface ViewController ()
@property (nonatomic, strong)UITableView* tableView;
@end
​
@implementation ViewController
@synthesize name = _name;
​
- (void)clickButton {NSLog(@"pop");
}

原子操作

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

atomic

原子性,加同步锁,是默认的修饰符,使用这个会消耗性能,也不一定保证线层安全,如果需要保证线程安全,需要使用其他锁机制

nonatomic

非原子性,不实用同步锁,声明属性时基本都设置为nonatomic,可以提高访问性能

读写权限

默认为readwrite,属性拥有setter与getter方法

readonly

只读,仅提供getter方法

内存管理

weak

ARC机制下才可以使用,修饰弱引用,不增加对象的引用计数,主要可以用于避免循环引用。weak修饰的对象在释放之后,会自动将指针置为nil,不会产生悬垂指针(指向已经被释放或失效的指针)

assign

既可以修饰基本数据类型,也可以修饰对象类型。setter实现是直接赋值,常用于基本数据类型。修饰对象类型时不增加其引用计数,会产生悬垂指针(其修饰的对象在被释放后,指针依然指向原对象地址,该指针成为悬垂指针,此时如果依然通过该指针访问原对象的话,就可能导致程序崩溃),之所以可以修饰基本数据类型,因为基本数据类型一般分配在栈上,栈的内存会由系统自动处理,不会造野指针。MRC下的delegate往往是assign,此操作是为了防止delegate和self等自身产生循环引用

__unsafe_unretain

与 _weak一样,唯一的区别在于对象即使被销毁,指针也不会被自动置为nil,此时指针指向的是一个无用的地址(悬垂指针),如果使用此地址,程序会抛出异常

下面这段代码可以很好的理解

id __unsafe_unretained temp = [[NSMutableArray alloc] init];
[temp addobject:@"pop"];

为什么会崩溃呢?因为适用这个修饰符的话,不会增加引用计数,并且也不会把指针置为nil,后面使用它的话就是在操作一个已经被释放的对象。

copy

用于修饰OC对象类型,大多是不可变类,在调用setter方法给成员变量赋值时,会先生成一个被复制对象的副本,在将该副本复赋值成员变量

retain
  • MRC下使用,ARC下基本使用strong

  • 修饰强引用,保留旧值,释放旧值,在设置新值,同时引用计数加一

  • setter方法实现如下,先release旧值,retain新值,用于OC对象

- (void)setObj:(NSObject)newObj {if (_obj != newObj) {[_obj release];_obj = [newObj retain];}
}
strong
  • ARC下使用,原理与retain相同,但是在修饰Block时,strong相当于copy,而retain相当于assign(跟block的特性有关,创建是在栈上,使用strong强引用的话,需要将block复制到堆区,延长生命周期)

copy和strong的对比
  • copy:深拷贝,内存地址不同,指针地址也不同,同样是release旧值,copy新值

  • strong:浅拷贝,内存地址不变,指针地址不同,逻辑同retain,(ARC)编译器会在合适的时候自动插入retain/release代码

  • 如果属性声明中指定了copy特性,合成方法会使用类的copy方法。属性并没有mutableCopy特性,即使是可变的实例变量,使用的也是copy特性。会生成一个不可变副本

strong和copy关键字的用法

@property属性用copy修饰不可变对象,用strong修饰可变对象

  • 常用的基本类型对应Foundation数据类型?

在声明一个属性时,尽量使用Foundation框架的数据类型,统一代码的数据类型

  • 定义属性的格式

@property (nonatomic, readwrite, copy) NSString* name;

按照原子操作、读写权限、内存管理排序

  • ARC下@property的默认属性

基本数据类型:atomic、readwrite、assign

OC对象:atomic、readwrite、strong

  • 注意事项

    • 避免使用copy来修饰可变对象

    • 如果一定要用copy的话,就重写setter方法

http://www.dtcms.com/a/359940.html

相关文章:

  • GEE 实战:计算 Landsat8 月均 NDVI 并导出(2013-2024)_后附完整代码
  • 【pve】
  • 秋招 AI 方向 —— 华为机考
  • 【学习笔记】LLM Interview(Agent相关)
  • 计算机视觉与深度学习 | 低照度图像处理算法综述:发展、技术与趋势
  • 大数据毕业设计选题推荐-基于大数据的大气和海洋动力学数据分析与可视化系统-Spark-Hadoop-Bigdata
  • (数组的定义与使用) 本篇目标 1. 理解数组基本概念 2. 掌握数组的基本用法 3. 数组与方法互操作 4. 熟练掌握数组相关的常见问题和代码
  • 同类软件对比(三):Python vs Anaconda vs Miniconda:深入解析与选择策略
  • 2025.8.18-2025.8.24第35周:备稿演讲有进步
  • Paimon——官网阅读:Spark 引擎
  • 【图论】Graph.jl 核心函数
  • 如何通过 AI IDE 集成开发工具快速生成简易留言板系统
  • Java面试-微服务(spring cloud篇)
  • 飞牛Docker部署免费frp内网穿透
  • RK3568平台开发系列讲解:瑞芯微平台4G模块篇移植
  • TFS-2005《A Possibilistic Fuzzy c-Means Clustering Algorithm》
  • 商业航天:中、美、欧“软件定义卫星” 路线全解析
  • Iterative loop of ML development|机器学习的迭代发展
  • JavaEE初阶网络原理-初识
  • PythonDay42
  • 提取动漫图像轮廓并拟合为样条曲线(MATLAB)
  • Mysql学习 Day3 Explain详解与索引优化
  • APB验证VIP Agent的各个组件之间的通信
  • SpringAI应用开发面试实录:核心技术、架构设计与业务场景全解析
  • React前端开发_Day12_极客园移动端项目
  • 解决 uni-app 中大数据列表的静默UI渲染失败问题
  • UniApp 基础开发第一步:HBuilderX 安装与环境配置
  • Wi-Fi技术——物理层技术
  • 【C++】构造函数初始化详解
  • 漫谈《数字图像处理》之基函数与基图像