iOS技术点ios

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

2017-07-04  本文已影响190人  AKyS佐毅

自从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
}

其中有两点需要注意:

let sum = add(32, 23)
func vsum(count: Int32, numbers: CVaListPointer) -> Int32
let vaListPointer = getVaList([1, 2, 3, 4, 5, 6])
let sum1 = vsum(6, vaListPointer)
let sum2 = withVaList([1, 2, 3, 4, 5, 6]) {
    vaListPointer in
    vsum(6, vaListPointer)
}
上一篇 下一篇

猜你喜欢

热点阅读