使用Go語言開發iOS應用(Swift版)

來源:互聯網
上載者:User
這是一個建立於 的文章,其中的資訊可能已經有所發展或是發生改變。

本文假設讀者對Go語言和Swift語言都有一定瞭解, 但是對二者混合使用不瞭解的同學.

本教程是基於一個真實上架的iOS應用做的簡單的總結。

我們先看看運行效果:

掃碼安裝:

背景

Go語言是Google公司於2010年開源的一個面向網路服務和多並發環境的程式設計語言,特點是簡單。但是因為簡單,也就只能實現90%的效能,這是Go語言的最大優點,因為 少即是多 的道理不是每個人都能領悟的。

Swift是Apple公司於2014年發布的用來替代ObjectiveC的語言,主要面向iOS和OS X上的介面程式開發。當然用swift來程式開發伺服器也是大家關注的一個領域,作者比較看好在不遠的將來Swift將逐步替代C++和Rust語言。

Go語言和Swift語言本來是風馬牛不相及的兩個語言,為何非一定要整到一起呢?原因很簡單,因為作者是一個Go粉,同時也算是半個Swift粉;想試水iOS開發,但是實在是受不了ObjectiveC的裹腳布文法。

補充下:本人雖然不喜歡ObjectiveC的文法,但是覺得ObjectiveC的runtime還是很強悍的。理論上,基於ObjectiveC的runtime,可以用任何流行的程式設計語言來開發iOS應用,RubyMotion就是一個例子。

其實,現在流行的絕大部分語言都有一個交集,就是c語言相容的二進位介面。所以說,C++流行並不是C++多厲害,而是它選擇無縫相容了C語言的絕大部份規範。

但是,完全相容C語言的規範也有缺點,就是語言本身無法自由地發展,因為很多地方會受到C語言編程模型的限制。C++和ObjectiveC是兩個比較有代表的例子。

所以說,Swift一出世就只相容C語言的二進位介面規範,同時抱緊了ObjectiveC的runtime大腿,而去自己確實有很大優秀的特性。

但是,我們這裡暫時不關心Swift和ObjectiveC的混合編程,我們只關注作為ObjectiveC子集的C語言如何與Swift混合編程。

Swift調用C函數

Swift調用C函數的方法有多種:通過ObjectiveC橋接調用和直接調用。其實兩者的原理是一樣的,我個人跟喜歡選擇最直接也最暴力的直接調用C函數的方式。

比如有一個C函數:

#include void getInput(int *output) {    scanf("%i", output);}

產生一個橋接的標頭檔xxx-Bridging-Header.h,裡麵包含c函數規格說明:

void getInput(int *output);

swift就可以直接使用了:

import Foundationvar output: CInt = 0getInput(&output)println(output)

如果不用橋接檔案,可以在swift中聲明一個Swift函數,對應C函數:

@_silgen_name("getInput") func getInput_swift(query:UnsafePointer)

為了明確區分C函數和swift函數,我們將getInput重新聲明為getInput_swift,使用方法和前面一樣:

import Foundationvar output: CInt = 0getInput_swift(&output)println(output)

Swift中如何管理c返回的記憶體

Swift語言本身是內建ARC的,使用者很少直接關注記憶體問題。但是C函數如果返回記憶體到Swift空間,Swift的ARC是無效的,需要手工釋放C記憶體。

假設我們自己用C語言實現了一個字串複製的函數:

char* MyStrDup(char* s) {    return strdup(s);}

在swift中可以這樣使用:

@_silgen_name("MyStrDup")func MyStrDup_swift(query:UnsafePointer) -> UnsafeMutablePointerlet p = MyStrDup_swift("hello swift-c!")let s = String.fromCString(p)!p.dealloc(1)

使用String.fromCString(p)!從C字串構建一個swift字串,然後手工調用p.dealloc(1)釋放c字串記憶體空間。

函數調用和記憶體管理是跨語言編程中最重要的兩個基礎問題,目前已久初步可以工作了。

Go語言匯出C靜態庫

Go語言提供了一個cgo的工具,用於Go語言和C語言互動。這是Go語言使用C語言的一個例子:

package main//#include import "C"func main() {    C.puts(C.CString("abc"))}

既然要互動,自然會涉及到C語言回調Go語言函數的情形。為此,cgo提供了一個export注釋命令,用於產生Go語言函數對應的C語言函數:

//export MyStrDupfunc MyStrDup(s *C.char) *C.char {    return C.strdup(s)}

MyStrDup指定的名字必須和Go函數名字一致,函數的參數最後是C語言支援的類型。

現在,我們就得到了用Go語言實現的MyStrDup函數,使用方法和前面的C語言實現的MyStrDup是一樣的。

和引用C語言函數庫遇到的問題一樣,我們如何在工程中引用這些C代碼或Go代碼實現的函數呢?

答案還是來自C語言:將代碼構建為C靜態庫或者C動態庫,然後將靜態庫或動態庫匯入Swift工程。

但是,對於iOS來說,構建C靜態庫或者C動態庫的過程要麻煩(使用xcode也只是隱藏了構建的具體步驟)。

因為,iOS涉及到多種CPU架構:模擬器的x86、4s的32位arm、5s以後的64位arm,64位arm中還有不同當版本...

這是C靜態庫或者C動態庫構建始終都要面對的問題。

交叉構建的參數

