【iOS】OC高級編程 iOS多線程與內存管理閱讀筆記——自動引用計數(二)

自動引用計數

  • 前言
  • ARC規則
    • 所有權修飾符
      • **__strong修飾符**
      • __weak修飾符
      • __unsafe_unretained修飾符
      • __autoreleasing修飾符
    • 規則
    • 屬性
    • 數組

前言

上一篇我們主要學習了一些引用計數方法的內部實現,現在我們學習ARC規則。


ARC規則

所有權修飾符

OC中,為了處理對象,可以將變類型定義為id類型或各種對象類型。

對象類型: 即OC類的指針,例如“NSObject* ”
id類型: 用于隱藏對象類型的類名部分,相當于C語言中的(void *)

ARC有效時,id類型和對象類型同C語言其他類型不同,必須附加上所有權修飾符

  • __strong修飾符
  • __weak修飾符
  • __unsafe_unretained修飾符
  • __autoreleasing修飾符

__strong修飾符

__strong修飾符是id類型和對象類型默認的所有權修飾符。也就是說

id obj = [[NSObject alloc] init];id __strong obj = [[NSObject alloc] init];

這兩種代碼是一樣的。

但是,當ARC無效時,該如何實現__strong修飾符呢。

{id obj = [[NSObject alloc] init];[obj release]
}

如上述代碼所示,附有__strong修飾符的變量obj在超出其變量作用域時,即在該變量被廢棄時,會釋放其被賦予的對象。

因此,我們可以通過在最后調用release代碼,實現這一功能。

如“strong”所示,__strong修飾符表示對對象的“強引用”。持有強引用的變量在超出其作用域時廢棄。隨著強引用的失效,引用的對象會隨之釋放。

對于自己生成并持有對象的源代碼來說,對象的所有者和對象的生存周期都是明確的,那么如果是取得非自己生成并持有的對象呢。

{id__strong obj = [NSMutableArray array];
}

這里我們通過NSMutableArray類的array類方法學習。

{//取得非自己生成并持有的對象id __strong obj = [NSMutableArray array];//變量obj為強引用,所以自己持有對象。
}
//變量obj超出其作用于,強引用失效,自動釋放自己持有的對象。

可見取得非自己生成但是持有的對象的生存周期也是明確的

即使是OC類成員變量,也可以在方法參數上,使用附有__strong修飾符的變量。

@interface Test : NSObject
{id __strong obj_;
}
- (void)setObject:(id __strong)obj;
@end@implementation Test
- (id)init
{self = [super init];return self;
}
- (void)setObject:(id __strong)obj
{obj_ = obj;
}
@end

下面我們進行使用:

{id __strong test = [[Test alloc] init];//test持有Test對象的強引用[test setObject:[[NSObject alloc] init];//Test對象的obj_成員,持用NSObjcet對象的強引用。
}
/*因為test變量超出其作用域,強引用失效所以自動釋放Test對象。Test對象的所有者不存在,因此廢棄該對象。廢棄Test對象的同時,Test對象的obj_成員也被廢棄,NSObjcet對象的強引用失效自動釋放NSObjcet對象所有者不存在,廢棄該對象。*/

通過這種方法,無需額外工作便可以使用于類成員變量以及方法參數中。

修飾符可以保證將附有這些修飾符的自動變量初始化為nil。

id __strong ojb0;
//這兩種初始化方式相同
id __strong obj0 == nil;

通過__strong修飾符,不必再次鍵入retain或者release即可實現OC內存管理的思考方式。

并且,id類型和對象類型的所有權修飾符默認為__strong修飾符,所以不需要寫上"__strong"。這一設定使得ARC有效以及簡單的編程遵循了OC內存管理的思考方式。

__weak修飾符

如果僅使用__strong修飾符,容易發生循環引用的問題,這對項目是毀滅性的。
如以下這種情況:

