自 Swift 推出以來,新專案可直接使用 Swift 開發,但實際專案中常會遇到需整合既有 Objective-C 類別、封裝模組或第三方 Objective-C 庫的情況。此時「混合編程」便成為關鍵技術,自 iOS 8 起正式支援。本文將系統說明在同一專案目錄下,Swift 調用 Objective-C、Swift 調用 C 函式、Objective-C 調用 Swift 三種核心混編場景,並記錄實作過程中的關鍵細節與避坑指南。
以 SwiftTest 為專案名稱建立 Swift 專案。新增 Swift 類別時,務必選擇 Cocoa Touch Class 並繼承 NSObject,否則 Objective-C 無法直接識別該類別。另需注意 Target 選擇 iOS Source,預設的 OS X Source 會導致匯入 Cocoa 模組而非 UIKit,造成混編錯誤。
在 Swift 專案中新增 Objective-C 類別時,Xcode 會提示建立橋接頭文件(專案名-Bridging-Header.h),點選 Create Bridging Header 即可自動生成。若無提示,可手動新增 Header 檔並在 Target → Build Settings → Objective-C Bridging Header 設定路徑(如 SwiftTest/JeckHeader.h)。橋接頭文件是 Swift 存取 Objective-C 與 C 宣告的關鍵。
新增 C File 時,勾選 Also create a header file 可同時產生 .h 與 .c,便於後續在橋接頭文件中匯入。本文分別建立含頭文件(CClass.h/.c)與無頭文件(CClassNo.c)兩種情況示範。
// SwiftClass.swift
import UIKit
class SwiftClass: NSObject {
func sayHello(name: String) -> String {
return "Hello \(name)!"
}
}
// OCClass.h
#import
@interface OCClass : NSObject
int sum2(int a, int b);
- (void)desc22;
- (void)desc2;
@end
// OCClass.m
#import "OCClass.h"
#import "SwiftTest-Swift.h" // 自動生成的 Swift 模組頭文件
@implementation OCClass
- (void)desc22 {
int (^block)(int, int) = ^(int a, int b) { return a + b; };
int result = block(10, 40);
NSLog(@"Swift呼叫OC方法輸出 result:%d", result);
SwiftClass *sc = [[SwiftClass alloc] init];
NSString *str = [sc sayHello:@"jeck"];
NSLog(@"OC呼叫Swift函式輸出 %@", str);
}
- (void)desc2 {
int (^block)(int, int) = ^(int a, int b) { return a + b; };
int result = block(10, 40);
printf("Swift呼叫OC方法輸出 result:%d\n", result);
}
int sum2(int a, int b) { return a + b; }
@end
// CClass.h
#ifndef CClass_h
#define CClass_h
#include
int sum3(int a, int b);
void desc3();
#endif
// CClass.c
#include "CClass.h"
int sum3(int a, int b) { return a + b; }
void desc3() {
int (*p)(int, int) = sum3;
int result = p(10, 10);
printf("Swift呼叫含頭文件C函式輸出:%d\n", result);
}
// CClassNo.c(無頭文件)
#include
int sum1(int a, int b) { return a + b; }
void desc1() {
int (*p)(int, int) = sum1;
int result = p(10, 20);
printf("Swift呼叫無頭文件C函式輸出 result:%d\n", result);
}
// SwiftTest-Bridging-Header.h
#import "CClass.h"
#import "OCClass.h"
// 無頭文件的 C 函式需在此前向宣告
void desc1();
int sum1(int a, int b);
// ViewController.swift
import UIKit
class ViewController: UIViewController {
override func viewDidLoad() {
super.viewDidLoad()
// Swift 呼叫 OC 方法
let oc = OCClass()
oc.desc2()
oc.desc22()
let sum2Result = sum2(10, 1)
print("Swift呼叫OC類別中的C函式輸出:\(sum2Result)")
// Swift 呼叫無頭文件 C 函式
desc1()
let sum1Result = sum1(10, 2)
print("Swift呼叫無頭文件C函式輸出:\(sum1Result)")
// Swift 呼叫含頭文件 C 函式
desc3()
let sum3Result = sum3(10, 3)
print("Swift呼叫含頭文件C函式輸出:\(sum3Result)")
}
}
程式成功編譯執行,輸出結果顯示各跨語言呼叫均正常運作。關鍵重點:
專案名-Swift.h),此文件不顯示於專案導航器,但可 Cmd+Click 跳轉查看。NSObject 或標註 @objc,否則不會被編譯進模組頭文件,導致 Objective-C 找不到宣告。掌握上述機制,即可在專案中靈活混用 Swift、Objective-C 與 C,最大化程式碼復用與開發效率。