泊学翻译自Swift在Github上发布的Swift ABI Manifesto

Swift ABI的构成

在实践中,ABI关注的内容是紧密耦合在一起的。但是,作为一个概念模型。我更愿意把它分成6个独立的分类:

1.和类型相关的,例如:所有的结构和类对象应该有确定的内存布局。为了达成二进制层次上的交互(这里应该指的是不同版本Swift编译器生成的结果在二进制上兼容),它们必须共享相同的布局协议。这部分内容会在数据布局的章节进行讨论。

2.Swift可执行程序,运行时、反射机制、调试器以及可视化工具都和类型的Metadata息息相关。因此,Metadata应该有一种更稳定的读取方式,要不为类型的Metadata设计确定的内存布局,要不为访问类型的Metadata提供一套稳定的APIs。这部分内容会在类型的Metadata章节继续讨论。

3.程序库中每一个被导出或来自外部的符号都需要一个唯一的名称,这个名称的识别应该在所有的二进制实体之间达成一致。由于Swift提供了函数重载以及上下文相关的名字空间(这里应该指的是通过module引入的名字空间),因此,在代码中出现的任何名称可能都不是全局唯一的。为了把它们表达成一个全局唯一的名字,Swift使用了一种叫做name mangling的技术。具体的name mangling方案会在Mangling章节中讨论。

4.函数必须知道如何相互调用,因此我们需要约定调用栈在内存中是如何布局的,哪些寄存器会在调用间被保留。这些内容统称为函数的调用约定。这部分内容会在Calling Convention章节中讨论。

5.Swift发布的时候自带了一个运行时库,用来处理诸如动态类型转换、引用计数、类型反射等相关的工作。Swift程序在编译的时候会调用这些运行时中的API。因此,Swift运行时API也是Swift ABI的一部分。运行时API的稳定性会在运行时的章节中讨论。

6.除此之外,Swift还自带了一个标准库,其中定义了很多公共的类型、结构和基于这些结构的方法。为了让这份自带的标准库可以被用不同版本Swift编写的程序调用,标准库也需要对外暴露一份稳定的API。因此,和标准库中定义的类型需要有确定的内存布局一样,Swift标准库的API也是Swift ABI的一部分。关于标准库ABI的稳定性会在标准库的章节中进行讨论。

数据布局

背景

首先,我们来定义一些术语。

  • 对象(object)是指某个类型的存储实体,它可以存储在内存中的某个位置,或存储在寄存器里。对象可以是 struct / enum类型的值、class的实例、class实例的引用、protocol类型的值,甚至是closures。而在那些视class为全部的面向对象编程语言中,对象则就是指的class的一个实例,这是Swift有别于它们的地方;

  • 对象的数据成员(data member)是指任意需要存放在类对象内存布局内的值。数据成员包括了一个对象所有的stored properties和associated values;

  • 闲置位(spare bit)是某种类型对象(的内存布局)中,没有被使用的部分。这些部分通常是为了对齐内存地址而填充的地址空间。稍后,会更深入讨论这个话题;

  • 在对象的布局中,除了那些表示对象值的bit之外,还有一类bit并没有实际的意义。例如,对于一个包含3个case的传统C风格enum来说,它的值用两个bit就可以表示了(数字0,1,2分别对应三个case,它们对应二进制的00,01和10)。这时,这两个bit可以表示的第4个值3,它的二进制表示中的第一个1就是无意义的;

数据布局,也被称作类型布局,定义了一个对象的数据在内存中的布局。这包括了对象在内存中的大小,对象的对齐(稍后会定义)以及如何在对象中找到每一个数据成员。

如果编译器可以在编译期确定一个对象的布局,这个对象的布局就是静态的。如果对象的布局只有在运行时在可以确定,这类对象的布局就是不透明的(opaque layout)。我们会在opaque layout章节中深入讨论这类对象。

布局和类型的属性

在Swift里,对于每一个静态布局的类型T,ABI指定了计算以下内容的方式:

  • 关于类型的对齐:对于x: T来说,对象x的起始地址对当前硬件平台的内存对齐值取模一定是0(也就是说总在内存地址对齐的位置开始);

  • 关于类型的尺寸:一个类型对象的大小,按对象占用的字节数计算(可以是0),但是不包含填充在对象结尾的字节;

  • 关于每个数据成员的偏移(如果可行):每一个数据成员的地址,都是从对象起始地址开始计算的;

把计算对齐和对象大小的方式结合在一起,就是这个类型的对象占用内存时的步进计算方式,它等于把对象的尺寸按照内存对齐的大小向上取整一个单位(最小是1单位。例如,对象的大小是7,内存要求8字节对齐,那么对象占用的内存就向上调整到8)。这种计算方式对于在连续内存地址空间中排列对象(例如:数组)时很有帮助。

