【iOS】源碼閱讀(五)——類類的結構分析

文章目錄

  • 前言
  • 類的分析
    • 類的本質
    • objc_class 、objc_object和NSObject
      • objc_object:所有對象的基類型
      • objc_class:類的底層結構
      • NSObject:面向用戶的根類
    • 小結
  • 指針內存偏移
    • 普通指針----值拷貝
    • 對象----指針拷貝或引用拷貝
    • 用數組指針引出----內存偏移
  • 類的結構
    • Class ISA
    • Class surperclass
    • cache_t cache
    • class_data_bits_t bits
  • 總結

前言

??本篇博客主要是筆者在學習類&類的結構的底層探索和分析時所作的筆記,主要涉及實例對象的類以及類的結構。Objective-C的類結構是其動態性和面向對象特性的核心,理解類的內存布局和內部機制,對開發、調試和性能優化至關重要。

類的分析

類的本質

??在 OC 中,類(Class)本身是一個對象(objc_class 結構體),在探索類的本質之前,我們先在main文件里自定義一個繼承自NSobjetc的TCJPerson類:

#import <Foundation/Foundation.h>
#import <objc/runtime.h>@interface TCJPerson : NSObject@end@implementation TCJPerson@endint main(int argc, const char * argv[]) {@autoreleasepool {TCJPerson *person = [TCJPerson alloc];Class cls = object_getClass(person);}return 0;
}

然后利用clang工具將oc語言的main文件輸出為cpp文件,命令行如下:

clang -rewrite-objc main.m -o main.cpp

將文件拖到我們的objc源碼中打開,方便我們調試和查找,可以發現,這段代碼在cpp文件文件中如下:

