Swift和C:来回

你好!



一旦为我分配了iOS任务-具有特定加密功能的VPN客户端。



传统上,我们公司有自己的密码学,有一个现成的C语言实现。



在本文中,我将告诉您如何在C和Swift之间结交朋友。



为了清楚起见,作为示例,我们将编写一个简单的函数来转换C语言中的字符串并从Swift中调用它。



此类任务的主要困难是参数传递和返回值。让我们谈谈他们。让我们有一个功能:



uint8_t* flipString(uint8_t* str, int strlen){
  uint8_t* result = malloc(strlen);
  int i;
  int j=0;
  for(i = strlen-1; i>=0; --i){
      result[j] = str[i];
      j++;
  }
  return result;
}


该函数获取一个指向要反转的字节数组和字符串长度的指针。我们返回一个指向结果字节数组的指针。牢记malloc。贯穿,写下,返回。



我们用函数的标题创建MyCFile.h。添加Bridging-Header.h,其中连接了相同的MyCFile.h。



进一步的类型转换。让我们从一个简单的事情开始-int是Int32。然后是指针。快速有几个指针。我们对UnsafeMutablePointer感兴趣。



let str = "qwerty"
var array: [UInt8] = Array(str.utf8)
let stringPointer = UnsafeMutablePointer<UInt8>.allocate(capacity: array.count)
stringPointer.initialize(from: &array, count: array.count)
guard let res = flipString(stringPointer, Int32(array.count)) else {return}


从给定的字符串创建一个UInt8数组(所以它是一个字节)。我们创建一个指向一定大小数据的指针。我们指出。打电话,看看不是零。



而且,如果使用传递的指针看起来一切都很简单,则res当前为UnsafeMutablePointer?类型。单击几次,绕过StackOverflow,我们找到了pointee属性。使用此属性,可以通过此指针访问内存。我们尝试使用此属性扩展单词“ qwerty”,然后在其中出现... Badum-ts ...“ 121”。好的,鼓声是多余的,但是结果却不是我想要的。



虽然,如果您考虑一下,一切都是合乎逻辑的。在Swift中,我们的res(C函数返回的)是指向Int8数组的指针。自古以来,指针就指向数组的第一个元素。就是这样我们进入ASKII表。 121是字母“ y”的代码。巧合?我不这么认为。一个字符被计数。



此外,根据古老的Sish传统,您可以通过移动指针来遍历数组并获取以下字节:



let p = res+1
print(p.pointee)


这样我们得到116,即代码“ t”。



从理论上讲,如果您知道分配的内存大小,则可以像这样继续进行。并且此内存分配在C代码内部。



在我们的情况下,没有问题,但是在一些更严肃的程序中,您将不得不动手。这是我做的。



解决方案是以良好的旧C结构的形式出现的。



该计划如下:创建一个结构,将倒置的字符串和大小复制到相应的字段中,并返回指向该结构的指针。



struct FlipedStringStructure {
    void *result;
    int resultSize;
};


让我们这样重写函数:



struct FlipedStringStructure* flipStringToStruct(uint8_t* str, int strlen){
    uint8_t* result = malloc(strlen);
    int i;
    int j=0;
    for(i = strlen-1; i>=0; --i){
        result[j] = str[i];
        j++;
    }
    struct FlipedStringStructure* structure;
    structure = malloc(sizeof(struct FlipedStringStructure));
    structure->resultSize=j;
    structure->result = malloc(j);
    memcpy(structure->result,result,j);
    free(result);
    return structure;
}


我们注意到我们为结构和字符串分配了内存。



好吧-它仍然是重写挑战。我们跟随我们的双手。




func flip(str:String)->String?{
    var array: [UInt8] = Array(str.utf8)
    let stringPointer = UnsafeMutablePointer<UInt8>.allocate(capacity: array.count)
    stringPointer.initialize(from: &array, count: array.count)

    let structPointer = flipStringToStruct(stringPointer, Int32(array.count))
    guard structPointer != nil else{return nil}
    let tmp = structPointer!.pointee
    let res = Data(bytes: tmp.result, count: Int(tmp.resultSize))
    let resStr = String(decoding: res, as: UTF8.self)
    freeMemmory(tmp.result)
    freeSMemmory(structPointer)
    return resStr
}


我们仍然使用pointee属性,但是现在我们获得了用C代码创建的结构类型的第一个元素。这个想法的好处是我们可以引用代码C部分中声明的数据类型,而不必进行不必要的强制转换。第一部分已被拆卸。接下来的步骤:获取一个指向用C填充的结构的指针(structPointer)。



我们可以访问此结构的内存。该结构具有数据和数据大小。



您可以将它们称为迅速(通过点)创建的结构的字段。



我们从中收集一个字节数组(数据),并将其解码为String。好吧,别忘了自己清理。我们创建2个函数:




void freeMemmory(void* s){
    free(s);
}
void freeSMemmory(struct FlipedStringStructure* s){
    free(s);
}


从Swift调用这些函数时,我们会将接收到的指针或结构中的指针作为参数传递给它们。



freeMemmory(tmp.result)
freeSMemmory(structPointer)


瞧-它在书包里!



当然,这种方法没有什么新意,但是它允许您主动使用跨平台功能,并且非常方便。



感谢那些阅读它的人。



链接到git中的项目-这里

EOF



All Articles