【iOS】strong和copy工作流程探尋、OC屬性關鍵字復習

文章目錄

    • 前言
    • strong和copy的區別
      • 為什么要用copy?
      • 什么時候用什么修飾?
    • strong(ARC自動管理)
      • strong修飾變量的底層流程圖
      • 底層代碼核心實現
      • 小結
    • copy
      • 底層流程圖
      • 對比與strong的關鍵不同之處
      • 內部調用關系(偽代碼)
      • 小結
    • OC中的屬性關鍵字
      • 原子性(Atomicity):控制操作的原子性
        • atomic(原子性,默認值)
        • nonatomic(非原子性)
        • 小結
      • 讀寫權限:控制屬性的訪問方式
        • readwrite(可讀可寫,默認值)
        • readonly(只讀)
        • 小結
      • 內存管理:控制對象的內存生命周期
        • strong(強引用,默認值)
        • weak(弱引用)
        • copy(拷貝)
        • assign(直接賦值)
        • 小結
      • 空值約束(Nullability):標記屬性是否允許為 nil
        • nonnull(非空)
        • nullable(可空)
        • null_unspecified(未指定)
        • 集合宏(簡化聲明)
        • 小結
    • 總結

前言

??我們編寫OC代碼在.h文件里聲明變量時會用到strong和copy這兩個修飾符,那么這兩者到底有什么區別,什么時候該用strong,什么時候該用copy?今天我們通過這兩者的底層實現邏輯來探究一下這里面的奧秘。

strong和copy的區別

首先,先看一下這段測試代碼:
請添加圖片描述
請添加圖片描述

請添加圖片描述

代碼運行結果如下:

請添加圖片描述

??從上述結果我們可以看出來,copy修飾的字符串strCopy的值沒有隨著newStr的改變而改變,而strong修飾的字符串strStrong因為newStr的修改而隨著改變了。

??在我們前面學習的過程中,我們知道copy修飾的變量,編譯器會自動生成自定義的 setter方法,其核心邏輯是:==賦值時不直接保留原對象,而是先調用對象的 copy方法生成副本,再保留該副本。==也就是說,copy修飾的變量對象地址是一個新的地址,這個指針變量指向的是一個新的內存區域(相當于深拷貝),所以修改原本的newStr不會對其產生影響。

??所以,使用 copy和 strong修飾變量,在 OC中的最大區別在于 賦值時是否進行對象拷貝(復制內存)。這在處理==可變對象(如 NSMutableString)==時尤為關鍵。

關鍵字作用
strong引用傳遞:只增加引用計數,指向原對象
copy拷貝傳遞:生成副本,指向新對象

為什么要用copy?

防止外部可變對象的改變影響內部狀態

保證對象的不可變性,尤其是對 NSString、NSArray、NSDictionary 等常用于聲明為不可變對象的屬性

推薦使用 copy 修飾 NSString / NSArray / NSDictionary(防御性編程)

什么時候用什么修飾?

類型建議修飾符
NSStringcopy
NSMutableStringcopy
NSArray/NSDictionarycopy
自定義對象strong(實現copy時才用copy)

strong(ARC自動管理)

??使用 strong修飾的變量,在 ARC(Automatic Reference Counting)下,其底層原理和邏輯可以歸結為對 objc_storeStrong() 函數的封裝,它等價于:

id oldValue = _ivar;
_ivar = [newValue retain];   // 引用計數 +1
[oldValue release];         // 引用計數 -1

strong修飾變量的底層流程圖

因為strong在底層是對objc_storeStrong()函數的封裝,所以其底層流程圖其實就等效 objc_storeStrong()

                  +----------------------+|   objc_storeStrong   |+----------------------+|+----------------------+| 檢查目標地址是否為 nil |+----------------------+|v+----------------------+| old = *object        || *object = value      |+----------------------+|+-----------------------------------+| 如果新舊對象不同:                 || - retain(value) → 引用計數 +1     || - release(old) → 引用計數 -1      |+-----------------------------------+|v+--------------------+| 引用更新完成返回    |+--------------------+

底層代碼核心實現

在 Apple Runtime 源碼中,objc_storeStrong 類似于這樣實現(簡化版偽代碼):

