iOS-Swift 結構體與類

2022-01-10 邏輯iOS技術號


在 Swift 的標準庫中,絕大多數的公開類型都是結構體,而枚舉和類只佔很小一部分。比如 Bool、Int、Double、 String、Array、Dictionary 等常見類型都是結構體。

 

我們現在定義一個結構體。

struct SHPerson {var age: Intvar name: String}
let p = SHPerson(age: 18, name: "Coder_張三")

(滑動顯示更多)

所有的結構體都有一個編譯器自動生成的初始化器(initializer,初始化方法、構造器、構造方法)。如上代碼,可以傳入所有成員值,用以初始化所有成員(存儲屬性,Stored Property)。

編譯器根據情況,可能會為結構體生成多個初始化器。前提是保證 所有成員都有初始值。

一但在定義結構體時自定義初始化器,編譯器就不會幫自動生成其他初始化器。

struct SHPerson {var age: Intvar name: String
init(age: Int) {self.age = ageself.name = "Coder_張三" }}
let p = SHPerson(age: 0)

(滑動顯示更多)

在對結構體進行初始化的時候,必須保證結構體的成員都值,所以當我們對結構體的某個成員變量設置初始值時,生成的初始化器可以不用傳該成員變量的參數賦值。

我們來看下面這個結構體,SHPerson 有 age,weight,sex 三個成員。

struct SHPerson {var age: Intvar weight: Intvar sex: Bool}
print(MemoryLayout<SHPerson>.size) // 17print(MemoryLayout<SHPerson>.stride) // 24print(MemoryLayout<SHPerson>.alignment) // 8

(滑動顯示更多)

列印出其內存對齊字節數和佔用的內存,在 64 位系統下,結構體中 Int 佔 8 字節,Bool 佔一個字節 ,所以 SHPerson 一共佔 17 個字節,但是因為要遵守內存對齊原則(8個字節),所以系統會分配 24 個字節來存儲 SHPerson。

類的定義和結構體類似,但編譯器並沒有為類自動生成可以傳入成員值的初始化器。

當類的成員沒有初始值時,必須自定義初始化器,初始化成員值。

class SHPerson {var age: Intvar name: String
init(age: Int, name: String) {self.age = age;self.name = name; }}
let p = SHPerson(age: 18, name: "Coder_張三")

(滑動顯示更多)

如果類的所有成員都在定義的時候指定了初始值,編譯器會為類生成無參的初始化器,成員的初始化是在這個初始化器中完成的。

class SHPerson {var age: Int = 18var name: String = "Coder_張三"}
let p = SHPerson()

(滑動顯示更多)

當初始化的值不滿足某個條件時我們需要給初始化方法返回一個nil,那麼可以在 init 後面加上一個可選項來修飾。

class SHPerson {var age: Intvar name: String
init?(age: Int, name: String) {if age < 18 { return nil}self.age = ageself.name = name }}
let p1 = SHPerson(age: 16, name: "Coder_ 張三")let p2 = SHPerson(age: 18, name: "Coder_李四")print("p1 - \(String(describing: p1))")print("p2 - \(String(describing: p2))")

(滑動顯示更多)

列印結果:p1 - nilp2 - Optional(_1_結構體與類.SHPerson)

(滑動顯示更多)

例如,當 SHPerson 不滿 18 歲時返回 nil,屬於未成年人。

 

必要初始化器需要在 init 前用 required 修飾。

