OC和Swift混編

weixin_34041003發表於2018-12-06

OC和Swift的混編,分為兩種情況:

  • OC專案中嵌入Swift檔案
  • Swift專案中嵌入OC檔案

先來看OC專案中嵌入Swift檔案的情況

新建一基於OC的專案,比如:OC_project

接下來我們建立一個Swift類:BridgeTest.swift,並在裡面新建一個類:Person

-- Person.swift --
    
import Foundation

// public可以省略,外面同樣可以調得到
// 要OC呼叫到這個Swift類,則@objc不可省略,另外要直接或間接繼承於NSObject
@objc public class Person: NSObject { 
    // 為了讓OC呼叫,方法同樣需要@objc標示
    @objc
    func think() -> Void {
        print("person can think, it's important.")
    }
    
    @objc
    func eat() -> Void {
        print("person can eat, it's important.")
    }
    
    @objc
    func drink(_ water: String) -> Void {
        print("person can drink \(water), it's important")
    }
}

注:這裡的Swift檔名叫BridgeTest.swift,而類名叫Person, 這並不是一個好的命名習慣,一般檔名和類名可以統一,在此寫成不一樣,是為了說明這和Java並不一樣,在java中一個class被命名為public的,則此檔名必須和此public的類名相同,但swift中並沒有這個限制。

在新建這個Swift類時,Xcode會提示生成橋接檔案:


65036-9871efea39994c86.png
1.png

這個橋接檔案的作用主要是在Swift中呼叫OC的,我們暫且不理會,照提示確定即可。這會生成檔案OC_project-Bridging-Header.h

然後我們在OC的類裡面呼叫這個Swift類:
#import "ViewController.h"
#import "OC_project-Swift.h" // 如果要使用Swift類,這個標頭檔案需要新增,此檔案是系統自動生成,不可見

@interface ViewController ()

@end

@implementation ViewController

- (void)viewDidLoad {
    [super viewDidLoad];

    // 依賴於Apple自動生成的‘工程名-Swift.h’檔案,本示例中是:OC_project-Swift.h
    // 其實在這個檔案中宣告瞭一些Swift到OC的轉換後方法,因此我們可以以OC的呼叫方式呼叫Swift方法
    Person *p = [[Person alloc] init];
    NSLog(@"%@", p);
    [p think];
    [p eat];
    [p drink:@"咖啡"];
}

注意,一般這樣做就可以了,但Xcode有時候會抽瘋的,因此如果有問題我們需要檢查Xcode的設定:

  • Defines Module : YES

    65036-b58d8925994160f0.png
    2.png

  • Product Module Name : yourProjectName

    65036-57a9f72a9ea1abd7.png
    3.png

    Make sure that your Product Module Name doesn't contain any special characters.

  • Install Objective-C Compatibility Header : YES

    65036-1d0fde9ee5fb336d.png
    4.png

    Once you've added *.swift file to the project this property will appear in Build Settings。

  • Objective-C Generated Interface Header : yourProject-Swift.h

    65036-26a6f20f54a9047f.png
    5.png

    This header is auto generated by Xcode

  • Objective-C Bridging Header : $(SRCROOT)/yourProject-Bridging-Header.h

    65036-c56b3e94d08411ae.png
    6.png

上面是讓OC呼叫Swift方法,下面我們依然基於這個OC專案,讓Swift呼叫OC,在做這個之前我們需要知道一個概念:

Objective-C呼叫Swift: 需要在暴露出來的Swift方法和屬性上加@objc標識,否則不可用;且檔案的類需要繼承自NSObject或NSObject的子類。另外如果仍有問題,需要在工程配置裡面做一些小改動。
Swift呼叫Objective-C: 需要在統一的bridge標頭檔案(YourProjectName-Bridging-Header.h)裡面import,然後即可使用。

在上面的示例中, 使用OC調Swift並不需要使用橋接檔案, 但如果要讓Swift調OC,則需要在這個檔案中import要在Swift中使用的OC類的標頭檔案。
接下來我們新建一個OC的Dog類,讓Swift方法呼叫這個Dog類的方法。

-- Dog.h --
@interface Dog : NSObject

- (void)think;
- (void)eat:(NSString *)food;
@end

-- Dog.m --
@implementation Dog

- (void)think {
    NSLog(@"Dog can think, but it's different with person's think");
}

- (void)eat:(NSString *)food {
    NSLog(@"%@", [NSString stringWithFormat:@"dog can eat: %@, it's important", food]);
}

@end

然後在BridgeTest.swift中的Person類中新增兩個方法:

@objc
func dogThink() -> Void {
    let dog: Dog = Dog()
    dog.think()
}

@objc
func dogEat(_ food: String) -> Void {
    let dog: Dog = Dog()
    dog.eat(food)
}
-- OC_project-Bridging-Header.h --
#import "Dog.h"
-- ViewController.m --
@implementation ViewController

- (void)viewDidLoad {
    [super viewDidLoad];
    
    // 依賴於Apple自動生成的‘工程名-Swift.h’檔案
    Person *p = [[Person alloc] init];
    NSLog(@"%@", p);
    [p think];
    [p eat];
    [p drink:@"咖啡"];
    [p dogThink]; // 新增
    [p dogEat:@"狗糧"]; // 新增
}

@end

以上是基於OC專案嵌入Swift檔案,接下來我們在Swift專案中嵌入OC檔案

Swift專案中嵌入OC檔案相對比較簡單,新建一基於Swfift的專案,比如:Swift_project, 然後新建OC類,在新建的時候同樣按照系統提示生成bridge檔案。

如果Swift類要呼叫OC,只須在Swift_project-Bridging-Header.h中#import相對應的OC類即可。如果OC類要呼叫Swift, 只需#import "Swift_project-Swift.h"即可。

示例:

-- Person.h --
@interface Person : NSObject

- (void)think;
- (void)dogThink;

@end

-- Person.m --
#import "Person.h"
#import "Swift_project-Swift.h"

@implementation Person

- (void)think {
    NSLog(@"think");
}

- (void)dogThink {
    Dog *d = [[Dog alloc] init];
    [d think];
}

@end

-- Dog.swift --
import UIKit

@objc
class Dog: NSObject {
    @objc
    public func think() -> Void {
        print("dong think, but different with person")
    }
}

-- Swift_project-Bridging-Header.h --
#import "Person.h"
-- ViewController.swift --
class ViewController: UIViewController {
    override func viewDidLoad() {
        super.viewDidLoad()
     
        let p: Person = Person()
        p.think()
        p.dogThink()
    }
}

相關文章