void objc_storeStrong(id *object, id value) {id old = *object;if (old != value) {objc_retain(value);   // 引用計數 +1*object = value;objc_release(old);    // 引用計數 -1}
}

小結

步驟動作
新值 retain增加新對象引用計數,確保它不會被過早釋放
舊值 release減少舊對象引用計數,若為0則銷毀
設置 _ivar將指針指向新的對象地址
安全性ARC 生成的代碼是線程安全的,并避免循環引用等問題

copy

??當我們使用 copy修飾屬性時,其背后的行為與 strong非常不同,關鍵在于對象賦值時會生成副本(調用 copy 方法),而不是直接引用原對象

當你使用 copy 修飾屬性時,其背后的行為與 strong 非常不同,關鍵在于 對象賦值時會生成副本(調用 copy 方法),而不是直接引用原對象

底層流程圖

使用copy修飾符在底層的調用邏輯等價于 objc_storeStrong(&ivar, [value copy])):

              +----------------------+|   objc_storeStrong   |+----------------------+^|+----------------------+|     調用 [value copy] |+----------------------+|+-----------------------------+| copy 調用 -copyWithZone:     ||  -> 生成新的對象             ||  -> 返回新對象引用           |+-----------------------------+|+-----------------------------+| retain(copyValue)           || release(oldValue)           |+-----------------------------+|+----------------------+| ivar 指向新副本對象  |+----------------------+

對比與strong的關鍵不同之處

行為strongcopy
是否復制對象? 不復制,直接引用原對象? 調用 copy 創建新對象
內部處理方式objc_storeStrong(&ivar, value)objc_storeStrong(&ivar, [value copy])
所需協議支持無需對象需實現 NSCopying 協議
適用場景一般對象引用NSString / NSArray / 自定義不可變類等
對可變對象是否防御? 外部修改影響內部? 內部得到的是獨立副本,不受外部影響

內部調用關系(偽代碼)

// ARC 編譯器生成代碼
- (void)setName:(NSString *)name {id copyValue = [name copy];             // 關鍵步驟:生成副本objc_storeStrong(&_name, copyValue);    // 然后存儲副本對象
}

小結

copy修飾的屬性會在賦值時復制對象副本,而不是保留原始引用。

如果傳入的是 NSMutableString,copy 后變成 NSString(不可變),從而防止外部修改影響內部狀態

最終仍通過 objc_storeStrong 來管理引用計數,但傳入的是 [x copy] 的返回值。

OC中的屬性關鍵字

在 Objective-C 中,屬性(Property)是封裝對象狀態的核心機制,通過 @property 聲明并結合不同的屬性關鍵字,可以精確控制屬性的行為(如內存管理、訪問權限、線程安全等)。

原子性(Atomicity):控制操作的原子性

原子性關鍵字用于修飾屬性的 setter 和 getter 方法,決定其操作是否為“原子操作”(不可分割的操作)。

atomic(原子性,默認值)

保證 settergetter 操作的原子性(即操作要么完全執行,要么完全不執行),避免多線程并發訪問時的數據不一致問題。

特點:

  • 線程安全,但不保證業務邏輯的絕對安全(例如,復合操作如 _count++ 仍需額外同步)。
  • 性能開銷較大(因需要加鎖/解鎖機制)。
@property (atomic, assign) NSInteger count; // 默認 atomic,線程安全但性能一般
nonatomic(非原子性)

不保證 settergetter 的原子性,多線程并發訪問時可能導致數據不一致。

特點:

  • 無鎖機制,性能更高(適合高頻讀寫的場景)。
  • 需開發者自行處理線程安全(如通過 @synchronized、GCD 隊列等)。
@property (nonatomic, strong) NSString *name; // 非原子性,性能更優
小結
  • 優先選 nonatomic:大多數場景下(尤其是移動端),性能比絕對線程安全更重要。
  • 僅當選中 atomic:需要框架級線程安全(如系統底層庫),或配合其他同步機制使用。

讀寫權限:控制屬性的訪問方式

讀寫權限關鍵字決定屬性是否生成 setter 方法,從而控制屬性的可寫性。

readwrite(可讀可寫,默認值)

自動生成 setter(寫方法)和 getter(讀方法),屬性可讀可寫。

@property (readwrite, copy) NSString *title; // 可讀可寫(默認)
readonly(只讀)

僅生成 getter 方法,屬性不可寫(外部只能讀取,不能直接修改)。

  • 常用于接口設計,隱藏內部實現細節(如通過類擴展在 .m 文件中重新聲明為 readwrite,允許內部修改)。
