简析Swift和C的交互Word文件下载.docx

上传人:b****5 文档编号:21471961 上传时间:2023-01-30 格式:DOCX 页数:16 大小:30.48KB
下载 相关 举报
简析Swift和C的交互Word文件下载.docx_第1页
第1页 / 共16页
简析Swift和C的交互Word文件下载.docx_第2页
第2页 / 共16页
简析Swift和C的交互Word文件下载.docx_第3页
第3页 / 共16页
简析Swift和C的交互Word文件下载.docx_第4页
第4页 / 共16页
简析Swift和C的交互Word文件下载.docx_第5页
第5页 / 共16页
点击查看更多>>
下载资源
资源描述

简析Swift和C的交互Word文件下载.docx

《简析Swift和C的交互Word文件下载.docx》由会员分享,可在线阅读,更多相关《简析Swift和C的交互Word文件下载.docx(16页珍藏版)》请在冰豆网上搜索。

简析Swift和C的交互Word文件下载.docx

也可以手动添加BridgeHeader,位置在项目的Build Settings中的 SwiftCompiler–CodeGeneration子项里。

指向你的BridgeHeader 文件名就可以了。

一般这个文件是ProjectName-Bridging-Header.h。

情况基本和与Objective-C混编没区别。

剩下的工作就很简单了。

在.c文件填上传说中的牛逼算法。

在ProjectName-Bridging-Header.h中加上该函数原型或者引入相关的头文件。

在Swift中调用的名字和C名字一样就可以了,比如你定义了一个 intmycsort()那么在Swift中就是 funcmycsort()->

CInt。

这时候问题来了。

一个漂亮的问题。

我的C 函数名字和Swift标准库冲突怎么办?

比如我定义了一个函数就叫 println,我们知道Swift里也有个 println。

这样,如果直接调用会提示Ambiguoususeof'println'

没辙了么?

这里有个我发现的UndocumentedFeatuer或者说是UndocumentedAttribute。

你转载好歹提下我好吧。

