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

「OC」源码学习——属性关键字

「OC」源码学习——属性关键字

属性的声明

@property (nonatomic, strong) NSString *name;

其实属性就是成员变量和自动编写的存取方法

Property 的组成

属性的作用

读写类的关键字

  • readonly,只读只生成相应的 getter 方法,以及带下划线的实例变量;
@property ( readonly ) int age;
  • readwrite,生成 setter 、getter 方法,以及带下划线的实例变量;
@property ( readwrite ) int age; 

原子性操作类关键字

1. atomic (默认)
  • 作用:保证属性访问的原子性

  • 实现原理:

    - (id)object {@synchronized(self) {return _object;}
    }
    
  • 特点:

    • 线程安全但性能较低
    • 不能完全避免线程问题
2. nonatomic
  • 作用:非原子访问,提高性能

  • 特点:

    • 访问速度比 atomic 快 10-20 倍
    • 需要开发者自行处理线程安全
  • 使用场景:

    @property (nonatomic) NSString *name; // 推荐在单线程环境使用
    

内存管理关键字

strong (默认)
  • 作用:创建强引用,增加对象的引用计数

  • 适用类型:对象类型

  • 实现原理:

    - (void)setObject:(id)newObject {[newObject retain];    // 保留新对象[_object release];     // 释放旧对象_object = newObject;   // 赋值
    }
    
  • 使用场景:

    @property (strong) NSString *name; // 推荐用于对象属性
    
weak
  • 作用:创建弱引用,不增加引用计数

  • 适用类型:对象类型

  • 特点:

    • 对象释放时自动置 nil
    • 避免循环引用
  • 实现原理:

    // 运行时自动注册到 weak_table
    objc_storeWeak(&_weakRef, object);
    
  • 使用场景:

    @property (weak) id delegate; // 代理模式
    
copy
  • 作用:创建对象的副本

  • 适用类型:实现了 NSCopying 协议的对象

  • 实现原理:

    - (void)setName:(NSString *)name {[_name release];_name = [name copy]; // 调用 copy 方法
    }
    
  • 使用场景:

    @property (copy) NSString *uniqueID; // 保护不可变对象
    
assign
  • 作用:直接赋值,不进行内存管理

  • 适用类型:基本数据类型、C 结构体

  • 风险:对象类型使用可能造成野指针

  • 使用场景:

    @property (assign) NSInteger count; // 基本数据类型
    
unsafe_unretained
  • 作用:类似 assign和weak的结合,但用于对象类型,不添加引用计数,比weak性能高些

  • 风险:对象释放后不置 nil

  • 使用场景:

    @property (unsafe_unretained) NSObject *legacyRef; // 兼容旧代码
    

可空性关键字

nullable
  • 作用:表示属性可以为 nil

  • 使用场景:

    @property (nullable) NSString *optionalName;
    
nonnull
  • 作用:表示属性不应为 nil

  • 使用场景:

    @property (nonnull) NSString *requiredID;
    

@property在runtime中的实现

struct property_t {const char *name;            // 属性名称const char *attributes;      // 属性特性字符串
};

特性字符串格式:"T[类型],[特性1],[特性2],...,V[变量名]"

示例分析:

// 声明
@property (nonatomic, copy) NSString *name;// 对应的 attributes 字符串:
"T@\"NSString\",C,N,V_name"
代码含义说明
T…类型后面跟着类型编码
Ccopy使用 copy 语义
&retain/strong使用引用计数保留
Nnonatomic非原子性
Rreadonly只读
Wweakweak 引用

设置@property的函数——reallySetProperty

static inline void reallySetProperty(id self,           // 目标对象(属性所属的实例)SEL _cmd,          // 设置方法的选择器(通常为 setXxx:)id newValue,        // 要设置的新值ptrdiff_t offset,   // 实例变量在对象内存中的偏移量bool atomic,        // 是否原子操作bool copy,          // 是否使用 copy 语义bool mutableCopy    // 是否使用 mutableCopy 语义
)
static inline void reallySetProperty(id self, SEL _cmd, id newValue, ptrdiff_t offset, bool atomic, bool copy, bool mutableCopy)
{// 1. 处理特殊情况:offset为0表示要设置的是isa指针(即改变对象的类)if (offset == 0) {object_setClass(self, newValue);return;}id oldValue; // 用于保存旧值,以便后续释放// 2. 计算实例变量在对象内存中的位置(slot指针)id *slot = (id*) ((char*)self + offset);// 3. 根据属性修饰符处理新值(copy/mutableCopy/retain)if (copy) {// 执行copy操作:调用copyWithZone:方法创建不可变副本newValue = [newValue copyWithZone:nil];} else if (mutableCopy) {// 执行mutableCopy操作:调用mutableCopyWithZone:方法创建可变副本newValue = [newValue mutableCopyWithZone:nil];} else {// 如果不是copy/mutableCopy,则进行retain操作(strong修饰)// 先检查新旧值是否相同,避免不必要的操作if (*slot == newValue) return; // 相同则直接返回// 对新值进行retain(增加引用计数)newValue = objc_retain(newValue);}// 4. 根据原子性要求(atomic/nonatomic)执行赋值操作if (!atomic) {// 非原子操作:直接赋值oldValue = *slot; // 保存旧值*slot = newValue; // 将新值赋给实例变量} else {// 原子操作:需要加锁保证线程安全// 4.1 获取与当前slot关联的自旋锁spinlock_t& slotlock = PropertyLocks[slot];// 4.2 加锁slotlock.lock();// 4.3 在锁保护下赋值oldValue = *slot;*slot = newValue;// 4.4 解锁slotlock.unlock();}// 5. 释放旧值(在retain/copy/mutableCopy之后,旧值不再被引用)objc_release(oldValue);
}