// .h 文件(對外接口)
@interface User : NSObject
@property (readonly, copy) NSString *userId; // 外部只讀
@end// .m 文件(內部實現)
@interface User ()
@property (readwrite, copy) NSString *userId; // 內部可寫
@end
小結
  • readwrite:常規可修改屬性。
  • readonly:常用于封裝(外部只讀,內部可寫)。

內存管理:控制對象的內存生命周期

內存管理關鍵字用于告訴編譯器如何管理屬性所引用對象的內存(僅適用于對象類型,基本數據類型如 intCGFloat 不適用)。

strong(強引用,默認值)

屬性對對象持有強引用(增加對象的引用計數),確保對象在屬性作用域內不會被釋放。

  • 適用于大多數對象類型(如自定義對象、系統容器類)。

  • 循環引用風險:若兩個對象相互 strong 引用,會導致內存泄漏(需配合 weak 打破)。

    @property (strong, nonatomic) NSArray *dataList; // 強引用數組,數組內對象也會被保留
    
weak(弱引用)

屬性對對象持有弱引用(不增加引用計數),對象釋放后,屬性自動置為 nil(避免野指針)。

特點:

  • 解決循環引用(如 delegate 模式、視圖控制器與子視圖的相互引用)。
  • 無法持有對象(對象可能因無強引用被提前釋放)。
// 視圖控制器的 delegate 通常用 weak,避免循環引用
@property (weak, nonatomic) id<MyDelegate> delegate;
copy(拷貝)

賦值時調用對象的 copy 方法生成副本,屬性持有副本的強引用(原對象不受后續修改影響)。

適用場景:

  • 不可變對象(如 NSStringNSArray):防止外部傳入可變對象(如 NSMutableString)后被修改。
  • 需要“快照”的場景(如配置參數、緩存數據)。
@property (copy, nonatomic) NSString *username; // 外部傳入 NSMutableString 會被拷貝為 NSString
assign(直接賦值)

直接賦值(不涉及引用計數管理),適用于基本數據類型非對象類型(如 intCGFloat、指針)。

特點:對對象類型使用 assign 會導致野指針(對象釋放后屬性仍指向無效內存)。