//類型定義部分
#ifndef _REWRITER_typedef_TCJPerson
#define _REWRITER_typedef_TCJPerson
typedef struct objc_object TCJPerson; //將 TCJPerson 定義為 objc_object 結構體
typedef struct {} _objc_exc_TCJPerson; //空結構體,用于異常處理占位(實際未使用)
#endif//類的實現結構
struct TCJPerson_IMPL {struct NSObject_IMPL NSObject_IVARS; //繼承自 NSObject 的實例變量
};/* @end */// @implementation TCJPerson
// @end
//main函數的底層轉換
int main(int argc, const char * argv[]) {/* @autoreleasepool */ { __AtAutoreleasePool __autoreleasepool; TCJPerson *person = ((TCJPerson *(*)(id, SEL))(void *)objc_msgSend)((id)objc_getClass("TCJPerson"), sel_registerName("alloc"));//objc_getClass("TCJPerson"):獲取 TCJPerson 的類對象(Class)//sel_registerName("alloc"):注冊方法名 alloc,返回 SEL 類型的選擇子//objc_msgSend:發送 alloc 消息給 TCJPerson 類,創建實例//強制類型轉換 (TCJPerson *(*)(id, SEL))(void *) 是為了匹配 objc_msgSend 的函數指針簽名Class cls = object_getClass(person);//獲取 person 實例的類(即 TCJPerson 的類對象)}return 0;
}
  • TCJPerson 被定義為 objc_object,說明在底層,Objective-C 類實例的本質就是 objc_object 結構體。
  • 因為 TCJPerson 繼承自 NSObject,所以它的底層結構會包含父類的實例變量。其中,NSObject_IVARS 就是 NSObject 的實例變量,通常就是 isa 指針(指向類的元數據)。

然后我們發現類在底層是用class接收的。
在這里插入圖片描述

typedef struct objc_class *Class;

在左側搜索欄查找objc_class,我們可以發現objc_class繼承自objc_object。

請添加圖片描述

點擊進入objc_object的源碼實現中:

在這里插入圖片描述
小結

  • Objective-C 對象的本質:
    類實例(如 person)本質是 objc_object 結構體,包含 isa 指針(來自 NSObject_IVARS)。類(如 TCJPerson)本質是 objc_class 結構體,繼承自 objc_object。所以滿足萬物皆對象。
  • 方法調用的本質:[TCJPerson alloc] 被編譯為 objc_msgSend 的調用,動態查找并執行方法。
  • 內存布局:TCJPerson_IMPL 只包含 NSObject 的 isa,因為沒有自定義實例變量。

objc_class 、objc_object和NSObject

objc_object:所有對象的基類型

底層源碼:

struct objc_object {Class _Nonnull isa  OBJC_ISA_AVAILABILITY; //必須為非空的類指針(即指向 objc_class 結構體的指針)
};

這證明objc_object是所有 Objective-C 對象的最底層結構,包括實例對象、類對象、元類對象。其通過 isa 指針實現對象與類的關聯(即“對象是什么類”)。

特點:

實例對象的 isa 指向它的類(如 TCJPerson 實例的 isa 指向 TCJPerson 類)。
類對象的 isa 指向元類(Meta Class)。
元類的 isa 指向根元類(Root Meta Class)。

關于類 元類 根元類

  1. ??類(Class)??
    ??作用??:定義對象的??實例變量??和??實例方法??(如 -init、-description)。
    ??內存結構??:每個類是一個 objc_class 結構體實例,包含方法列表、父類指針、緩存等。
    ??對象關系??:實例對象(如 MyObject *obj)的 isa 指針指向其類對象。
  2. ??元類(Meta-class)??
    ??作用??:定義類的??類方法??(如 +alloc、+new)。
    元類本身也是一個類,它的實例是類對象。
    ??內存結構??:元類也是一個 objc_class 結構體,但其方法列表存儲類方法。
    ??對象關系??:類對象(如 MyObject.class)的 isa 指針指向其元類。
  3. ??根元類(Root Meta-class)??
    ??作用??:所有元類的最終基類,通常是 NSObject 的元類。
    根元類的類方法(如 +alloc)會被所有類的元類繼承。
    根元類的 isa 指針指向自身,形成閉環。

在這里插入圖片描述

??Instance (MyObject)??: 一個對象實例
??Class (MyObject class)??: 定義該實例的類
??Meta-class (MyObject meta)??: 定義該類的元類

isa 指針:形成繼承鏈的核心,每個實例、類和元類都有一個指向其類型的指針
實例通過 isa 指向類,類通過 isa 指向元類,元類通過 isa 指向根元類
super_class 指針:形成繼承體系
類通過 super_class 指向父類,元類通過 super_class 指向父元類

??Root Meta-class (NSObject meta)??: 所有元類的根元類
根元類的 isa 指向自己(self),形成閉環
根元類的 super_class 指向 NSObject 類

運行時??方法查找路徑??:
實例方法首先在實例所屬的類中查找
如果沒找到,則沿著 super_class 鏈向上查找
類方法則在元類及其父元類中查找
??
繼承機制??:實例繼承自類,類繼承自元類,元類繼承自父元類,最終根元類繼承自NSObject類

objc_class:類的底層結構

底層代碼:

struct objc_class : objc_object { //繼承自objc_objectobjc_class(const objc_class&) = delete;objc_class(objc_class&&) = delete;void operator=(const objc_class&) = delete;void operator=(objc_class&&) = delete;// Class ISA;Class superclass;            //父類指針cache_t cache;             // formerly cache pointer and vtable  //方法緩存class_data_bits_t bits;    // class_rw_t * plus custom rr/alloc flags  //類的方法、屬性、協議等數據Class getSuperclass() const {
#if __has_feature(ptrauth_calls)
#   if ISA_SIGNING_AUTH_MODE == ISA_SIGNING_AUTHif (superclass == Nil)return Nil;......
}

這說明objc_class是 objc_object 的子類,說明類本身也是對象(即“類對象”)。
存儲類的元數據:方法列表、屬性列表、協議列表、父類指針等。
因為其繼承自 objc_object,所以其類對象也有 isa 指針(指向元類)。
其中,Class 是指向 objc_class 的指針(typedef struct objc_class *Class)。

NSObject:面向用戶的根類

底層代碼:

@interface NSObject <NSObject> {
#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Wobjc-interface-ivars"Class isa  OBJC_ISA_AVAILABILITY; //指向類對象的指針
#pragma clang diagnostic pop
}

NSObject 是 Objective-C 中所有類的根類(除了 NSProxy),提供面向開發者的基礎方法(如 alloc、init、description)。

與 objc_object 的關系:
NSObject 的實例對象在底層就是 objc_object。
NSObject 的類對象在底層是 objc_class。
編譯器會將 NSObject 的代碼轉換為對 objc_object 和 objc_class 的操作。

小結

通過對objc_class、objc_object和NSObject底層的大致了解,我們就可以明白三者存在以下金字塔關系:
請添加圖片描述
objc_object:所有對象的終極基類(C結構體)
objc_class:繼承objc_object,說明"類也是對象"
NSObject:繼承鏈最頂端的公開類,封裝了objc_class的面向對象接口

小結

所有的對象 + 類 + 元類 都有isa屬性
所有的對象都是由objc_object繼承來的
簡單概括就是萬物皆對象,萬物皆來源于objc_object,有以下兩點結論:
所有以 objc_object為模板創建的對象,都有isa屬性
所有以 objc_class為模板創建的類,都有isa屬性

在結構層面可以通俗的理解為上層OC 與 底層的對接:

下層是通過 結構體 定義的 模板,例如objc_class、objc_object
上層是通過底層的模板創建的一些類型,例如TCJPerson

objc_class、objc_object、isa、object、NSObject等的整體的關系如下圖:
請添加圖片描述

指針內存偏移

??在分析類的結構之前,我們需要先來學習一下指針內存偏移作為前綴知識。

普通指針----值拷貝

        //普通指針//值拷貝int a = 10;int b = 10;NSLog(@"&a:%d--%p", a, &a);NSLog(@"&b:%d--%p", b, &b);

請添加圖片描述

通過代碼及其運行結果可以看出來,變量a和b雖然都是被賦值為10,但是變量a和b的內存地址是不一樣的,我們稱這種方式為值拷貝。

對象----指針拷貝或引用拷貝

        //對象NSObject *obj1 = [[NSObject alloc] init];NSObject *obj2 = [[NSObject alloc] init];NSLog(@"%@--%p", obj1, &obj1);NSLog(@"%@--%p", obj2, &obj2);

在這里插入圖片描述
通過運行結果,我們可以看到obj1和obj2對象不僅自身內存地址不一樣,其指向的對象的內存地址也不一樣,這被稱為指針拷貝或引用拷貝。

用數組指針引出----內存偏移

        //數組指針int arr[4] = {1, 2, 3, 4};int *c = arr;NSLog(@"&arr:%p--%p--%p", arr, &arr[0], &arr[1]);NSLog(@"&c:%p--%p--%p", c, c + 1, c + 2);for (int i = 0; i < 4; i++) {int value = *(c + i);NSLog(@"value:%d", value);}

請添加圖片描述

通過運行結果可以看到:

  • &a和&a[0]的地址是相同的——即首地址就代表數組的第一個元素的地址。
  • 第一個元素地址0x16fdff2f8和第二個元素地址0x16fdff2fc相差4個字節,也就是int的所占的4字節,因為他們的數據類型相同。
  • d、d+1、d+2這個地方的指針相加就是偏移地址。地址加1就是偏移,偏移一個位數所在元素的大小。
  • 可以通過地址,取出對應地址的值。

小結
在這里插入圖片描述

類的結構

從objc_class的定義可以得出,類有4個屬性:isa、superclass、cache、bits。

Class ISA

不但實例對象中有isa指針,類對象中也有isa指針關聯著元類。
Class本身就是一個指針,占用8字節。

Class surperclass

顧名思義就是類的父類(一般為NSObject)superclass是Class類型,所以占用8字節。

cache_t cache

進入cache_t的實現源碼,我們能看到(筆者對部分進行了注釋,方便理解):

struct cache_t {
private:explicit_atomic<uintptr_t> _bucketsAndMaybeMask; //原子存儲緩存桶指針或掩碼(根據配置不同復用同一內存)//原子性??:保證并發訪問時的線程安全(如 objc_msgSend 高頻調用場景)union {// Note: _flags on ARM64 needs to line up with the unused bits of// _originalPreoptCache because we access some flags (specifically// FAST_CACHE_HAS_DEFAULT_CORE and FAST_CACHE_HAS_DEFAULT_AWZ) on// unrealized classes with the assumption that they will start out// as 0.struct {
#if CACHE_MASK_STORAGE == CACHE_MASK_STORAGE_OUTLINED && !__LP64__// Outlined cache mask storage, 32-bit, we have mask and occupied.explicit_atomic<mask_t>    _mask; //緩存掩碼uint16_t                   _occupied; //已占用槽位數
#elif CACHE_MASK_STORAGE == CACHE_MASK_STORAGE_OUTLINED && __LP64__// Outlined cache mask storage, 64-bit, we have mask, occupied, flags.explicit_atomic<mask_t>    _mask;uint16_t                   _occupied;uint16_t                   _flags; //狀態標志(FAST_CACHE_HAS_DEFAULT_CORE: 是否有默認核心實現 FAST_CACHE_HAS_CUSTOM_DEALLOC_INITIATION: 是否自定義釋放邏輯)
#   define CACHE_T_HAS_FLAGS 1
#elif __LP64__// Inline cache mask storage, 64-bit, we have occupied, flags, and// empty space to line up flags with originalPreoptCache.//// Note: the assembly code for objc_release_xN knows about the// location of _flags and the// FAST_CACHE_HAS_CUSTOM_DEALLOC_INITIATION flag within. Any changes// must be applied there as well.uint32_t                   _unused;uint16_t                   _occupied;uint16_t                   _flags;
#   define CACHE_T_HAS_FLAGS 1
#else// Inline cache mask storage, 32-bit, we have occupied, flags.uint16_t                   _occupied;uint16_t                   _flags;
#   define CACHE_T_HAS_FLAGS 1
#endif

cache在英文中的意思是緩存。
cache_t是一個結構體,內存長度由所有元素決定:

_bucketsAndMaybeMask是long類型,它是一個指針,占用8字節;
mask_t是個uint32_t類型,_mask占用4字節;
_occupied和_flags都是uint16_t類型,uint16_t是 unsigned short 的別名,所以_occupied占用2字節;
_flags占用2字節;
所以,cache_t共占用16字節。

這里對cache簡單了解一下,后面還會呢詳細學習。

class_data_bits_t bits

class_data_bits_t實現源碼如下:

struct class_data_bits_t {friend objc_class;// Values are the FAST_ flags above.uintptr_t bits;
private:bool getBit(uintptr_t bit) const{return bits & bit;}// Atomically set the bits in `set` and clear the bits in `clear`.// set and clear must not overlap.  If the existing bits field is zero,// this function will mark it as using the RW signing scheme.void setAndClearBits(uintptr_t set, uintptr_t clear){ASSERT((set & clear) == 0);uintptr_t newBits, oldBits = LoadExclusive(&bits);do {uintptr_t authBits= (oldBits? (uintptr_t)ptrauth_auth_data((class_rw_t *)oldBits,CLASS_DATA_BITS_RW_SIGNING_KEY,ptrauth_blend_discriminator(&bits,CLASS_DATA_BITS_RW_DISCRIMINATOR)): FAST_IS_RW_POINTER);newBits = (authBits | set) & ~clear;newBits = (uintptr_t)ptrauth_sign_unauthenticated((class_rw_t *)newBits,CLASS_DATA_BITS_RW_SIGNING_KEY,ptrauth_blend_discriminator(&bits,CLASS_DATA_BITS_RW_DISCRIMINATOR));} while (slowpath(!StoreReleaseExclusive(&bits, &oldBits, newBits)));}void setBits(uintptr_t set) {setAndClearBits(set, 0);}void clearBits(uintptr_t clear) {setAndClearBits(0, clear);}public:void copyRWFrom(const class_data_bits_t &other) {bits = (uintptr_t)ptrauth_auth_and_resign((class_rw_t *)other.bits,CLASS_DATA_BITS_RW_SIGNING_KEY,ptrauth_blend_discriminator(&other.bits,CLASS_DATA_BITS_RW_DISCRIMINATOR),CLASS_DATA_BITS_RW_SIGNING_KEY,ptrauth_blend_discriminator(&bits,CLASS_DATA_BITS_RW_DISCRIMINATOR));}void copyROFrom(const class_data_bits_t &other, bool authenticate) {ASSERT((flags() & RO_REALIZED) == 0);if (authenticate) {bits = (uintptr_t)ptrauth_auth_and_resign((class_ro_t *)other.bits,CLASS_DATA_BITS_RO_SIGNING_KEY,ptrauth_blend_discriminator(&other.bits,CLASS_DATA_BITS_RO_DISCRIMINATOR),CLASS_DATA_BITS_RO_SIGNING_KEY,ptrauth_blend_discriminator(&bits,CLASS_DATA_BITS_RO_DISCRIMINATOR));} else {bits = other.bits;}}

雖然我們不是很能看懂,但至少能看出來這里是oc運行時用來存儲數據的。
根據上面的分析,class指針各為8字節,cache_t cache為16字節,所以想要獲取bits的中的內容,只需通過類的首地址平移32字節即可。

總結

??在學習過程中,筆者關于LLDB調試出了比較多的問題,至今還沒解決,所以這篇筆記還有待完善,等筆者解決完問題后,會再次進行編撰,LLDB在源碼學習中還是很不錯的工具。

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

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

相關文章

Baklib構建企業CMS高效協作與安全管控體系

企業CMS高效協作體系構建 基于智能工作流引擎的設計邏輯&#xff0c;現代企業內容管理系統通過預設多節點審核路徑與自動化任務分配機制&#xff0c;有效串聯市場、技術、法務等跨部門協作鏈路。系統支持多人同時編輯與版本追溯功能&#xff0c;結合細粒度權限管控模塊&#x…

Linux環境變量與地址空間

哈嘍&#xff0c;各位Linux初學者們&#xff01;今天咱們來聊聊Linux中那兩個看起來很高大上但實際上跟我們日常使用息息相關的概念&#xff1a;環境變量和地址空間。別被這些術語嚇到&#xff0c;我會用最接地氣的方式給你解釋清楚&#xff01; 一、環境變量&#xff1a;Linu…

Oracle SHARED POOL的SUB POOL技術

從Oracle 9i開始&#xff0c;SHARED POOL可以分為多個SUB POOL&#xff0c;其數量受以下幾個因素影響&#xff1a; ?系統CPU的數量。默認情況下&#xff0c;在Oracle中每4個CPU分配一個SUB POOL&#xff0c;最多不能超過7個。 ?共享池的大小。SUB POOL的最小容量隨著Oracle版…

Collection集合遍歷的三種方法

1.foreach循環遍歷 格式&#xff1a;for&#xff08;元素的數據類型 變量名&#xff1a;數組或集合&#xff09;{ } 2.使用迭代器遍歷 方法名稱&#xff1a;Iterator<E> iterator&#xff08;&#xff09; 說明&#xff1a;返回集合中的迭代器對象&#xff0c;該迭代…

頭歌之動手學人工智能-Pytorch 之autograd

目錄 第1關&#xff1a;Variable 任務描述 編程要求 測試說明 沒有偉大的愿望&#xff0c;就沒有偉大的天才。——巴爾扎克開始你的任務吧&#xff0c;祝你成功&#xff01; 第2關&#xff1a;Variable 屬性 任務描述 編程要求 測試說明 真正的科學家應當是個幻想家&a…

篇章二 數據結構——前置知識(二)

目錄 1. 包裝類 1.1 包裝類的概念 1.2 基本數據類型和對應的包裝類 1.3 裝箱和拆箱 1.4 自動裝箱和自動拆箱 1.5 練習 —— 面試題 2. 泛型 2.1 如果沒有泛型——會出現什么情況&#xff1f; 2.2 語法 2.3 裸類型 1.沒有寫<> 但是沒有報錯為什么&#xff1f; …

Git典型使用場景相關命令

Git典型使用場景相關命令 1 建立本地倉庫與遠程倉庫的聯系2 作為開發者參與項目的常用命令2-1 一般步驟2-2 **合并與同步主分支改動**2-3 **查看日志和差異**2-4 **提交后想修改或撤銷**2-5 分支管理2-6 清除未被追蹤的文件&#xff08;謹慎使用&#xff09; 3 作為遠程倉庫管理…

redis緩存-更新策略-三大緩存問題

緩存&#xff1a;數據交換的緩沖區&#xff0c;存儲的數據的臨時地方&#xff0c;讀寫性能較高。 步驟&#xff1a; 先從redis里面查詢 緩存命中&#xff1a;直接返回結果緩存未命中 從數據庫里面查詢 沒有數據&#xff1a;返回null有數據&#xff1a;存到redis里面&#xff…

[TriCore] 01.QEMU 虛擬化 TriCore 架構中的寄存器 指令

目錄 1.寄存器宏 - FIELD() 2.寄存器操作 - FIELD_SETTER() & FIELD_GETTER() 3.指令輔助方法 - HELPER() 3.1.輔助宏 3.2.指令示例 3.3.函數調用 4.PSW 寄存器讀寫 - psw_read() & psw_write() 1.寄存器宏 - FIELD() FIELD() 宏定義寄存器 MASK // include/hw…

《軟件工程》第 4 章 - 需求獲取

在軟件工程中&#xff0c;需求獲取是挖掘用戶真實需求的關鍵步驟&#xff0c;它為后續的設計、開發和測試提供堅實基礎。本章將圍繞需求獲取的流程、方法及工具展開&#xff0c;結合實際案例與 Java 代碼&#xff0c;深入講解這一重要環節。 4.1 軟件需求的初始表示 4.1.1 用例…

react diff 算法

diff 算法作為 Virtual DOM 的加速器&#xff0c;其算法的改進優化是 React 整個界面渲染的基礎和性能的保障&#xff0c;同時也是 React 源碼中最神秘的&#xff0c;最不可思議的部分 diff 算法會幫助我們就算出 VirtualDOM 中真正變化的部分&#xff0c;并只針對該部分進行原…

Gin項目腳手架與標配組件

文章目錄 前言設計思想和原則? 技術棧視頻實況教程sponge 內置了豐富的組件(按需使用)幾個標配常用組件主要技術點另一個參考鏈接 前言 軟件和汽車一樣&#xff0c;由多個重要零部件組裝而成。 本文堆積了一些常用部件&#xff0c;還沒來得及好好整理。先放著。 神兵利器雖多…

【Webtrees 手冊】第 10章 - 用戶體驗

Webtrees 手冊/用戶體驗 < Webtrees 手冊 跳轉到導航跳轉到搜索 信息 手冊部分仍在建設中 請耐心等待或隨意貢獻自己的力量:-)。 第 10 章 - 用戶體驗 <- 章節概述 目錄 1多位系譜學家的合作 1.1家庭研究模型1.2“孤膽戰士”模型1.3示范“本地家庭書”1.4模特“俱樂部”…

Linux 進程概念(下)

目錄 前言 4.進程狀態 一.普遍的操作系統層面上宏觀概念&#xff1a; 二.具體的Linux操作系統的狀態&#xff1a; 5.進程優先級&#xff08;了解&#xff09; 6.其他概念 進程切換 前言 本篇是接著上一篇的內容繼續往下了解進程相關的一些概念&#xff01; 4.進程狀態 運…

使用java實現word轉pdf,html以及rtf轉word,pdf,html

word,rtf的轉換有以下方案&#xff0c;想要免費最靠譜的是LibreOffice方案, LibreOffice 是一款 免費、開源、跨平臺 的辦公軟件套件&#xff0c;旨在為用戶提供高效、全面的辦公工具&#xff0c;適用于個人、企業和教育機構。它支持多種操作系統&#xff08;Windows、macOS、…

IP證書的作用與申請全解析:從安全驗證到部署實踐

在網絡安全領域&#xff0c;IP證書&#xff08;IP SSL證書&#xff09;作為傳統域名SSL證書的補充方案&#xff0c;專為公網IP地址提供HTTPS加密與身份驗證服務。本文將從技術原理、應用場景、申請流程及部署要點四個維度&#xff0c;系統解析IP證書的核心價值與操作指南。 一…

GitLab 18.0 正式發布,15.0 將不再受技術支持,須升級【三】

GitLab 是一個全球知名的一體化 DevOps 平臺&#xff0c;很多人都通過私有化部署 GitLab 來進行源代碼托管。極狐GitLab 是 GitLab 在中國的發行版&#xff0c;專門為中國程序員服務。可以一鍵式部署極狐GitLab。 學習極狐GitLab 的相關資料&#xff1a; 極狐GitLab 官網極狐…

超簡單Translation翻譯模型部署

Helsinki-NLP/opus-mt-{en}-{zh}系列翻譯模型可以實現200多種語言翻譯&#xff0c;Helsinki-NLP/opus-mt-en-zh是其中英互譯模型。由于項目需要&#xff0c;在本地進行搭建&#xff0c;并記錄下搭建過程&#xff0c;方便后人。 1. 基本硬件環境 CPU&#xff1a;N年前的 Intel…

Go語言JSON 序列化與反序列化 -《Go語言實戰指南》

JSON&#xff08;JavaScript Object Notation&#xff09;是一種常見的數據交換格式。Go 標準庫提供了 encoding/json 包&#xff0c;用于方便地將結構體與 JSON 之間互轉。 一、序列化&#xff08;Marshal&#xff09; 將 Go 中的數據結構&#xff08;如結構體、map、slice 等…

免費PDF工具-PDF24V9.16.0【win7專用版】

【百度】https://pan.baidu.com/s/1H7kvHudG5JTfxHg-eu2grA?pwd8euh 提取碼: 8euh 【夸克】https://pan.quark.cn/s/92080b2e1f4c 【123】https://www.123912.com/s/0yvtTd-XAHjv https://creator.pdf24.org/listVersions.php