{id test0 = [[Test alloc] init];//test0持有Test對象A的強引用id test1 = [[Test alloc] init];//test1持有Test對象B的強引用[test0 setObject:test1];/*Test對象A的obj_成員變量持用Test對象B的引用此時,持有Test對象B的強引用的變量為Test對象A的obj_和test1。*/[test1 setObject:test0];/*Test對象B的obj_成員變量持用Test對象A的引用此時,持有Test對象A的強引用的變量為Test對象B的obj_和test0。*/
}/*
因為 test0 變量超出其作用域,強引用失效,
所以自動釋放 Test 對象 A。
因為 test1 變量超出其作用域,強引用失效,
所以自動釋放 Test 對象 B。
此時,持有 Test 對象 A 的強引用的變量為
Test 對象 B 的 obj_。
此時,持有 Test 對象 B 的強引用的變量為
Test 對象 A 的 obj_。
發生內存泄漏!
*/

如下圖所示:
請添加圖片描述
循環引用容易發生內存泄漏:即應當廢棄的對象在超出其生存周期后繼續存在。
上述代碼分別將對象A賦給test0,對象B賦給test1后,在超出作用域后無法正確被釋放。

為了避免以上這種情況,我們可以采用__weak修飾符。

__weak修飾符:提供弱引用,不能持有對象實例。

id __weak obj = [[NSObject alloc] init];

會出現以下警告。
請添加圖片描述

變量 obj 持有對持有對象的弱引用。因此,為了不以自己持有的狀態來保存自己生成并持有的對象,生成的對象會立即被釋放。

如果使用以下代碼,將對象賦值給附有__strong修飾符的變量之后,在賦值附有__weak修飾符的變量,就不會發生警告。

{//自己生成并且持有對象id __strong obj0 = [[NSObject alloc] init];//obj0變量為強引用,所以自己持有對象id __weak obj1 = obj2;//obj1變量持有生成對象的弱引用
}
//因為obj0變量超出其作用域,強引用失效,所以自動釋放自己持有的對象
//因為對象的所有者不在,所以會自動廢棄obj1

因此上述代碼只需要將可能發生循環引用的類成員變量改成附有__weak修飾符的成員變量,即可避免循環引用的問題。如下修:

@interface Test : NSObject
{id __weak obj_;
}
- (void)setObject:(id __strong)obj;
@end

此時對象引用情況如圖所示:
請添加圖片描述
__weak修飾符還有另一優點:在持有某對象的弱引用時,若該對象被廢棄,則此弱引用將自動失效切處于nil被賦值的狀態(空弱引用)。

id __weak obj1 = nil;{id __strong obj0 = [[NSObject alloc] init];obj1 = obj0;NSLog(@"A: %@", obj1);}/*obj0變量超出其作用域,強引用失效所以自動釋放自己持有的對象因為對象無持用者,所以廢棄該對象廢棄對象的同時持有該對象弱引用的obj1變量的弱引用失效,nil賦值給obj1*/
NSLog(@"B: %@", obj1);

源代碼的結果如下:
請添加圖片描述
像這樣,使用__weak修飾符即可避免循環引用。通過檢查附有__weak修飾符的變量是否為nil,可以判斷被賦值的對象是否已廢棄。

__unsafe_unretained修飾符

__unsafe_unretained修飾符正如其名,是不安全的所有權修飾符。
附有該修飾符的變量不屬于編譯器的內存管理對象。
與附有__weak修飾符的變量一樣,因此自己生成并持有的對象不能繼續為自己所有,所以生成的對象會立即釋放。但是當廢棄時并不會自動置nil。

id __unsafe_unretained obj1 = nil;{id __strong obj0 = [[NSObject alloc] init];obj1 = obj0;NSLog(@"A: %@", obj1);}
NSLog(@"B: %@", obj1);

以上代碼偶爾會運行成功,但更多情況下訪問一個空對象會報錯。

__autoreleasing修飾符

在 ARC 有效時,用 @autoreleasepool 塊替代 NSAutoreleasePool 類,用附有 __autoreleasing 修飾符的變量替代 autorelease 方法

/* ARC無效 */
NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
id obj = [[NSObject alloc] init];
[obj autorelease];
[pool drain];/*  有效 */
@autoreleasepool {id __autoreleasing obj = [[NSObject alloc] init];
}

請添加圖片描述
但是,通常情況下我們不會顯示的附加__autoreleasing修飾符和__strong修飾符。

當使用alloc/new/copy/mutableCopy以外的方法來取得丟下時,該對象會自動被注冊到autorelease方法中。