@property (assign, nonatomic) NSInteger age; // 基本數據類型用 assign
@property (assign, nonatomic) CGPoint position; // 結構體用 assign
小結
關鍵字適用類型內存行為典型場景注意事項
strong對象強引用(+1 RC)常規對象屬性避免循環引用
weak對象弱引用(不+RC,釋放后置nil)delegate、IBOutlets僅適用于對象
copy對象(需實現 NSCopying生成副本并強引用字符串、集合、防止外部修改不可變對象 copy 是淺拷貝
assign基本類型/非對象直接賦值(無引用計數)數值、結構體對象類型會導致野指針

空值約束(Nullability):標記屬性是否允許為 nil

空值約束關鍵字用于明確屬性是否允許為 nil,幫助編譯器進行靜態檢查,減少運行時崩潰(需 Xcode 7+ 支持)。

nonnull(非空)

屬性必須非空(不能為 nil),編譯器會對可能的 nil賦值發出警告。

@property (nonatomic, copy, nonnull) NSString *userId; // 必須賦值非空字符串
nullable(可空)

屬性可以為空(允許為 nil),無編譯器警告。

@property (nonatomic, strong, nullable) UIImage *avatar; // 頭像可能為空
null_unspecified(未指定)

屬性是否為空未明確聲明(編譯器不做強制檢查),用于兼容舊代碼或無法確定的情況。

@property (nonatomic, copy, null_unspecified) NSString *legacyData; // 未指定是否可空
集合宏(簡化聲明)

為避免重復寫 nonnull/nullable,可使用以下宏包裹屬性列表:

  • NS_ASSUME_NONNULL_BEGIN/NS_ASSUME_NONNULL_END:區間內默認 nonnull,顯式 nullable除外。

  • 示例:

    NS_ASSUME_NONNULL_BEGIN
    @interface User : NSObject
    @property (nonatomic, copy) NSString *name; // 默認 nonnull
    @property (nonatomic, strong, nullable) NSNumber *age; // 顯式 nullable
    @end
    NS_ASSUME_NONNULL_END
    
小結
  • nonnull:強制屬性非空,提升代碼健壯性。
  • nullable:明確屬性可空,避免無意義檢查。
  • null_unspecified:兼容過渡場景。

總結

場景推薦關鍵字組合
常規對象屬性(如自定義模型)nonatomic, strong
字符串/集合(防外部修改)nonatomic, copy
避免循環引用(如 delegatenonatomic, weak
基本數據類型/結構體nonatomic, assign
接口只讀,內部可寫.hreadonly.mreadwrite
強制非空nonatomic, strong, nonnull

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/news/917432.shtml
繁體地址,請注明出處:http://hk.pswp.cn/news/917432.shtml
英文地址,請注明出處:http://en.pswp.cn/news/917432.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

程序代碼篇---多循環串口程序切換

上位機版&#xff08;Python&#xff09;要實現根據串口接收結果高效切換四個 while 循環函數&#xff0c;我們可以采用狀態機模式&#xff0c;配合非阻塞串口讀取來設計程序結構。這種方式可以實現快速切換&#xff0c;避免不必要的資源消耗。下面是一個高效的實現方案&#x…

rk3568上,實現ota,計算hash,驗證簽名,判斷激活分區,并通過dd命令,寫入對應AB分區

通過自定義升級程序&#xff0c;更直觀的理解ota升級原理。 一、模擬計算hash&#xff0c;驗證簽名&#xff0c;判斷激活分區&#xff0c;并通過dd命令&#xff0c;寫入對應分區 #include <stdio.h> #include <stdlib.h> #include <string.h> #include <u…

數據分析—numpy庫

numpy庫NumPy 庫全面指南NumPy (Numerical Python) 是 Python 科學計算的基礎庫&#xff0c;提供了高性能的多維數組對象和工具。以下是 NumPy 的核心功能和使用方法。一、安裝與基礎1. 安裝 NumPypip install numpy2. 導入 NumPyimport numpy as np # 標準導入方式二、數組創建…

Vue3 setup、ref和reactive函數

一、setup函數1.理解&#xff1a;Vue3.0中一個新的配置項&#xff0c;值為一個函數。2.setup是所有Composition API(組合API)的“表演舞臺”。3.組件中用到的&#xff1a;數據、方法等等&#xff0c;均要配置在setup中。4.setup函數的兩種返回值&#xff1a;(1).若返回一個對象…

python中appium 的NoSuchElementException錯誤 原因以及解決辦法

錯誤收集D:\Program\Util\python.exe "D:/Program/myUtil/PyCharm 2024.3.5/plugins/python-ce/helpers/pycharm/_jb_pytest_runner.py" --target demo.py::TestAppium Testing started at 15:57 ... Launching pytest with arguments demo.py::TestAppium --no-hea…

mybatis-plus從入門到入土(四):持久層接口之BaseMapper和選裝件

大家好&#xff0c;今天繼續更新MybatisPlus從入門到入土系列&#xff0c;上一次的持久層接口還沒講完&#xff0c;只講了IService接口&#xff0c;今天我們繼續來講一下。 BaseMapper BaseMapper中的方法也比較簡單&#xff0c;都是增刪改查的基礎API&#xff0c;不知道大家還…

數組和指針的關系

在 C 語言中&#xff0c;?指針和數組有著非常緊密的聯系&#xff0c;但它們本質上是 ?不同的概念。理解它們的關系是掌握 C 語言內存操作的關鍵。下面我會從多個角度幫你梳理 ?指針和數組的直接聯系&#xff0c;并解釋它們的異同點。1. 數組和指針的本質區別?概念本質存儲方…

AI大模型探索之路-實戰篇:智能化IT領域搜索引擎之github網站在線搜索

系列篇章?? No. 文章 1 AI大模型探索之路-實戰篇:智能化IT領域搜索引擎的構建與初步實踐 2 AI大模型探索之路-實戰篇:智能化IT領域搜索引擎之GLM-4大模型技術的實踐探索 3 AI大模型探索之路-實戰篇:智能化IT領域搜索引擎之知乎網站數據獲取(初步實踐) 4 AI大模型探索之路…

從0到1學PHP(十二):PHP 框架入門與項目實戰

目錄一、主流 PHP 框架介紹1.1 Laravel1.2 ThinkPHP1.3 Yii1.4 框架的優勢二、框架基本使用&#xff08;以 Laravel 為例&#xff09;2.1 框架的安裝與配置2.2 路由定義、控制器創建、視圖渲染2.3 數據庫操作&#xff08;ORM 的使用&#xff09;三、小型項目實戰3.1 項目需求分…

MPLS LSP

一、概述上一章我們已經介紹過,LSP是MPLS報文在MPLS網絡中轉發時經過的路徑,可以看作是由報文傳輸方向節點為對應FEC分配的MPLS入標簽組成的,因為每臺設備上為每個FEC分配的入標簽是唯一 的&#xff0c;并與由下游節點為本地節點上該FEC分配的出標簽建立映射關系&#xff0c; 所…

圖像、視頻、音頻多模態大模型中長上下文token壓縮方法綜述

多模態大模型MLLMs 能夠處理高分辨率圖像、長視頻序列和冗長音頻輸入等復雜上下文&#xff0c;但自注意力機制的二次復雜度使得大量輸入 token 帶來了巨大的計算和內存需求。 如下圖&#xff0c;上&#xff1a;圖像、視頻和音頻數據類型可以在其表示維度上進行擴展&#xff0c;…

Spring MVC 九大組件源碼深度剖析(一):MultipartResolver - 文件上傳的幕后指揮官

文章目錄一、為什么從 MultipartResolver 開始&#xff1f;二、核心接口&#xff1a;定義文件上傳的契約三、實現解析&#xff1a;兩種策略的源碼較量1. StandardServletMultipartResolver&#xff08;Servlet 3.0 首選&#xff09;2. CommonsMultipartResolver&#xff08;兼容…

stm32是如何實現電源控制的?

STM32的電源控制主要通過內置的電源管理模塊&#xff08;PWR&#xff09;實現&#xff0c;涵蓋電壓調節、功耗模式切換和電源監控等功能。以下是其核心機制及實現方式&#xff1a;??1. 電源架構與供電區域??STM32的電源系統分為多個供電區域&#xff0c;各司其職&#xff1…

《R for Data Science (2e)》免費中文翻譯 (第3章) --- Data transformation(1)

寫在前面 本系列推文為《R for Data Science (2)》的中文翻譯版本。所有內容都通過開源免費的方式上傳至Github&#xff0c;歡迎大家參與貢獻&#xff0c;詳細信息見&#xff1a; Books-zh-cn 項目介紹&#xff1a; Books-zh-cn&#xff1a;開源免費的中文書籍社區 r4ds-zh-cn …

rclone、rsync、scp使用總結

數據同步工具使用總結【rclone、rsync、scp】一、數據處理背景二、數據處理方法對比1、數據關系梳理2、不同工具處理方法3、經驗總結三、工具擴展知識1、rclone工具介紹&#xff08;1&#xff09;、rclone概述&#xff08;2&#xff09;、安裝工具及配置本地文件遷移到云上服務…

用latex+vscode+ctex寫畢業論文

文章目錄前言一、安裝latex二、安裝ctex包三、更新ctex包四、使用ctex文檔類前言 用latexvscodectex寫畢業論文。&#xff08;英文論文不用安裝ctex&#xff09; CTEX 宏集是面向中文排版的通用 LATEX 排版框架&#xff0c;為中文 LATEX 文檔提供了漢字輸出支持、標點壓縮、字…

深度學習·mmsegmentation基礎教程

mmsegmentation的使用教程 mmsegmentation微調方法總結 自定義自己的數據集&#xff1a;mmsegmentation\configs\_base_\datasets\ZihaoDataset_pipeline.py注冊&#xff1a;mmsegmentation\configs\_base_\datasets\__init__.py定義訓練和測試的pipeline&#xff1a;mmsegme…

InfluxDB 與 Node.js 框架:Express 集成方案(二)

四、優化與注意事項 &#xff08;一&#xff09;性能優化技巧 連接池管理&#xff1a;使用連接池可以有效減少創建和銷毀數據庫連接的開銷。在 Node.js 中&#xff0c;可以借助influx模塊結合第三方連接池庫&#xff0c;如generic-pool來實現連接池的管理 。通過設置連接池的…

單位長度上的RC參數

1inch1000mil25.4mm2.54cm 使用SI9000計算導線上電容電感參數并使用Q2D進行仿真驗證。使用SI9000建立一個阻抗為50歐的微帶線模型&#xff0c;后對該模型進行1GHz頻域計算 通過計算得到結果&#xff0c;可知1GHz頻率下單位傳輸線上的RLGC參數使用SI9000計算好單位長度上的RLGC參…

基于Dockerfile 部署一個 Flask 應用

Docker 與 Python&#xff1a;容器化部署應用&#xff0c;實現快速發布與彈性伸縮 以下是一個簡單的 Flask 應用 # app.py - 一個簡單的Flask應用 from flask import Flask import osapp Flask(__name__)app.route("/") def hello():env os.environ.get(FLASK_ENV,…