一些类型有以下两种有趣的属性:

  • 如果一个类型仅仅用来存储数据(注:很多struct就是如此,但不是全部),在拷贝、移动或销毁这类对象时,就不会有额外的复杂语义。我们管这种类型叫做POD(Plain of Data),也叫做trivial type。这种类型的对象在拷贝时,直接复制它的值即可,销毁时,可以直接回收分配给它的存储资源。只有在一个类型的所有数据成员都是trivial type时,这个类型才是一个trivial type。

  • 如果一个对象的地址没有被其它辅助设计的表结构(注:这里应该是指为了实现对象布局而引入的额外数据结构)引用,这种类型的对象就是可以按位移动的(bitwise ovable)。当一个对象要从一个地址拷贝到另外一个地址,并且原地址的对象已经不再需要的时候,就可以把原地址的对象按位拷贝到新地址,然后把原地址对象标记为不可用的状态。如果一个类型所有数据成员都是可以按位移动的,则这个类型的对象也是可以按位移动的。并且,所有的trivial type的对象都是可以按位移动的。

例如,一个struct Point,它有两个Double类型的属性xy,表示平面上X轴和Y轴的坐标。此时,Point就是一个trivial type。复制Point对象的时候,我们只要按位拷贝对象的内容就可以,销毁的时候我们也无需做任何额外的工作。

再来看一个可以按位移动的非POD类型的例子,就是包含类对象引用的struct。在拷贝这类对象时,我们不能只是简单拷贝struct对象的值,还要retain其包含的类对象。而在销毁这类struct对象的时候,我们也要release其引用的类对象。但是,这类对象却是可以在不同的内存地址间移动的,只要每次移动后,我们都把原地址的对象标记为不可用,保持其包含的类对象总体引用计数不变就好了。

最后,来看一个不可按位移动的非POD类型的例子,就是一个包含weak引用的struct。所有的weak引用都是通过一个辅助表格维护的。因此,当它们引用的对象被销毁的时候,这些weak references才可以被设置成nil。当移动这类对象的时候,必须要更新表格中的weak reference,让它引用到新的对象地址。

不透明布局

当一个对象的布局只有在运行时才可以确定时,这类对象的布局就叫做不透明的。例如,一个泛型对象,我们就无法在编译期确定对象的布局。再有,就是一种更具适应性的类型(resilient type),我们会在下一节描述这个概念。

对于一个具有不透明布局的对象来说,它的大小、对齐,是否是一个POD类型或者是否可以按位移动都是通过查询它的value witness table来确定的。我们会在value witness table这一节中深入讨论这个话题。数据成员的偏移是通过查询类型的Metadata得到的,我们会在value Metadata的章节中讨论。拥有不透明布局的对象必须通过间接的方式传递,我们会在函数底层签名(Function Signature Lowering)的章节中讨论。Swift运行时,通过一些指针和拥有不透明布局的对象进行交互,因此,这类对象必须是可以取地址的。我们会在抽象级别的章节中进一步进行描述。

在实践中,编译器可以在编译期对布局有部分的了解。例如,对于下面这样的struct

struct Type<T> {
    var number: Int
    var object: T
}

在这种情况下,根据特定的布局算法,整数number的布局以及它在struct对象中的位置都是可以确定的。但是,泛型属性的存储却是不透明布局,因此,整个结构的大小和对齐都是不确定的。我们正在调研如何用更高效的方式布局这种“半透明”形式的组合SR-3722。这很可能会导致把不透明的部分放到布局的末尾以保证所有静态布局的部分可以正常计算偏移。

(To be continue...)