class SHPerson {var age: Intvar name: String// 父類定義必要實現初始化器required init(age: Int, name: String) {self.age = ageself.name = name    }}
class SHStudent: SHPerson {var height: Int
init(height: Int) {self.height = heightsuper.init(age: 18, name: "Coder_ 張三") }
// 子類必須實現父類的必要初始化器required init(age: Int, name: String) {fatalError("init(age:name:) has not been implemented") }}

(滑動顯示更多)

如代碼所示,當在 init 前修飾 required,該類的子類都必須實現該初始化器。

 

我們可以為類提供一個便捷初始化器,便捷初始化器需要在 init 前用 convenience 修飾。

class SHPerson {var age: Intvar name: String
init(age: Int, name: String) {self.age = ageself.name = name }
convenience init() {self.init(age: 18, name: "Coder_ 張三") }}

(滑動顯示更多)

如代碼所示,便捷初始化器必須從相同的類裡調用另一個初始化器,並且最終必須調用一個指定初始化器。

結構體與類的本質區別為結構體是值類型,類是引用類型(其實也可以理解為指針類型)。那麼它們還有一個最直觀的區別就是存儲的位置不同:一般情況,值類型存儲的在棧上,引用類型存儲在堆上。

class SHPerson {var age = 18var height = 180}
struct SHPoint {var x = 0;var y = 0;}
func test() {let point = SHPoint()let person = SHPerson()}

(滑動顯示更多)

SHPoint 在初始化完成賦值給 point 後,SHPoint 的內存數據直接就是放在棧空間。而 SHPerson 在初始化完成賦值給 person 後,person 只是一個引用地址,這個地址存的內存數據為 SHPerson 的內存地址,該內存地址放在堆空間。

值類型賦值給 var、let 或者給函數傳參,是直接將所有內容拷貝一份。類似於對文件進行 copy、paste操作,產生了全新的文件副本。屬於深拷貝(deep copy)。

struct SHPoint {var x = 4;var y = 8;}
var p1 = SHPoint()var p2 = p1;
p2.x = 6
print("p1 - \(p1)")print("p2 - \(p2)")

列印結果:p1 - SHPoint(x: 4, y: 8)p2 - SHPoint(x: 6, y: 8)

我們可以看到在修改 p2 的 x 後,對 p1 並沒有影響,這屬於深拷貝。我們來看數組的列印結果。

var a1 = [1, 2, 3]var a2 = a1a2.append(4)a1[0] = 2print(a1)print(a2)

列印結果:[2, 2, 3][1, 2, 3, 4]

在 Swift 標準庫中,為了提升性能,String、Array、Dictionary、Set 採取了Copy On Write 的技術, 比如僅當有「寫」操作時,才會真正執行拷貝操作。

對於標準庫值類型的賦值操作,Swift 能確保最佳性能,所有沒必要為了保證最佳性能來避免賦值。

建議:不需要修改的,儘量定義成 let。

引用賦值給var、let或者給函數傳參,是將內存地址拷貝一份。類似於製作一個文件的替身(快捷方式、連結),指向的是同一個文件。屬於淺拷貝(shallow copy)。

class SHPerson {var age: Int = 18var name: String = "Coder_張三"}
let p1 = SHPerson()let p2 = p1
print("p1-age: \(p1.age)")p2.age = 20print("p1-age: \(p1.age)")print("p2-age: \(p2.age)")

(滑動顯示更多)

列印結果:p1-age: 18p1-age: 20p2-age: 20

在Swift中,創建類的實例對象,要向堆空間申請內存,大概流程如下:

 

Class.__allocating_init()

libswiftCore.dylib: swift_allocObject

libswiftCore.dylib: swift_slowAlloc

libsystem_malloc.dylib: malloc

 

在Mac、iOS中的 malloc 函數分配的內存大小總是16 的倍數。

 

class CGPoint  {var x = 11var y = 22var test = true}var p = CGPoint()print(class_getInstanceSize(CGPoint.self))print(malloc_size(unsafeBitCast(p, to: UnsafeRawPointer.self)))

(滑動顯示更多)

通過列印得知,CGPoint 的大小為 40 個字節,系統分配 CGPoint 的內存大小為 48 個字節。

在 CGPoint 中,x 佔 8 個字節,y 佔 8 個字節,test 佔 1 個字節,所以目前我們看到的有 17 個字節。但是因為類存儲在堆空間中,它前面會有 8 個字節存放類型信息,8個字節存引用計數,再加上面的,加起來一共是 33 個字節,根據內存對齊原則(8 個字節),所以 CGPoint 的大小為 40 個字節。

因為在Mac、iOS中的 malloc 函數分配的內存大小總是16 的倍數,所以最終系統會分配 CGPoint 的內存大小為 48 字節。

 

結構體與類的使用方式很相似,那麼在平時開發中使用結構體比較好還是類比較好呢?這種時候分情況,如果定義的數據結構比較簡單的情況下,建議用結構體,比如 Model。如果定義的數據結構比較複雜的話,建議用類,比如需要用到多態的時候。

 

 

StructVsClassPerformance demo 測試

我們可以通過 github 上的 上 StructVsClassPerformance 這個 demo 來直觀的測試當前結構體和類的時間分配。

具體的代碼就不去貼出來了,我們來看一下調用方式以及列印結果:

列印結果:Running tests
class (1 field)9.566281178005738
struct (1 field)6.391943185008131
class (10 fields)10.430800677015213
struct (10 fields)6.610909776005428

通過列印結果可以直觀的看到,結構體對比類的時間分配時要快將近一倍的速度。

 

iOS 開發的語言不管是 OC 還是 Swift,後端都是通過 LLVM 進行編譯的,如下圖所示:

OC 通過 clang 編譯器,編譯成 IR,然後再生成可執行文件 .o(這裡也就是我們的機器碼)。Swift 則是通過 Swift 編譯器編譯成 IR,然後在生成可執行文件。

首先 Swift Code 經過 -dump-parse 進行語義分析解析成 Parse(抽象語法樹)。

Parse 經過 -dump-ast 進行語義分析分析語法是否正確,是否安全。

Seam 之後會把 Swift Code 會降級變成 SILGen(Swift 中間代碼),對於 SILGen 又分為原生的(Raw SIL)和經過優化的(SIL Opt Canonical SIL)。

優化完成的 SIL 會由 LLVM 降級成為 IR,降級成 IR 之後由後端代碼編譯成機器碼。

 

以上就是 Swift 的編譯流程,下面為編譯流程的命令。

分析輸出 AST:

// 分析輸出ASTswiftc main.swift -dump-parse
// 分析並且檢查類型輸出ASTswiftc main.swift -dump-ast
// 生成中間體語言(SIL),未優化swiftc main.swift -emit-silgen
// 生成中間體語言(SIL),優化後的swiftc main.swift -emit-sil
// 生成LLVM中間體語言 (.ll文件)swiftc main.swift -emit-ir
// 生成LLVM中間體語言 (.bc文件)swiftc main.swift -emit-bc
// 生成彙編swiftc main.swift -emit-assembly
// 編譯生成可執行.out文件swiftc -o main.o main.swift

(滑動顯示更多)

將以下代碼編譯成 sil 代碼:

import Foundation
class SHPerson {var age = 18var name = "Coder_張三"}
let p = SHPerson()

終端 cd 到項目的 main.swift 目錄,輸入 swiftc main.swift -emit-sil 並按回車鍵,會在生成一個 main.sil 文件,並且會在終端輸出 SIL 代碼。SIL 代碼如下:

關於 SIL 的語法說明,其實也有相應的文檔。這裡貼上文檔說明的地址:SIL參考文檔

 

接下來我們通過彙編來查看類的初始化流程,我們打個斷點如下:

接下來打開彙編調試

通過彙編查看,SHPerson 在進行初始化的時候,在底層會調用 __allocating_init 的函數,那麼 __allocating_init 做了什麼事情呢,跟進去看一下。

讓斷點走到 __allocating_init 這一行代碼,按住 control 鍵,點擊這個向下的按鈕。

可以看到,進入到 __allocating_init 的內部實現後,發現它會調用一個 swift_allocObject 函數,那麼在繼續跟彙編的時候跟丟了。

接下來我們來看一下源碼。源碼可以去蘋果官網下-swift源碼下載地址。用 VSCode 打開下載好的 swift 源碼,全局搜索 swift_allocObject 這個函數。

在 HeapObject.cpp 文件中找到 swift_allocObject 函數的實現,並且在 swift_allocObject 函數的實現上方,有一個 _swift_allocObject_ 函數的實現。

// 第一個參數,元數據。// 第二個參數,分配內存的大小// 第三個參數,內存對齊,值一般為 7,因為遵守8位元組對齊static HeapObject *_swift_allocObject_(HeapMetadata const *metadata,size_t requiredSize,size_t requiredAlignmentMask) {assert(isAlignmentMask(requiredAlignmentMask));auto object = reinterpret_cast<HeapObject *>(   swift_slowAlloc(requiredSize, requiredAlignmentMask));
// NOTE: this relies on the C++17 guaranteed semantics of no null-pointer// check on the placement new allocator which we have observed on Windows,// Linux, and macOS.new (object) HeapObject(metadata);
// If leak tracking is enabled, start tracking this object.SWIFT_LEAKS_START_TRACKING_OBJECT(object);
SWIFT_RT_TRACK_INVOCATION(object, swift_allocObject);
return object;}

(滑動顯示更多)

在函數的內部會調用一個 swift_slowAlloc 函數,我們來看下 swift_slowAlloc 函數的內部實現:

void *swift::swift_slowAlloc(size_t size, size_t alignMask) {void *p;// This check also forces "default" alignment to use AlignedAlloc.if (alignMask <= MALLOC_ALIGN_MASK) {#if defined(__APPLE__) p = malloc_zone_malloc(DEFAULT_ZONE(), size);#else p = malloc(size);#endif} else {size_t alignment = (alignMask == ~(size_t(0)))                        ? _swift_MinAllocationAlignment                        : alignMask + 1; p = AlignedAlloc(size, alignment);}if (!p) swift::crash("Could not allocate memory.");return p;}

(滑動顯示更多)

swift_slowAlloc 函數的內部是去進行一些分配內存的操作,比如 malloc。所以就印證了第四點引用類型->對象申請堆空間的過程。

在調用 _swift_allocObject_  函數的時候有一個參數,名為 metadata 的 HeapMetadata。以下是 HeapMetadata 跟進的代碼過程:

//  HeapMetadata 為 TargetHeapMetadata 的別名,InProcess 為泛型。using HeapMetadata = TargetHeapMetadata<InProcess>;

(滑動顯示更多)

template <typename Runtime>struct TargetHeapMetadata : TargetMetadata<Runtime> {using HeaderType = TargetHeapMetadataHeader<Runtime>;
TargetHeapMetadata() = default;constexpr TargetHeapMetadata(MetadataKind kind) : TargetMetadata<Runtime>(kind) {}#if SWIFT_OBJC_INTEROPconstexpr TargetHeapMetadata(TargetAnyClassMetadata<Runtime> *isa) : TargetMetadata<Runtime>(isa) {}#endif};

(滑動顯示更多)

在這裡有對 OC 和 Swift 做兼容。調用的 TargetHeapMetadata 函數的時候,如果是 OC 的類,那麼參數為 isa 指針,否則就是一個 MetadataKind 類型。MetadataKind 是一個 uint32_t 的類型。

enum class MetadataKind : uint32_t {#define METADATAKIND(name, value) name = value,#define ABSTRACTMETADATAKIND(name, start, end)                                 \  name##_Start = start, name##_End = end,#include "MetadataKind.def"
/// The largest possible non-isa-pointer metadata kind value.////// This is included in the enumeration to prevent against attempts to/// exhaustively match metadata kinds. Future Swift runtimes or compilers/// may introduce new metadata kinds, so for forward compatibility, the/// runtime must tolerate metadata with unknown kinds./// This specific value is not mapped to a valid metadata kind at this time,/// however. LastEnumerated = 0x7FF,};

(滑動顯示更多)

那麼 MetadataKind 的種類如下:

name                       Value
Class 0x0Struct 0x200Enum 0x201Optional 0x202ForeignClass 0x203ForeignClass 0x203Opaque 0x300Tuple 0x301Function 0x302Existential 0x303Metatype 0x304ObjCClassWrapper 0x305ExistentialMetatype 0x306HeapLocalVariable 0x400HeapGenericLocalVariable 0x500ErrorObject 0x501LastEnumerated 0x7FF

(滑動顯示更多)

接下來我們找到 TargetHeapMetadata 的繼承 TargetMetadata(在 C++ 中結構體是允許繼承的)。在 TargetMetadata 結構體中找到了 getTypeContextDescriptor 函數,代碼如下:

ConstTargetMetadataPointer<Runtime, TargetTypeContextDescriptor>  getTypeContextDescriptor() const {switch (getKind()) {case MetadataKind::Class: {const auto cls = static_cast<const TargetClassMetadata<Runtime> *>(this);if (!cls->isTypeMetadata())return nullptr;if (cls->isArtificialSubclass())return nullptr;return cls->getDescription();    }case MetadataKind::Struct:case MetadataKind::Enum:case MetadataKind::Optional:return static_cast<const TargetValueMetadata<Runtime> *>(this)          ->Description;case MetadataKind::ForeignClass:return static_cast<const TargetForeignClassMetadata<Runtime> *>(this)          ->Description;default:return nullptr;    }  }

(滑動顯示更多)

可以看到,當 kind 是一個 Class 的時候,會拿到一個名為 TargetClassMetadata 的指針,我們看看 TargetClassMetadata 的實現:

終於看到熟悉的東西了,我們在看看它的繼承 TargetAnyClassMetadata 結構體,可以看到有 superclass,isa 等。

通過以上的分析,我們可以得出,Swift 類中的 metadata 數據結構大致如下:

struct Metadata {var kind: Intvar superClass: Any.Typevar cacheData: (Int, Int)var data: Intvar classFlags: Int32var instanceAddressPoint: UInt32var instanceSize: UInt32var instanceAlignmentMask: UInt16var reserved: UInt16var classSize: UInt32var classAddressPoint: UInt32var typeDescriptor: UnsafeMutableRawPointervar iVarDestroyer: UnsafeRawPointer}

(滑動顯示更多) 

接下來我們做一個測試,通過 lldb 查看 Swift 類的內存結構,那麼既然在 Swift 的底層,_swift_allocObject_ 函數返回的是 HeapObject 的指針類型,我們來看一下 HeapObject 的結構:

struct HeapObject {/// This is always a valid pointer to a metadata object.   HeapMetadata const *__ptrauth_objc_isa_pointer metadata;
SWIFT_HEAPOBJECT_NON_OBJC_MEMBERS;
#ifndef __swift__ HeapObject() = default;
// Initialize a HeapObject header as appropriate for a newly-allocated object.constexpr HeapObject(HeapMetadata const *newMetadata) : metadata(newMetadata) , refCounts(InlineRefCounts::Initialized) { }
// Initialize a HeapObject header for an immortal objectconstexpr HeapObject(HeapMetadata const *newMetadata, InlineRefCounts::Immortal_t immortal) : metadata(newMetadata) , refCounts(InlineRefCounts::Immortal) { }
#ifndef NDEBUGvoid dump() const SWIFT_USED;#endif
#endif // __swift__ };

(滑動顯示更多)

知道了 HeapObject 的源碼結構之後,我們也假裡假氣的模仿源碼,自己定義一個 HeapObject,一下的 refcounted1 和 refcounted2 可以先忽略,不管,主要看 metadata。

struct HeapObject {var metadata: UnsafeRawPointervar refcounted1: UInt32var refcounted2: UInt32}

接下來我們將 SHPerson 類轉成 HeapObject 結構體,通過 lldb 列印,查看其內存結構。

class SHPerson {var age = 18var name = "Coder_張三"}
let p = SHPerson()
// 將 SHPerson 轉成 HeapObject 指針let p_raw_ptr = Unmanaged.passUnretained(p as AnyObject).toOpaque()let p_ptr = p_raw_ptr.bindMemory(to: HeapObject.self, capacity: 1)// 將 p_ptr 指針轉成 HeapObject 的指針類型並列印出 HeapObject 的內存結構print(p_ptr.pointee)

(滑動顯示更多)

列印結果:HeapObject(metadata: 0x00000001000081a0, refcounted1: 3, refcounted2: 0)
(lldb) x/8g 0x00000001000081a00x1000081a0: 0x0000000100008168 0x00007fff806208f80x1000081b0: 0x00007fff20208aa0 0x00008030000000000x1000081c0: 0x00000001085040f2 0x00000000000000020x1000081d0: 0x0000000700000028 0x00000010000000a8
(lldb) x/8g 0x00000001000081680x100008168: 0x00007fff80620920 0x00007fff806209200x100008178: 0x00007fff20208aa0 0x0000a031000000000x100008188: 0x0000000108504090 0x00000001000032b00x100008198: 0x00007fff8152f3e0 0x0000000100008168(lldb)

(滑動顯示更多)

通過列印,得知,Swift 類的本質就是 HeapObject 的結構體指針,並且,我們將其內存布局以 x/8g 的形式列印出來。

 

接下來我需要列印出 HeapObject 中 metadata 的內存結構,來試一下:

struct Metadata{var kind: Intvar superClass: Any.Typevar cacheData: (Int, Int)var data: Intvar classFlags: Int32var instanceAddressPoint: UInt32var instanceSize: UInt32var instanceAlignmentMask: UInt16var reserved: UInt16var classSize: UInt32var classAddressPoint: UInt32var typeDescriptor: UnsafeMutableRawPointervar iVarDestroyer: UnsafeRawPointer}
struct HeapObject {var metadata: UnsafeRawPointervar refcounted1: UInt32var refcounted2: UInt32}
class SHPerson {var age = 18var name = "Coder_張三"}
let p = SHPerson()
let p_raw_ptr = Unmanaged.passUnretained(p as AnyObject).toOpaque()let p_ptr = p_raw_ptr.bindMemory(to: HeapObject.self, capacity: 1)// 我們將 HeapObject 中的 metadata 綁定成 Metadata 類型,並轉成 Metadata 的指針類型,那麼數據類型的大小可以用 MemoryLayout 測量出來。let metadata = p_ptr.pointee.metadata.bindMemory(to: Metadata.self, capacity: MemoryLayout<Metadata>.stride).pointeeprint(metadata)

(滑動顯示更多)

列印結果:Metadata(kind: 4295000432, superClass: _TtCs12_SwiftObject, cacheData: (140733732391584, 140943646785536), classFlags: 2, instanceAddressPoint: 0, instanceSize: 40, instanceAlignmentMask: 7, reserved: 0, classSize: 168, classAddressPoint: 16, typeDescriptor: 0x0000000100003c6c, iVarDestroyer: 0x0000000000000000)(lldb)

(滑動顯示更多)

我們成功的列印出 kind、superClass、cacheData 等成員變量的值。

原文連結:https://juejin.cn/post/7046043638781968421

粉絲福利:最近給小夥伴們整理了一些最新的面試題資料,匯總整理出來的資源,可謂是程式設計師面試必備!所有資料都整理到網盤了,歡迎下載!

相關焦點

  • Swift 中存在反射嗎?
    自動執行相同的操作假設我們正在進行開發的 APP 有一個 UserSession 的類用來跟蹤用戶登錄以後的部分操作。例如,用戶的基本信息、用戶的點讚/收藏/喜歡信息以及用戶的設置等存儲類——我們為了能夠在調用時能夠便捷的時候而生成這些存儲類。作為一個單會話維持的應用,在用戶登出時,我們需要重置這些存儲類。
  • 四種繞過iOS SSL驗證和證書固定的方法
    Downloading...Downloading from: https://github.com/frida/frida/releases/download/12.0.3/frida-gadget-12.0.3-ios-universal.dylib.xzDownloading iOS dylib to /Users/netspi/.objection/ios/FridaGadget.dylib.xz
  • 結構體變量建模之終極解決方案(續)
    從前面結構體變量和結構體嵌套的兩個例子,我們也可以很清楚的體會到這兩點,所以,結構體數組,對應到模型中,自然也就是多維的Bus信號了。,同樣也可以使用這個myBus設置參數k的數據類型,只是,一旦參數對象k的數據類型被設置為myBus,那麼參數的初始化就不像以前那麼簡單的,這個例子中,設置如下:k.Value =struct(『a』,2, 『b』,3, 『c』,4);如果參數k的存儲類設置為ConstVolatile,那麼代碼中參數k的定義如下:const volatile myBus k
  • iOS超全開源框架、項目和學習資料匯總:UI篇
    BreakOutToRefresh --swift,上拉和下拉刷新2000star9. refresher --swift,上拉和下拉刷新800star10. SvpplyTable --一個可展開可收縮的下拉菜單,類似Svpply app11. ODRefreshControl --原iOS6上的橡皮糖刷新樣式,很有意思。
  • 史上最全的 iOS 各種測試工具集錦
    具體參考資料:https://developer.apple.com/library/ios/documentation/DeveloperTools/Conceptual/InstrumentsUserGuide/UIAutomation.htmlXCTest 是蘋果在 iOS 7 和 Xcode5 引入的一個簡單而強大的測試框架,
  • 在iOS 8中使用UIAlertController
    版本和Objective-C版本不同,在swift中,alertView的初始化只允許創建擁有一個取消按鈕的對話框視圖。swift版本的UIAlertView要能夠創建和上面Objective-C版本相同的對話框視圖,我們可以採取曲線救國的方法,雖然麻煩了些,但是我們為了目的可以不擇手段的,是吧?
  • Xcode 9.3 新增能力,優化 Swift 編譯生成代碼的尺寸
    -Osize 根據項目不同,大致可以優化掉 5% - 30% 的代碼空間佔用。 相比 -0 來說,會損失大概 5% 的運行時性能。我們現在越來越少的會為怎麼寫一行代碼能夠減少電量消耗,或者如何提高多核 CPU 的利用率這類的問題花費精力。編譯優化就是在一定程度上幫助我們處理這類問題的功能。
  • 圖解 Swift async/await
    completion 來通知調用者失敗(Swift 不能保證強制執行 completion),則可能導致流程異常;• 這裡大約 20 行的代碼,看似是按流程來走,但層層嵌套會讓代碼顯示更加晦澀(所謂的回調地獄);有一些現成的改進方案,但效果並不理想:• completion 的入參可以使用標準庫的 Result,會更安全一點,但也只是一點點;• 類
  • Linux C 中重要的數據結構——結構體詳解
    結構體,顧名思義,就是結構化的來組織一些數據結構,那麼對象是要封裝數據和操作的,只用來組織數據結構,那函數呢?你忽略了C語言的強大在於它的指針,熟練的應用指針能給程序帶來數量級的效率的提升。但他的不安全又是Java棄之而去的本源。
  • swift UIAlertController 工具類
  • Airtest之iOS API匯總
    以下基於airtest1.2.0Airtest核心API文件路徑:your_python_path/site-packages/airtest/core/api.pyiOS API文件路徑:your_python_path/site-packages/airtest/core/ios/ios.pyios.py中定義了一個IOS類,繼承自Device
  • 如何更好地利用 iOS Crash Log
    #6 in InnerWebSocket.step() + 228 (WebSocket.swift:783)5 TPNetworking 0x0000000102fec784 partial apply for closure #1 in InnerWebSocket.fire(_:) + 20 (WebSocket.swift:936)6
  • 在Windows上一鍵啟動IOS穩定性測試
    1、Android Monkey介紹Monkey,猴子,小淘氣。Monkey工具,可以解釋為像猴子或者小淘氣一樣任性的操作工具。Android平臺自帶有monkey工具,所以日常我們可以很方便的使用命令來使用monkey工具。那麼android的monkey工具在運行當中究竟發生了什麼呢?
  • Swift 4.0 正式發布,更快更兼容更好用
    編譯器支持兩種語言模式:語言模式由-swift-version指定給編譯器,由Swift包管理器和Xcode自動處理。
  • 首個蘋果IOS模擬器,電腦運行IOS系統,這可不是假哦!!
    pc端運行一個安卓模擬器我們見得太多了,因為這個不稀奇,因為太多太多主播都是這樣做的,而ios模擬器在很長一段時間市場上都是空白的,這次神哥給大家分享一款蘋果的模擬器
  • 小雞模擬器ios版遊戲官方下載_小雞模擬器ios版手遊官方下載_18183...
    最新小雞模擬器ios版官方下載就在18183遊戲庫,快來下載玩玩吧! 小雞模擬器ios版是專為蘋果用戶打造的模擬器遊戲下載平臺。模擬很多掌機和街機,簡直是神器。街機掌機最全、經典遊戲最多的遊戲模擬器!在這裡,你可以玩到萬款經典模擬器遊戲,如:恐龍快打、拳皇97等,趕快下載,回味經典吧!
  • Unity3D遊戲修改&iOS免越獄hook
    Mac-mini:frida-ios-dump-master-2 $ frida-ps -U PID Name---- ----2911 App Store2947 信息2980 密室逃脫絕境系列11遊樂園2936 微信2972 設置2949 郵件2814 AppleIDAuthAgent1412  AssetCacheLocatorService
  • ios、安卓前端兼容性大全
    日期兼容性將日期轉換成時間戳的形式,在安卓和ios不同的系統下轉正會有兼容性的問題。
  • iOS10.0-iOS10.3.3系統支持一鍵越獄,64位系統
    這段時間,各大版本的越獄都有重大的更新,同時各個版本的越獄工具也開始普及,近日,愛思助手更新至7.61版本,正式在愛思助手上增加ios10.0-