C中的基本类型和基本函数在Swift中是如何表示的

自从Swift开源并被移植到更多平台之后,一个日益显现的问题就是它需要更多地和C进行混编,调用OS API也好,使用第三方程序库也好。
因此,接下来的一个话题就是,从各种基础类型struct函数指针到OC对C的各种扩展,这些语言元素是如何桥接到Swift的呢?这个系列里,我们就通过一些实际的场景来了解SwiftC交互的方式。

Demo 地址:InteroperateSwiftWithC

C语言中的基础类型

首先要介绍的,是C中的基础类型,大家可以在这里
找到完整的基本类型对应表,简单来说,就是对C中的类型采取驼峰式命名之后,加上前缀字母C。例如:

CType SwiftType.png

int变成 CInt
unsigned char变成CUnsignedChar
unsigned long long变成CUnsignedLongLong

其中,只有有三个表示宽字符的类型是特殊的:

wchat_t变成CWideChar
char16_t变成CChar16
char32_t变成CChar32

于是,在Swift里,我们可以直接使用这些类型来定义变量,例如:

   let cInt: CInt = 10
   let cChar: CChar = 49

在Xcode里按住option点击这些类型就会看到,它们都是typealias。例如,CInt的定义是这样的:

    typealias CInt = Int32

但是,即便我们知道了这些C类型对应的Swift类型,当和C代码交互的时候,我们也应该总是使用这些类型的typealias版本,而不要直接使用这些别名对应的原生类型。

traditional_oc.h

#ifndef traditional_oc_h
#define traditional_oc_h
#import <Foundation/Foundation.h>

//导入基本类型的全局变量
const int global_ten;

//NS_STRING_ENUM修饰的类型,通常表示某个范围里,值固定的类型
typedef NSString * TrafficLightColor NS_STRING_ENUM;

TrafficLightColor const TrafficLightColorRed;
TrafficLightColor const TrafficLightColorYellow;
TrafficLightColor const TrafficLightColorGreen;

//如果一个类型的值有可能扩展,我们可以使用`NS_EXTENSIBLE_STRING_ENUM`来修饰它
typedef int Shape NS_EXTENSIBLE_STRING_ENUM;

Shape const ShapeCircle;
Shape const ShapeTriangle;
Shape const ShapeSquare;


int add(int m, int n);
int sum(int count, ...);
int vsum(int count, va_list numbers);

#endif

traditional_oc.m

#import "traditional_oc.h"

const int global_ten = 10;

TrafficLightColor const TrafficLightColorRed = @"Red";

TrafficLightColor const TrafficLightColorYellow = @"Yellow";

TrafficLightColor const TrafficLightColorGreen = @"Green";

Shape const ShapeCircle = 1;
Shape const ShapeTriangle = 2;
Shape const ShapeSquare = 3;


int add(int m, int n) {
    return m + n;
}


int sum(int count, ...) {
    va_list ap;
    int s = 0;
    
    va_start(ap, count);
    vsum(count, ap);
    va_end(ap);
    
    return s;
}

int vsum(int count, va_list numbers) {
    int s = 0;
    int i = 0;
    for (; i < count; ++i) {
        s += va_arg(numbers, int);
    }
    return s;
}

main.swift

import Foundation

let ten = global_ten


/*
 对于用NS_STRING_ENUM修饰的TrafficLightColor,引入到Swift就会变成一个类似这样的struct:
 
 struct TrafficLightColor: RawRepresentable {
      typealias RawValue = String
 
      init(rawValue: RawValue)
      var rawValue: RawValue { get }
 
      static var red: TrafficLightColor { get }
      static var yellow: TrafficLightColor { get }
      static var green: TrafficLightColor { get }
 }
 这个转换规则是这样的:
 根据NS_STRING_ENUM修饰的类型决定导入到Swift时struct的名字,因此,导入的类型名称就是TrafficLightColor;
 去掉和类型名称相同的公共前缀,并把剩余部分首字母小写后,变成struct的type property;
 */

let redColor: TrafficLightColor = .red
let redColorRawValue = redColor.rawValue // Red


//这样,按照之前的逻辑,类型Shape在Swift中会被导入成一个struct,它和TrafficLight唯一不同的地方在于,多了一个可以省略参数的init方法,使得我们可以在Swift里,这样扩展Shape的值:
extension Shape {
    static var ellipse: Shape {
        return Shape(4)
    }
}

let e: Shape = .ellipse

//当然,这并不是说使用NS_STRING_ENUM导入的类型就不可以扩展,例如,我们也可以在Swift里,这样扩展TrafficLightColor:

extension TrafficLightColor {
    static var blue: TrafficLightColor {
        return TrafficLightColor(rawValue: "Blue")
    }
}

//从语法上来说,这没有任何问题。因此,NS_STRING_ENUM和NS_EXTENSIBLE_STRING_ENUM并不是什么语言层面上的限制,而只是语义上的差别。面对这种差别,Swift为NS_EXTENSIBLE_STRING_ENUM提供了更为方便的扩展方法罢了。

基本函数是如何做桥接的

在Swift里,add会变成这样:

func add(_ m: Int32, _ n: Int32) -> Int32 {
   return m + n
}

其中有两点需要注意:

  • C中桥街过来的函数默认都是省略external name的;
  • C中的int会自动转换成Int32,因此默认是不能传递Swift Int类型的,只能使用CInt类型;
let sum = add(32, 23)
  • 它们都是很简单的C代码,如果你还不熟悉C的可变参数函数,可以先在这里简单了解下,我们就不重复了。这两个函数会如何桥接到Swift呢?遗憾的是,Swift只能接受vsum,而不能接受sum。也就是说,无论如何都无法在Swift中直接调用sum函数。而vsum桥接到Swift之后,是这样的:
func vsum(count: Int32, numbers: CVaListPointer) -> Int32
  • 因此,在Swift里,我们不能像vsum(6, 1, 2, 3, 4, 5, 6)这样调用vsum,那么这个CVaListPointer是什么呢?简单来说,它就是C中va_list桥接到Swift后对应的类型。为了得到这个对象,我们有两种方法。

  • 第一种,是调用getVaList方法,并把要传递的可变参数作为一个数组传递给它:

let vaListPointer = getVaList([1, 2, 3, 4, 5, 6])
let sum1 = vsum(6, vaListPointer)
  • 这样,我们就可以把vaListPointer作为vsum的第二个参数了。

  • 第二种,是调用withVaList方法,它的第一个参数是一个数组,我们像调用getVaList一样把所有可变参数传递给它;第二个参数是一个closurewithVaList会根据第一参数中的所有成员,生成一个对应的CVaListPointer对象,并传递给这个closure。因此,我们只要在closure里调用vsum就好了:

let sum2 = withVaList([1, 2, 3, 4, 5, 6]) {
    vaListPointer in
    vsum(6, vaListPointer)
}
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 206,968评论 6 482
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 88,601评论 2 382
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 153,220评论 0 344
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 55,416评论 1 279
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 64,425评论 5 374
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 49,144评论 1 285
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,432评论 3 401
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 37,088评论 0 261
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 43,586评论 1 300
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 36,028评论 2 325
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,137评论 1 334
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,783评论 4 324
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,343评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,333评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,559评论 1 262
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 45,595评论 2 355
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,901评论 2 345

推荐阅读更多精彩内容