(发现方法是通过 Xcode 查看定义,然后通过nm命令发现符号,对照llvmir 确认的。

那就是@asmname("

func_name_in_c")。

用于函数声明前。

使用方法:

1.int 

println() 

.... 

} 

1.@asmname("

println"

) 

func 

c_println() 

->

CInt 

// 

声明,不需要 

{} 

函数体 

2.c_println() 

// 

调用 

也就是C中的同名函数,我们可以给赋予一个别名,然后正常调用。

这么一看就基本没有问题了。

至于类型问题,待会说,详细说。

CFramework

很多时候我们拿到的是第三方库,格式大概是个Framework。

比如 SDL2.framework。

举这个例子是因为我想对来说比较熟悉 SDL2。

直接用Finder 找到这个 .framework文件,拖动到当前项目的文件列表里,这样它将作为一个可以展开的文件夹样式存在于我们的项目中。

在ProjectName-Bridging-Header.h 中引入其中需要的.h。

比如我们引入SDL2.framework,那么我们就需要写上#import<

SDL2/SDL.h>

然后在Swift文件里正常调用就好了。

所以其实说到底核心就是那个ProjectName-Bridging-Header.h,因为它是作为参数传递给 Swift编译器的,所以Swit 文件里可以从它找到定义的符号。

但是,这个桥接头文件的一切都是隐式的,类型自动对应,所以很多时候需要我们在Swift里调用并封装。

或者使用@asmname(...)避免名字冲突。

第三部分类型转换

前面说到了 C中有指针,而Swift 中没有,同时基本类型还有很多不同。

牢记一个万能函数 reinterpretCast<

T,U>

(T)->U,只要T, Usizeof运算相等就可以直接转换。

这个在之前的标准库函数里有提到。

调用C代码的利器!

基本类型对应

int=>

CInt

char =>

CChar /CSignedChar

char* =>CString

unsigned long = >CUnsignedLong

wchar_t=> CWideChar

double =>

CDouble

T*=>CMutablePointer

void*=>

CMutableVoidPointer

const T*=> CConstPointer

constvoid*=>CConstVoidPointer

继续这个列表,你肯定会想这么多数值类型,怎么搞。

其实大都是被 typealias 定义到UInt8,Double这些的。

放心。

C中数值类型全部被明确地用别名定义到带size的Swift 数值类型上。

完全是一样用的。

其实真正的Pointer 类型只是UnsafePointer<

T>

大小与 C保证一致,而对于这里不同类型的Pointer,其实都是 UnsafePointer 到它们的隐式类型转换。

还有个指针相关类型是 COpaquePointer,不过没试验怎么用。

UPDATE:

我们在调用的时候,更多地用到COpaquePointer,我将再坑一篇介绍它。

同时NilType,也就是nil 有到这些指针的隐式类型转换。

所以可以当做任何一种指针的NULL用。

还有个需要提到的类型是CString,他的内存layout等于UnsafePointer<UInt8>

,下面说。

CString

用于表示char*,\0结尾的c字符串,实际上似乎还看到了判断是否ASCII的选项,但是没试出来用法。

实现了StringLiteralConvertible和LogicValue。

可以从字符串常量直接赋值获得CString。

LogicValue也就是是ifa_c_str{},实际是用于判断是否为NULL,可用,但是不稳定,老 crash。

运算符支持==,判断两字符串是否相当,猜测实际是 strcmp 实现,对比NULL会 crash。

Orz。

CString和 String的转换通过一个extension实现,也是很方便。

1.extension 

String 

{ 

2. 

static 

func 

fromCString(cs:

CString) 

->

String 

3. 

static 

func 

fromCString(up:

UnsafePointer<

CChar>

-> 

String 

4.} 

5.// 

还有两个方便的工具方法。

Rust 

背景的同学一定仰天长啸。

太相似了。

6.extension 

String 

7. 

func 

withCString<

Result>

(f:

(CString) 

Result) 

Result 

8. 

withCString<Result>

(f:

(UnsafePointer<

CChar>

Result) 

Result 

9.} 

在我们的 BridgingHeader头文件中 char *的类型会对应为 UnsafePointer<

CChar>

而实际上CString更适合。

所以在Swift代码中,往往我们要再次申明下这个函数。

或者用一个函数封装下,转换成我们需要的类型。

例如,假设在 Bridging Header中我们声明了char*foo();

,那么,在 Swift代码中我们可以用上面提到的方法:

1.@asmname("foo"

func 

c_foo() 

-> 

CString 

2.// 

注意这里没有 

{},只是声明 

3.let 

ret 

= 

c_foo() 

当然也可以直接调用原始函数然后类型转换:

1.let 

raw 

foo() 

UnsafePointer<

Int8>

<

=>

UnsafePointer<CChar>

2.let 

ret 

String.fromCString(ret) 

如果这里要转成 CString就略复杂了,因为CString构造函数接受的参数是UnsafePointer<UInt8>

 而CChar是 Int8 的别名,所以还牵扯到Genrics类型转换,不够方便。

如果非要作为 CString处理,可以用reinterpretCast(),直接转换。

但是请一定要知道自己在转换什么,确保类型的sizeof相同,确保转换本身有意义。

例如获得环境变量字符串:

1.let 

key 

"PATH"

2.// 

这里相当于把 

UnsafePointer<

CChar>

转为了 

UnsafePointer<

UInt8>

然后到 

CString 

3.let 

path_str:

CString 

reinterpretCast(key.withCString(getenv)) 

Unmanaged

这个先挖坑,随后填上。

VaList

这个也是坑,随后填上。

第三部分 C调用Swift

如果项目里加入了C文件,那么它可以调用我们的Swift 函数么?

答案是可以的,而且令人吃惊地透明。

这也许是因为Apple 所宣传的,SmallRuntime 概念吧。

极小的语言运行时。

和Objective-C混编类似,配置好 BridgingHeader的项目,在 .c .h .m文件中都可以使用一个叫做ProjectName-Swift.h 的头文件,其中包含 Swift代码导出的函数等。

参考之前的 Objective-C和 C交互我们可以知道,说到底交互就是链接过程,只要链接的时候能找到符号就可以。

不过不能高兴太早,Swift是带类、枚举、协议、多态、泛型等的高级语言,符号处理明显要比C中的复杂的多,现代语言一般靠namemangle 来解决这个问题。

也就是说一个Swift函数,在编译到.o的时候,名字就不是原来那么简单了。

比如__TFV5hello4Rectg9subscriptFOS_9DirectionSi这样的名字。

Xcode自带了个工具,可以查看这些mangledname 到底是什么东西:

1.xcrun 

swift-demangle 

__TFV5hello4Rectg9subscriptFOS_9DirectionSi 

2._TFV5hello4Rectg9subscriptFOS_9DirectionSi 

---> 

hello.Rect.subscript.getter 

(hello.Direction) 

Swift.Int 

当我们从 C调用的时候,应该规避这样的名字。

还记得前面的 @asmname么?

没错,它可以用于指定Swift函数的符号名,我猜测应该有指定mangledname 的作用,但是不是特别确定。

这里随便指定个例子先。

1.@asmname("

say_hello") 

func 

say_hello() 

Double 

println("

This 

is 

say_hello() 

in 

swift") 

return 

3.14 

4.} 

然后在.c文件中:

1.#include 

<ProjectName-Swift.h> 

3.extern 

double 

say_hello();

4. 

5.int 

some_func() 

6. 

say_hello();

// 

or 

capture 

its 

value 

and 

process 

it 

return 

8.} 

对于函数而言 extern 必须手动加上,对于class、protocol ,会在生成的头文件里。

按照这个思路,其实很容易实现Swift调用C中调用了Swift函数的函数。

这意味着,可以通过简单的方法封装支持向 C 传递 Swift block作为回调函数。

难度中上,对于有过类似扩展编写经验的人来说很简单。

第四部分 编译过程

其实调用基本就这么多, Objective-C 那篇文章中说的编译过程同样有效。

我C-cC-v下:

编译所有X.swift 文件到X.o(with -emit-objc-header,-import-objc-header)(其中包含.swiftmodule 子过程):

由于选项里有-emit-objc-header,所以之后的C文件可以直接import对应的ProjectName-Swift.h

编译X.c到X.o

链接所有.o生成可执行文件

仔细理解上面的简简单单四行编译过程说明,你就明白为什么 .swfit和.c可以互相调用了。

其中两个Header文件起到了媒介的作用,一个将.c/.m文件中的定义暴露给Swift,另一个将.swift中的定义暴露给 .c/.m。

再看类型对应

标准类型这里就不提了,上面的文章讲的很明白了。

7 种指针类型

从代码看,我认为Swift对应C 的指针时候,存在一个最原始的类型RawPointer,但是它是内部表示,不可以直接使用。

所以略过。

但它是基础,可以认为它相当于Word 类型(机器字长)。

COpaquePointer

不透明指针。

之前我以为它很少会用到,不过现在看来想错了,虽然类型不安全,但是很多场合只能用它。

它是直接对应RawPointer的。

字长相等。

“Incomputerprogramming,an opaquepointerisa specialcase of an opaquedatatype,adatatypedeclaredtobeapointertoa record ordata structureofsome unspecifiedtype.”——来自Wikipedia

几乎没有任何操作方法,不带类型,主要用于BridgingHeader中表示 C中的复杂结构指针

比如一个例子,libcurl中的CURL*的处理,其实就是对应为COpaquePointer。

UnsafePointer

泛型指针。

直接对应 RawPointer。

处理指针的主力类型。

常量中的C_ARGV 的类型也是它UnsafePointer<CString>

支持大量操作方法:

通过.memory 属性 { get set}操作指针指向的内容

支持subscript,直接对应于C的数组,例如C_ARGV[1]

通过alloc(num:

Int) 分配数组空间

initialize(val:

 T) 直接初始化

offset操作.succ() .pred()

可以从任意一种指针直接调用构造函数获得

隐式类型转换为非COpaquePointer之外的任意一种指针

AutoreleasingUnsafePointer

之前特地写文介绍过这个指针类型。

NSError 的处理就主要用它。

SwiftNSErrorInternals(解析 Swift对 NSError操作)

内部实现用了语言内置特性,从名字也可以看出来,这个应该是非常棒的一个指针,可以帮助管理内存,逼格也高。

内存直接对应 RawPointer可以传递给C 函数。

通过.memory属性{ get set}操作指针指向的内容

直接从 &T 类型获得,使用方法比较诡异,建议参考文章

CMutablePointer CConstPointer

分别对应于C 中的T*、constT *。

不可直接传递给C 函数,因为表示结构里还有一个owner域,应该是用来自动管理生命周期的。

sizeof操作返回16。

但是可以有隐式类型转换。

操作方法主要是funcwithUnsafePointer<

U>(f:

UnsafePointer<

T>->

 U) ->

U,用Trailing Closure语法非常方便。

CMutableVoidPointerCConstVoidPointer

分别对应于C中的void *、constvoid*。

其他内容同上一种。

小结指针

以上7 种指针类型可以分未两类,我给他们起名为第一类指针 和第二类指针。

(你看我在黑马克思耶,算了这个梗太深,参考马克思主义政治经济学)

-可以直接用于C函数声明的第一类指针

COpaquePointerUnsafePointer<T>

AutoreleasingUnsafePointer<

T>

是对RawPointer的封装,直接对应于C的指针,它们的sizeof 都是单位字长

-不可用于声明第二类指针

CMutablePointer<

 CConstPointer<T>CMutableVoidPointerCConstVoidPointer

直接从Swift对象的引用获得(一个隐藏特性,引用隐式转换)(主要构造方法)

包含了一个 owner字段,可以管理生命周期,理论上在Swift中使用

通过.withUnsafePointer 方法调用

所有指针都实现了 LogicValue协议,可以直接 ifa_pointer判断是否为NULL。

nil类型实现了到所有指针类型的隐式类型转换,等价于 C中的`NULL,可以直接判断。

什么时候用什么?

这个问题我也在考虑中,以下是我的建议。

对应复杂结构体,不操作结构体字段的:

 COpaquePointer例如CURL *

日常操作:

 UnsafePointer<

T>

同时需要在Swift和 C中操作结构体字段,由Swift管理内存:

AutoreleasingUnsafePointer<T>

Swift中创建对象,传递给C:

 第二类指针

工具类型

CVarArg CVaListPointerVaListBuilder

用于处理 C语言中的可变参数 valist函数。

1.protocol 

CVarArg 

func 

encode() 

Word[] 

3.} 

表示该类型可以作为可变参数,相当多的类型都实现了这个。

1.struct 

CVaListPointer 

var 

value:

UnsafePointer<Void>

init(fromUnsafePointer 

from:

UnsafePointer<

Void>

@conversion 

__conversion() 

CMutableVoidPointer 

5.} 

对应于C,直接给C函数传递,声明、定义时使用。

1.class 

VaListBuilder 

init() 

append(arg:

CVarArg) 

va_list() 

CVaListPointer 

5.} 

工具类,方便地创建CVaListPointer。

还有一些工具函数:

1.func 

getVaList(args:

CVarArg[]) 

CVaListPointer 

2.func 

withVaList<

R>

(args:

CVarArg[], 

f:

(CVaListPointer) 

R) 

R 

3.func 

withVaList<

R>

(builder:

VaListBuilder, 

(CVaListPointer) 

R) 

非常方便。

UnsafeArray

1.struct 

UnsafeArray<

:

Collection, 

Generator 

startIndex:

Int 

get 

endIndex:

Int 

subscript 

(i:

Int) 

T 

get 

5. 

init(start:

展开阅读全文
相关资源
猜你喜欢
相关搜索

当前位置:首页 > 法律文书 > 辩护词

copyright@ 2008-2022 冰豆网网站版权所有

经营许可证编号:鄂ICP备2022015515号-1