译:Swift ABI (二)的更多相关文章

  1. ios – 无法识别的选择器发送到实例NSTimer Swift

    解决方法让updateTime成为一个类方法.如果它是在一个纯粹的Swift类中,你需要在@objc前面说明该方法的声明,如:

  2. ios – 类型推断(自动类型检测)如何在swift中工作?

    LLVM如何检测变量是一个字符串?

  3. ios – Swift可选项:语言问题,还是做错了什么?

    应该有可选的类型;type是但是,如果我这样做,它的工作原理:它似乎是基本的替代,但我可能会遗漏一些语言的细微差别.谁能对此有所了解?之后就像暧昧一样,更多,这是我的解决方案:这适用于所有非对象Swift对象,包括Swift字符串,数字等.感谢Viktor提醒我String不是Swift中的对象.如果您知道值的类型,您可以替换任何?使用适当的可选类型,如String?

  4. ios – 覆盖Swift中的超类委托

    我正在开发一个包含两个UIViews的Swift(v1.2)项目.MyView和MyViewSubclass.MyView有一个委托,我想在MyViewSubclass中覆盖它作为一个子协议,类似于UITableViews有一个UITableViewDelegate,它也符合超级uiscrollviewdelegate.我的第一个想法是覆盖超类属性,但这会导致编译器错误,因为子类不能覆盖具有不同类

  5. ios – 我可以在swift中将字符串转换为代码块吗?

    有没有办法将字符串转换为代码块?

  6. ios – Swift:方法重载只在返回类型上有所不同

    我一直在看Swift类,其中定义了两种方法,它们的返回类型不同.我不习惯使用允许这种语言的语言,所以我去寻找描述它如何在Swift中工作的文档.我在任何地方都找不到任何东西.我本来期望在Swift书中有关于它的整个部分.这记录在哪里?

  7. ios – 字符串资源Xcode swift

    我是iOS开发和Swift语言的新功能.而且我尝试制作简单的iOS应用程序,我需要在应用程序中使用一些字符串资源.当然,我可以将这个字符串放在我的*.swift文件中作为常量,但我认为这是一个坏的方法.我该怎么做?

  8. ios – 如何使用新的Apple Swift语言发布JSON

    本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容,请发送邮件至dio@foxmail.com举报,一经查实,本站将立刻删除。

  9. ios – Swift元组到可选分配

    我在Swift编写一些代码来学习语言.这是我的基础课:问题在最后一行:编译器说“不能表达元组转换to”我明白问题是self.status和self.message是可选的,parseResponse不返回可选.我如何告诉它做必要的分配和转换,以获取数据到实例变量?

  10. ios – 使用未申报的“AppDelegate”Swift

    您可能会看到AppDelegate代码here,但是我没有修改任何由xCode自动生成的内容.我创建了一个具有this设置的单一视图应用程序.解决方法很有可能当您创建项目时,您还创建了一个“{ProjectName}Tests”目标.问题是AppDelegate没有在“{ProjectName}Tests”目标中分配成员资格.选择AppDelegate.swift,然后在右侧检查器中单击文件检查器,然后确保在“目标成员资格”中,您的项目和测试目标复选标记都设置为ON.清洁,重建.

随机推荐

  1. Swift UITextField,UITextView,UISegmentedControl,UISwitch

    下面我们通过一个demo来简单的实现下这些控件的功能.首先,我们拖将这几个控件拖到storyboard,并关联上相应的属性和动作.如图:关联上属性和动作后,看看实现的代码:

  2. swift UISlider,UIStepper

    我们用两个label来显示slider和stepper的值.再用张图片来显示改变stepper值的效果.首先,这三个控件需要全局变量声明如下然后,我们对所有的控件做个简单的布局:最后,当slider的值改变时,我们用一个label来显示值的变化,同样,用另一个label来显示stepper值的变化,并改变图片的大小:实现效果如下:

  3. preferredFontForTextStyle字体设置之更改

    即:

  4. Swift没有异常处理,遇到功能性错误怎么办?

    本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容,请发送邮件至dio@foxmail.com举报,一经查实,本站将立刻删除。

  5. 字典实战和UIKit初探

    ios中数组和字典的应用Applicationschedule类别子项类别名称优先级数据包contactsentertainment接触UIKit学习用Swift调用CocoaTouchimportUIKitletcolors=[]varbackView=UIView(frame:CGRectMake(0.0,0.0,320.0,CGFloat(colors.count*50)))backView

  6. swift语言IOS8开发战记21 Core Data2

    上一话中我们简单地介绍了一些coredata的基本知识,这一话我们通过编程来实现coredata的使用。还记得我们在coredata中定义的那个Model么,上面这段代码会加载这个Model。定义完方法之后,我们对coredata的准备都已经完成了。最后强调一点,coredata并不是数据库,它只是一个框架,协助我们进行数据库操作,它并不关心我们把数据存到哪里。

  7. swift语言IOS8开发战记22 Core Data3

    上一话我们定义了与coredata有关的变量和方法,做足了准备工作,这一话我们来试试能不能成功。首先打开上一话中生成的Info类,在其中引用头文件的地方添加一个@objc,不然后面会报错,我也不知道为什么。

  8. swift实战小程序1天气预报

    在有一定swift基础的情况下,让我们来做一些小程序练练手,今天来试试做一个简单地天气预报。然后在btnpressed方法中依旧增加loadWeather方法.在loadWeather方法中加上信息的显示语句:运行一下看看效果,如图:虽然显示出来了,但是我们的text是可编辑状态的,在storyboard中勾选Editable,再次运行:大功告成,而且现在每次单击按钮,就会重新请求天气情况,大家也来试试吧。

  9. 【iOS学习01】swift ? and !  的学习

    如果不初始化就会报错。

  10. swift语言IOS8开发战记23 Core Data4

    接着我们需要把我们的Rest类变成一个被coredata管理的类,点开Rest类,作如下修改:关键字@NSManaged的作用是与实体中对应的属性通信,BinaryData对应的类型是NSData,CoreData没有布尔属性,只能用0和1来区分。进行如下操作,输入类名:建立好之后因为我们之前写的代码有些地方并不适用于coredata,所以编译器会报错,现在来一一解决。

返回
顶部