訪問附有__weak修飾符的變量時,必須訪問注冊到autoreleasepool的對象。這是因為__weak修飾符紙持有對象的弱引用,而對象有可能被廢棄,但是如果把要訪問的對象注冊到autoreleasepool中,在@autoreleasepool塊結束之前都能確保該對象存在。因此:
使用附有__weak修飾符的變量時必定要使用注冊到autoreleasepool中的對象

當我們顯示的制定__autoreleasing修飾符時,必須注意對象變量要為自動變量(包括局部變量,函數以及方法參數)

無論 ARC 是否有效,調試用的非公開函數 _objc_autoreleasePoolPrint()都可使用。

該函數可以用于打印當前自動釋放池中的所有對象信息。

規則

當ARC有效時,需要遵守的規則:

  • 不能使用 retain/release/retainCount/autorelease
  • 不能使用 NSAllocateObject/NSDeallocateObject
  • 須遵守內存管理的方法命名規則
  • 不要顯式調用 dealloc
  • 使用 @autoreleasepool 塊替代 NSAutoreleasePool
  • 不能使用區域(NSZone
  • 對象型變量不能作為 C 語言結構體(struct/union)的成員
  • 顯式轉換 “id” 和 “void *

不能使用 retain/release/retainCount/autorelease

內存管理是編譯器的工作,因此沒必要使用內存管理的方法。

設置ARC有效時,無需(禁止)再次鍵入retain或release代碼。

實際上,再次鍵入retain和release代碼時會報錯,所以應該是禁止鍵入。
同樣的,retainCount和release也會引起編譯錯誤。

不能使用 NSAllocateObject/NSDeallocateObject

在ARC有效時,禁止使用NSAllocateObject函數。同retain方法一樣,會引起編譯報錯。同一釋放對象的NSDeallocateObject函數也不可使用。

須遵守內存管理的方法命名規則
當ARC無效時,用于對象生成/持有的方法必須遵守以下的命名規則。

使用alloc/new/copy/mutableCopy時,必須返回給調用方所應當持有的對象。

但是當ARC有效時,init開始的方法必須是實例方法,并且要返回對象。返回的對象應為id類型或該方法聲明類的對象類型,或者是該類型的父類或者子類。該返回對象并不注冊到autoreleasepool上。基本知識對alloc方法返回值的對象進行初始化處理并返回該對象。如下所示:

-(void) initWithObject:(id) obj;

對象型變量不能作為 C 語言結構體(struct/union)的成員

struct Data {NSMutableArray *array;
};

以上代碼會報錯

顯式轉換 “id” 和 “void *

//id和void*互轉時需要通過__bridge轉換id obj = [[NSObject alloc] init];void *p = (__bridge void *)obj;id o = (__bridge id)p;

__bridge_retained 轉換可使要轉換賦值的變量也持有所賦值的對象。

/* ARC無效 */
id obj = [[NSObject alloc] init];
void *p = obj;
[(id)p retain];

__bridge_retained 轉換變為了 retain。變量 obj 和變量 p 同時持有對象。

void *p = 0;
{id obj = [[NSObject alloc] init];p = (__bridge_retained void *)obj;
}
NSLog(@"class=%@", [(__bridge id)p class]);

變量作用域結束時,雖然隨著持有強引用的變量 obj 失效,對象隨之釋放,但由于 __bridge_retained 轉換使變量 p 看上去處于持有該對象的狀態,因此該對象不會被廢棄。

__bridge_transfer 轉換提供與此相反的動作,被轉換的變量所持有的對象在該變量被賦值給轉換目標變量后隨之釋放。

id obj = (__bridge_transfer id)p;
//上述代碼在ARC無效時如下表達:
/* ARC無效 */
id obj = (id)p;
[obj retain];
[(id)p release];

同 __bridge_retained 轉換與 retain 類似,__bridge_transfer 轉換與 release 相似。在給 id obj 賦值時 retain 即相當于 __strong 修飾符的變量。

屬性

當ARC有效時,以下可作為這種屬性聲明中使用的屬性來用

請添加圖片描述
以上各種屬性賦值給指定的屬性中就相當于賦值給附加各屬性對應的所有權修飾符的變量中。

只有 copy 屬性不是簡單的賦值,它賦值的是通過 NSCopying 接口的 copyWithZone: 方法復制賦值源所生成的對象。

另外,在聲明類成員變量時,如果同屬性聲明中的屬性不一致則會引起編譯錯誤。比如下面這種情況。

id obj;//默認為__strong@property (nonatomic, weak) id obj;
//會出現報錯//需要改成以下形式
id __weak obj;

數組

使用修飾符賦值數組的使用與變量相同。

id objs[10];id __weak objs[10];

__unsafe_unretained 修飾符以外的 __strong/__weak/__autoreleasing 修飾符保證其指定的變量初始化為 nil。同樣地,附有 __strong/__weak/__autoreleasing 修飾符變量的數組也保證其初始化為 nil
下面我們就來看看數組中使用附有 __strong 修飾符變量的例子。

{id objs[2];objs[0] = [[NSObject alloc] init];objs[1] = [NSMutableArray array];
}

數組超出其變量作用域時,數組中各個附有 __strong 修飾符的變量也隨之失效,其強引用消失,所賦值的對象也隨之釋放。這與不使用數組的情形完全一樣。

將附有 __strong 修飾符的變量作為動態數組來使用時又如何呢?在這種情況下,根據不同的目的選擇使用 NSMutableArrayNSMutableDictionaryNSMutableSet 等 Foundation 框架的容器。這些容器會恰當地持有追加的對象并為我們管理這些對象。

像這樣使用容器雖然更為合適,但在 C 語言的動態數組中也可以使用附有 __strong 修飾符的變量,只是必須要遵守一些事項。以下按順序說明。

聲明動態數組用指針。

id __strong *array = nil;

聲明動態數組時,我們需要顯式的指定為__strong修飾符。

id __strong *array = nil;

由于 “id * 類型” 默認 為 “id __autoreleasing * 類型”,所以有必要顯式指定為 strong 修飾符。另外,雖然保證了附有 __strong 修飾符的 id 型變量被初始化為 nil,但并不保證附有 __strong 修飾符的 id 指針型變量被初始化為 nil

使用類名如下述描述:

NSObject * __strong *array = nil;

其次,使用 calloc 函數確保想分配的附有 __strong 修飾符變量的容量占有的內存塊。

array = (id __strong *)calloc(entries, sizeof(id));

該源代碼分配了 entries 個所需的內存塊。由于使用附有 __strong 修飾符的變量前必須先將其初始化為 nil,所以這里使用使分配區域初始化為 0 的 calloc 函數來分配內存。不使用 calloc 函數,在用 malloc 函數分配內存后可用 memset 等函數將內存填充為 0。

但是,像下面的源代碼這樣,將 nil 代入到 malloc 函數所分配的數組各元素中來初始化是非常危險的。

array = (id __strong *)malloc(sizeof(id) * entries);
for (NSUInteger i = 0; i < entries; ++i)array[i] = nil;

這是因為由 malloc 函數分配的內存區域沒有被初始化為 0,因此 nil 會被賦值給附有 __strong 修飾符的并被賦值了隨機地址的變量中,從而釋放一個不存在的對象。在分配內存時推薦使用 calloc 函數。

像這樣,通過 calloc 函數分配的動態數組就能完全像靜態數組一樣使用。

array[0] = [[NSObject alloc] init];

但是,在動態數組中操作附有 __strong 修飾符的變量與靜態數組有很大差異,需要自己釋放所有的元素。

當我們要廢棄數組時,不能如下直接free。會使數組各元素的值的對象無法釋放,引起內存泄漏。如下述代碼所示。

free(array);

這是因為:在靜態數組中,編譯器能夠根據變量的作用域自動插入釋放賦值對象的代碼,而在動態數組中,編譯器不能確定數組的生存周期,所以無從處理

如以下源代碼所示,一定要將 nil 賦值給所有元素中,使得元素所賦值對象的強引用失效,從而釋放那些對象。在此之后,使用 free 函數廢棄內存塊。

for (NSUInteger i = 0; i < entries; ++i)array[i] = nil;
free(array);

同初始化時的注意事項相反,即使用 memset 等函數將內存填充為 0 也不會釋放所賦值的對象。這非常危險,只會引起內存泄漏。對于編譯器,必須明確地使用賦值給附有 __strong 修飾符變量的源代碼。所以請注意,必須將 nil 賦值給所有數組元素。

并且,memcpy和realloc函數也會有危險,因為數組元素所賦值的對象有可能被保留在內存中或是重復被廢棄,所以也禁止使用。


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

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

相關文章

可信空間數據要素解決方案

可信空間數據要素解決方案 一、引言 隨著數字經濟的蓬勃發展&#xff0c;數據已成為重要的生產要素。可信空間數據要素解決方案旨在構建一個安全、可靠、高效的數據流通與應用環境&#xff0c;促進數據要素的合理配置和價值釋放&#xff0c;推動各行業的數字化轉型和創新發展…

mysql刪除表后重建表報錯Tablespace exists

版本 mysql:8.0.23 復現步驟 1、刪除表 DROP TABLE IF EXISTS xxx_demo; 2、新建表 CREATE TABLE xxx_demo (id bigint NOT NULL AUTO_INCREMENT COMMENT 主鍵id,creator varchar(64) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NULL DEFAULT COMMENT 創建者,c…

【Leetcode-Hot100】缺失的第一個正數

題目 解答 有一處需要注意&#xff0c;我使用注釋部分進行交換值&#xff0c;報錯&#xff1a;超出時間限制。有人知道是為什么嗎&#xff1f;難道是先給nums[i]賦值后&#xff0c;從而改變了后一項的索引&#xff1f; class Solution(object):def firstMissingPositive(sel…

從單模態到多模態:五大模型架構演進與技術介紹

前言 1. ResNet — 殘差神經網絡背景核心問題與解決方案原理模型架構ResNet 系列變體技術創新與影響 2. ViT — Vision Transformer背景核心思想發展歷程Transformer的起源&#xff1a;ViT的出現&#xff1a;ViT的進一步發展&#xff1a; 模型架構技術創新與影響 3. Swin Trans…

JavaScript事件循環

目錄 JavaScript 執行機制與事件循環 一、同步與異步代碼 1. 同步代碼&#xff08;Synchronous Code&#xff09; 2. 異步代碼&#xff08;Asynchronous Code&#xff09; 二、事件循環&#xff08;Event Loop&#xff09; 1. 核心組成 2. 事件循環基本流程 3. 運行機制…

Java Collection(7)——Iterable接口

1.Iterator接口 1.1 Iterator接口和其他集合類的關系 Java集合類中&#xff0c;Iterable接口屬于頂層接口&#xff0c;除Map接口外&#xff0c;其他都實現了Iterable接口&#xff0c;這意味著它們都可以重寫和使用Iterable接口中的方法 1.2 Iterable接口簡介 在JDK1.7以前&a…

若依微服務版啟動小程序后端

目錄標題 本地啟動&#xff0c;dev對應 nacos里的 xxx-xxx-dev配置文件 本地啟動&#xff0c;dev對應 nacos里的 xxx-xxx-dev配置文件

STM32基礎教程——DMA+ADC多通道

目錄 前言 ?編輯 技術實現 連線圖 代碼實現 技術要點 實驗結果 問題記錄 前言 DMA(Direct Memory Access)直接存儲器存取&#xff0c;用來提供在外設和存儲器 之間或者存儲器和存儲器之間的高速數據傳輸。無需CPU干預&#xff0c;數據可以通過DMA快速地移動&#xff0…

23黑馬產品經理Day01

今天過了一遍23黑馬產品經理的基礎視頻 問題思考維度 抓住核心用戶 為什么需要抓住核心用戶&#xff1f; 主要原因&#xff1a;用戶越來越細分&#xff0c;保持市場競爭力&#xff0c;產品開發推廣更聚焦 做產品為什么要了解用戶&#xff1a;了解用戶的付費點&#xff0c;…

C/C++ 通用代碼模板

? C 語言代碼模板&#xff08;main.c&#xff09; 適用于基礎項目、算法競賽或刷題&#xff1a; #include <stdio.h> #include <stdlib.h> #include <string.h> #include <stdbool.h> #include <math.h>// 宏定義區 #define MAX_N 1000 #defi…

【數據結構_7】棧和隊列(上)

一、概念 棧和隊列&#xff0c;也是基于順序表和鏈表實現的 棧是一種特殊的線性表&#xff0c;其只允許在固定的一段進行插入和刪除元素操作。 遵循后進先出的原則 此處所見到的棧&#xff0c;本質上就是一個順序表/鏈表&#xff0c;但是&#xff0c;實在順序表/鏈表的基礎…

git UserInterfaceState.xcuserstate 文件頻繁更新

1> 退出 Xcdoe&#xff0c;打開終端&#xff08;Terminal&#xff09;&#xff0c;進入到你的項目目錄下。 2> 在終端鍵入 git rm --cached <YourProjectName>.xcodeproj/project.xcworkspace/xcuserdata/<YourUsername>.xcuserdatad/UserInterfaceState.x…

【Ai】MCP實戰:手寫 client 和 server [Python版本]

什么是mcp MCP 是一個開放協議&#xff0c;它為應用程序向 LLM 提供上下文的方式進行了標準化。你可以將 MCP 想象成 AI 應用程序的 USB-C 接口。就像 USB-C 為設備連接各種外設和配件提供了標準化的方式一樣&#xff0c;MCP 為 AI 模型連接各種數據源和工具提供了標準化的接口…

ESP8266/32作為AVR編程器(ISP programmer)的使用介紹

ESP8266作為AVR編程器( ISP programmer)的使用介紹 &#x1f33f;ESP8266自帶庫例程&#xff1a;https://github.com/esp8266/Arduino/tree/master/libraries/ESP8266AVRISP&#x1f4cd;支持ESP8266/32的ESP_AVRISP其它開源工程&#xff08;個人沒有再去驗證&#xff09;&…

08-JVM 面試題-mk

文章目錄 1.JVM 的各部分組成2.運行時數據區2.1.什么是程序計數器?2.2.你能給我詳細的介紹Java堆嗎?2.3.能不能解釋一下方法區?2.3.1常量池2.3.2.運行時常量池2.4.什么是虛擬機棧?2.4.1.垃圾回收是否涉及棧內存?2.4.2.棧內存分配越大越好嗎?2.4.3.方法內的局部變量是否線…

Vue3 nextTick

nextTick 是 Vue 中非常重要的一個 API&#xff0c;它允許你在 DOM 更新周期后執行延遲回調。 核心源碼位置 Vue3 的 nextTick 實現主要在 packages/runtime-core/src/scheduler.ts 文件中。 基本實現 const resolvedPromise Promise.resolve() as Promise<any> let …

DISCO:利用大型語言模型提取反事實

DISCO: Distilling Counterfactuals with Large Language Models - ACL Anthologyhttps://aclanthology.org/2023.acl-long.302/ 1. 概述 盡管在自然語言處理(NLP)領域針對各種推理任務取得了巨大進展(Wang 等, 2018, 2019a;Xu 等, 2020),但數據集偏差仍然是構建魯棒模型…

【Django】框架-路由系統核心概念解析

1. 最基本路由關系 路由是URL地址與處理邏輯&#xff08;視圖函數&#xff09;的對應關系。 本質&#xff1a;將用戶請求的URL路徑映射到具體的處理程序&#xff08;如Django視圖函數&#xff09;。 示例&#xff1a; # urls.py urlpatterns [ path(home/, views.home_…

理解 results = model(source, stream=True) 的工作原理和優勢

1. 核心概念解析 (1) streamTrue 的作用 生成器模式&#xff1a;當處理視頻或圖像序列時&#xff0c;streamTrue 會將結果包裝成一個 生成器&#xff08;Generator&#xff09;&#xff0c;逐幀生成 Results 對象&#xff0c;而不是一次性返回所有結果。內存優化&#xff1a;…

重新定義“邊緣”:邊緣計算如何重塑人類與數據的關系

在數字化浪潮中&#xff0c;云計算曾是科技界的寵兒&#xff0c;但如今&#xff0c;邊緣計算正在悄然改變游戲規則。它不僅是一種技術進步&#xff0c;更是對人類與數據關系的一次深刻反思。本文將探討邊緣計算如何從“中心化”走向“分布式”&#xff0c;以及它如何在效率、隱…