Go1.6之後增加了構建C靜態庫的支援,交叉編譯也非常簡單,只需要設定好GOARCHGOOS就行。

因為,iOS的GOOS只有Darwin一種類型,我們只需要設定GOARCH就可以了。

要構建C靜態庫,我們需要將上面的MyStrDup實現放到一個main包中:

package main//#include import "C"func main() {    //}//export MyStrDupfunc MyStrDup(s *C.char) *C.char {    return C.strdup(s)}

main包中的main函數不會被執行,但是init函數依然有效。

使用下面的命令就可以構建當前系統的c靜態庫:

go build -buildmode=c-archive

要交叉編譯iOS可用的c靜態庫,我們需要先設定GOARCH,同時開啟cgo特性(交叉編譯時間,cgo預設是關閉的)。

下面是構建針對模擬器的x86/amd64類型的C靜態庫:

export CGO_ENABLED=1export GOARCH=amd64go build -buildmode=c-archive -o libmystrdup_amd64.a

我們使用-o參數指定了輸出的靜態庫檔案名稱。構建命令同時還會產生一個標頭檔(可能叫libmystrdup_386.h),我們沒有用到這個標頭檔,直接刪除掉就可以。

下面是構建針對模擬器的x86/386類型的C靜態庫:

export CGO_ENABLED=1export GOARCH=386go build -buildmode=c-archive -o libmystrdup_386.a

在構建x86/386類型的C靜態庫時可能會有一些link錯誤,我們暫時先用以下方法迴避。

建立一個patch_386.go檔案:

// Copyright 2016 . All rights reserved.// Use of this source code is governed by a BSD-style// license that can be found in the LICENSE file.// 針對iOS模擬器link時缺少的函數// 屬於臨時解決方案package main/*#include #include #include #include size_t fwrite$UNIX2003(const void* a, size_t b, size_t c, FILE* d) {    return fwrite(a, b, c, d);}char* strerror$UNIX2003(int errnum) {    return strerror(errnum);}time_t mktime$UNIX2003(struct tm * a) {    return mktime(a);}double strtod$UNIX2003(const char * a, char ** b) {    return strtod(a, b);}int setenv$UNIX2003(const char* envname, const char* envval, int overwrite) {    return setenv(envname, envval, overwrite);}int unsetenv$UNIX2003(const char* name) {    return unsetenv(name);}*/import "C"

當然,還是會有一些警告出現,暫時忽略它們。

構建多cpu類型的靜態庫

然後,將C靜態庫加入到ios的xcode工程檔案就可以了。

x86構建是比較簡單的,因為我們可以預設使用本地的構建命令。但是,如果要構建arm的靜態庫,則需要先配置好構建環境。

我從Go代碼中扣出了一個clangwrap.sh指令碼(好像是在$GOROOT/misci/ios目錄):

#!/bin/sh# This uses the latest available iOS SDK, which is recommended.# To select a specific SDK, run 'xcodebuild -showsdks'# to see the available SDKs and replace iphoneos with one of them.SDK=iphoneosSDK_PATH=`xcrun --sdk $SDK --show-sdk-path`export IPHONEOS_DEPLOYMENT_TARGET=7.0# cmd/cgo doesn't support llvm-gcc-4.2, so we have to use clang.CLANG=`xcrun --sdk $SDK --find clang`if [ "$GOARCH" == "arm" ]; then    CLANGARCH="armv7"elif [ "$GOARCH" == "arm64" ]; then    CLANGARCH="arm64"else    echo "unknown GOARCH=$GOARCH" >&2    exit 1fiexec $CLANG -arch $CLANGARCH -isysroot $SDK_PATH "$@"

裡面比較重要的是IPHONEOS_DEPLOYMENT_TARGET環境變數,這裡意思是目標最低支援ios7.0系統。

構建arm64環境的靜態庫:

export CGO_ENABLED=1export GOARCH=arm64export CC=$PWD/clangwrap.shexport CXX=$PWD/clangwrap.shgo build -buildmode=c-archive -o libmystrdup_arm64.a

構建armv7環境的靜態庫:

export CGO_ENABLED=1export GOARCH=armexport GOARM=7export CC=$PWD/clangwrap.shexport CXX=$PWD/clangwrap.shgo build -buildmode=c-archive -o libmystrdup_armv7.a

然後我們用lipo命令將以上這些不同的靜態庫打包到一個靜態庫中:

lipo libmystrdup_386.a libmystrdup_adm64.a libmystrdup_arm64.a libmystrdup_armv7.a -create -output libmystrdup.a

這樣的話,只要引入一個靜態庫就可以支援不同cpu類型的目標了。

總結

毛主席教導我們:要在戰爭中學習戰爭。

野雞醫院 這個app是作者第一個iOS應用,這篇教程也是在iOS開發過程逐步學習總結的結果。裡面肯定有很多描述不準確的地方,作者歡迎任何不同意見或建議。

完整的例子:

  • AppStore安裝: https://appsto.re/cn/QH8ocb.i
  • Swift工程: https://github.com/chai2010/ptyy/tree/master/ios-app/yjyy-swift
  • Go靜態庫工程: https://github.com/chai2010/ptyy/tree/master/cmd/yjyy
  • 靜態庫構建指令碼: https://github.com/chai2010/ptyy/tree/master/ios-app/yjyy-swift/vendor/gopkg

所有的代碼均可以免費擷取(BSD協議): https://github.com/chai2010/ptyy

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.