Protocol 中的属性

  • 在 Protocol 中使用 @property 只会自动生成 setter 和 getter 方法声明,不会自动生成其实现。
  • 我们在 Protocol 中使用属性的目的,是希望遵守我协议的对象能实现该属性。

Category 中的属性

  • 在 Category 使用 @property 也是只会自动生成 setter 和 getter 方法的声明,不会自动生成其实现。
  • 如果我们真的需要给 category 增加属性的实现,需要借助于运行时的两个函数:objc_setAssociatedObjectobjc_getAssociatedObject。即关联对象

Atomic对线程安全的作用

看到reallySetProperty之中,对于Atomic之中的处理

 if (!atomic) {oldValue = *slot; *slot = newValue; } else {spinlock_t& slotlock = PropertyLocks[slot];slotlock.lock();oldValue = *slot;*slot = newValue;slotlock.unlock();}

其实我们可以看到,这个原子锁就是在调用setter/getter方法时,使用自旋锁进行加锁,即使用atomic修饰的作用在于:单个读/写操作的原子性,保证属性值的读取或写入操作是完整的

但是仍然存在一些问题:

1. 复合操作问题

// 线程1
if (self.atomicCounter > 0) {self.atomicCounter -= 1; // 非原子操作
}// 线程2
self.atomicCounter = 100;

问题分析

  • 虽然每个单独的读写是原子的
  • 检查值->修改值 的组合操作不是原子的
  • 线程2可能在检查后修改值

2. 多属性一致性问题

// 账户转账示例
@interface Account : NSObject
@property (atomic) double balanceA;
@property (atomic) double balanceB;
@end// 线程1:转账操作
account.balanceA -= 100;
account.balanceB += 100;// 线程2:读取总额
double total = account.balanceA + account.balanceB;

问题分析

  • 每个属性单独是原子的
  • 但两个属性的组合操作不是原子的
  • 可能读到中间状态:balanceA已扣款但balanceB未加款

copy

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

相关文章:

  • 基于深度学习的胸部 X 光图像肺炎分类系统(一)
  • STL学习(?map容器)
  • C++性能优化实战‘从毫秒到微秒的底层突围‘
  • C++ 性能优化
  • WPF 控制动画开关
  • 一键修复ipynb,Jupyter Notebook损坏文件
  • redis前期工作:环境搭建-在ubuntu安装redis
  • 基于xxl-job的分片实现分库分表后的扫表
  • Qt WebEngine Widgets的使用
  • MCNN-BiLSTM-Attention分类预测模型等!
  • ChemDraw23软件下载及安装教程|附带软件下载文件|ChemDraw20-23pro版本
  • <<P4116 Qtree3>>
  • 胡良兵Nature Chem Eng:孔隙门控焦耳热精准升级聚乙烯为航油前驱物
  • 中央广播电视总台联合阿里云研究院权威发布《中国人工智能应用发展报告(2025)》:我国依旧需要大力注重人工智能人才的培养
  • Coze工作流-更多图像插件
  • 数据集成难在哪?制造企业该怎么做?
  • Docker多主机网络连接:实现跨主机通信
  • 主流摄像头协议及其开源情况,GB/T 28181协议介绍
  • 配电自动化终端中电源模块的设计
  • uniapp中flex布局gap属性兼容处理
  • PH73211L_VC1/PH73211LQ_VC1:低功耗USB HiFi音频解码器固件技术解析
  • QML WorkerScript
  • 【Spring Boot】热部署终极指南:IDEA高效配置与JRebel替代方案深度解析
  • 第4章唯一ID生成器——4.1 分布式唯一ID
  • Vimba相机二次开发教程,基于Python
  • R 语言科研配色 --- 第 81 期 (附免费下载的配色绘图PPT)
  • 【性能测试】Jmeter+Grafana+InfluxDB+Prometheus Windows安装部署教程
  • 重生学AI第二十集(大结局):完善模型以及学习总结
  • 【STM32】FreeRTOS 任务的删除(三)
  • NX804NX810美光固态闪存NX815NX839