簡體   English   中英

ObjC 應用程序構建在 ObjC 靜態庫中調用 swift 函數時出現構建錯誤

[英]ObjC app build gets build error for call to swift function within ObjC static library

試圖讓 ObjC 應用程序項目在 ObjC 靜態庫中調用 Swift 函數.......

我的 ObjC 應用項目構建獲取構建錯誤以引用導入應用項目的 ObjC 靜態庫 (.a) 中的 Swift 函數。

文件 Hub_lib-Bridging-Header.h 沒有代碼。

OBJ-C APP 項目............................................................ .

ObjC 應用程序項目中的 ViewController.mm...

#import "ViewController.h"
#import "Hub_lib.h"
#import "Hub_lib-Swift.h"

#import "hublib.hpp"

@interface ViewController ()
@end

@implementation ViewController
. . .
- (IBAction)run_simple_central:(id)sender {
    [self   BLE.start_central];
}

ObjC 應用程序項目中的 BLE.h ......

#import <CoreBluetooth/CoreBluetooth.h>
#import "Hub_lib-Swift.h"


@interface BLE: NSObject
//< CBPeripheralManagerDelegate >
    @property(strong, nonatomic) CBPeripheralManager* peripheralManager;
    @property(strong, nonatomic) CBMutableCharacteristic* transferCharacteristic;
    @property(strong, nonatomic) NSData* dataToSend;
    @property(nonatomic, readwrite) NSInteger sendDataIndex;
    -(void)start_central;

@end /* BLE_h */

應用內的BLE.m; 用於調用 swift 的包裝器 .....................

#import "BLE.h"
#import "Hub_lib-Swift.h"

@interface BLE ()
@end

@implementation BLE

-(void)start_central
{
        Hub_lib* BLE_central = [Hub_lib new];

    [BLE_central    centralManager.run_central];
}

制作了一個測試項目,以便能夠復制您的錯誤。 您很接近,但您需要注意靜態庫如何在其標頭中公開其內部方法和類,以便您可以在其他地方使用它們。

讓我們從 Objective-C 靜態庫項目開始。 Hub_lib.h

#import <Foundation/Foundation.h>

// we want the lib header as clean as possible
// it will then be imported in your project with '#import <Hub_lib/Hub_lib.h>'

// auto-generated swift -> objc bridging header imported here will
// mess it up when imported somewhere else.
// so when using swift->objc bridging place next line in .m file instead
//#import "Hub_lib-Swift.h"

//@class BLE_Central; // pre-declaration of a later fully declared Class.
// as we moved the property into .m file we dont need it here.

@interface Hub_lib : NSObject

// to make this work you would need pre-declaration of BLE_Central, see above interface
//@property BLE_Central *ble; // placed in .m interface extension instead.

-(void)run_central;

@end

靜態庫對應物/實現Hub_lib.m

#import "Hub_lib.h"
#import "Hub_lib-Swift.h"

@interface Hub_lib ()
@property BLE_Central *ble_central;
@end;

@implementation Hub_lib

-(instancetype)init {
    if (!(self=[super init])) return nil;
    _ble_central = [[BLE_Central alloc] init];
    return self;
}
-(void)run_central {
    [_ble_central run_central];
}

@end

注意BLE_Central屬性放置在類接口擴展中,當您想使用暴露回 objc 的 swift 模塊內容時,您需要在某處聲明自動生成的橋(最好在.m文件中完成#import "Hub_lib-Swift.h" )

您的BLE_central.swift及其協議方法實現

import Foundation

import UIKit
import CoreBluetooth
import os

var centralManager: CBCentralManager = CBCentralManager()

class BLE_Central: NSObject, CBCentralManagerDelegate, CBPeripheralDelegate {
    
    func centralManagerDidUpdateState(_ central: CBCentralManager) {
        os_log("centralManagerDidUpdateState")
    }
    
    var discoveredPeripheral: CBPeripheral?
    var transferCharacteristic: CBCharacteristic?
    var writeIterationsComplete = 0
    var connectionIterationsComplete = 0

    let defaultIterations = 5  // change this value based on test usecase

    var data = Data()

    // as you figured out before we need to expose to @objc
    @objc public func run_central()
    {
        os_log("run_central")
        // out-commented for testing purposes
        //mobile_sys_hub_lib.centralManager = CBCentralManager(delegate: self, queue: nil, options: [CBCentralManagerOptionShowPowerAlertKey: true])
        os_log("Scanning started")
    }   
}

只要在靜態庫中的 swift 中沒有使用來自 objc 的額外代碼, Hub_lib-Bridging-Header.h就是空的

//
//  Use this file to import your target's public headers that you would like to expose to Swift.
//

接下來讓我們看看如何在“sim 后端 UI”Objective-C 項目應用程序中導入靜態庫。
轉到“sim backend UI” App Target settings > General > Framework, Libraries, and.. > 點擊+按鈕並搜索你編譯的libHub_lib.a文件。 > 選擇它 > 點擊確定。 現在應該在你的框架列表中。

是的,這還不夠! 您必須在您的應用程序項目中的某處聲明其標頭。 具體在哪里取決於您。 我們在BLE.h執行以下操作,而不是多次實現它

#import <Foundation/Foundation.h>

#import <CoreBluetooth/CoreBluetooth.h>
//#import "Hub_lib-Swift.h" // no no no no
#import <Hub_lib/Hub_lib.h> // much better. test-compile once if it is crying

NS_ASSUME_NONNULL_BEGIN

@interface BLE: NSObject
// <CBPeripheralManagerDelegate> // "//<Protocol>" will confuse doxygen

@property (strong, nonatomic) CBPeripheralManager* peripheralManager;
@property (strong, nonatomic) CBMutableCharacteristic* transferCharacteristic;
@property (strong, nonatomic) NSData* dataToSend;
@property (nonatomic, readwrite) NSInteger sendDataIndex;

-(void)start_central;

@end /* BLE_h */

NS_ASSUME_NONNULL_END

對應的BLE.m

#import "BLE.h"

@implementation BLE

-(void)start_central
{    
    NSLog(@"invoked BLE start_central");
    Hub_lib *Hub_central = [Hub_lib new];
    [Hub_central run_central];
}

@end

您的ViewController.m.mm使用BLE.h及其已導入的靜態庫標頭

#import "ViewController.h"

#import "BLE.h"

@interface ViewController ()
@property (nonatomic) BLE *ble;
@end

@implementation ViewController

- (void)viewDidLoad {
    [super viewDidLoad];
    // Do any additional setup after loading the view.
    
    UIButton *btn = [UIButton buttonWithType:UIButtonTypeSystem];
    btn.frame = CGRectMake(100, 100, 200, 50);
    [btn setTitle:@"run simple central" forState:(UIControlStateNormal)];
    [btn setTitleColor:UIColor.greenColor forState:(UIControlStateNormal)];
    btn.layer.backgroundColor = UIColor.orangeColor.CGColor;
    btn.layer.cornerRadius = 5.0f;
    [self.view addSubview:btn];
    
    [btn addTarget:self action:@selector(run_simple_central:) forControlEvents:(UIControlEventTouchUpInside)];
}

- (IBAction)run_simple_central:(id)sender {

    // BLE needs to be allocated to take effect.
    if (!_ble) _ble = [[BLE alloc] init];
    
    // testing.. should log "run_central" + "Scanning started"
    [self.ble start_central];
}

@end

制作了一個 testButton 以便您可以查看實現是否調用了您所期望的。 編譯!

和? 哎呀! 不起作用。 發生了什么?

如果你的應用程序是一個普通的 Objective-C 項目,它還不知道 swift 並且會以一種奇怪的方式抱怨,可能是通過類似的方式

鏈接:無法找到或使用自動鏈接庫“swiftCoreImage”
未定義符號:Builtin.UnknownObject* 的值見證表

解決方案:最簡單的方法是在您的應用程序項目中創建一個 swift 文件,並允許 Xcode 為您制作橋接頭。 (或者更改項目設置,搜索“bridge”並逐步瀏覽屬性) swift 文件不必具有特殊內容。

//
//  MakeProjectSwiftCompatible.swift
//
import Foundation

再次編譯。 現在它應該可以工作了,因為當您的 Objc-App-Project 能夠處理 swift 的東西時,靜態庫中部分實現的 swift 模塊可以正常工作。


當你要求在你的靜態庫中使用 Objective-C++/C++ 時進行編輯,事情發生了一些變化......所以這里有一些額外的示例代碼來證明它。 在 Hub_lib 項目(針對您的“框架”)中添加一些文件,這些文件將保留一些隨機測試 C++ 代碼

//HubCPP.h
#import <Foundation/Foundation.h>

#include <vector>

NS_ASSUME_NONNULL_BEGIN
class SomeCPPClass
{
public:
    SomeCPPClass(id<NSObject> obj, size_t size);
    ~SomeCPPClass();
    id<NSObject>                    getBuffer() { return buffers[bufferIdx]; }
    unsigned int                    getCurrentIdx();
private:
    std::vector <id <NSObject>>     buffers;
    unsigned int                    bufferIdx;
    bool                            isReady;
};
NS_ASSUME_NONNULL_END

要讓 Xcode 知道您使用 C++,您需要有以.mm結尾的實現文件,並且還需要將HubCpp.hIdentity and Type (選擇文件時右側 Xcode 面板)”更改為C++ Header

// HubCpp.mm
#import "HubCPP.h"

SomeCPPClass::SomeCPPClass (id<NSObject> obj, size_t size) :
bufferIdx (0),
isReady(false)
{
    uint8_t ringSize = 255;
    assert (ringSize > 0);
    for (uint8_t i = 0; i < ringSize; i++)
    {
        //buffers.push_back ();
        bufferIdx = (unsigned int)size;
    }
}
SomeCPPClass::~SomeCPPClass() {
    // cleanup allocated stuff here.
}

unsigned int SomeCPPClass::getCurrentIdx() {
    return bufferIdx;
}

Hub_lib.m重命名為.mm並將其導入規則相應地更改為以下 ..

#import "Hub_lib.h"
#import <CoreBluetooth/CoreBluetooth.h> //needed because the -Swift.h bridge will cry in the next line
#import "Hub_lib-Swift.h"
#import "HubCPP.h"

讓我們更改Hub_lib.mm的校對方法,使其真正使用 C++

-(void)run_central {
    [_ble_central run_central];
    SomeCPPClass *cpp = new SomeCPPClass(@"justSomeNSStringObject",2);
    unsigned int idx = cpp->getCurrentIdx();
    NSLog(@"objectiveCplusplus testIdx = %u", idx);
}

編譯 Hub_lib(方案)。 它現在應該可以工作並且還接受使用#import <vector>

如果這有效,請更改您的 Objc-Project-App。
切換您的編譯方案以定位您的 Objc-App。
將文件名BLE.m更改為BLE.mm (使其成為目標 C++ 源)
將文件名BLE.h更改為BLE.hh (使其成為 C++ 頭文件)
將 BLE.mm #import "BLE.h更改為#import "BLE.hh
ViewController.m踢出#import "BLE.h"並將其替換為ViewController.h而不是#import "BLE.hh"
(通常,當您將導入頭文件放在頭文件中時,讓您的編譯器知道在實現中期望使用哪種語言要容易得多。)

編譯。 就是這樣! 此時您的 Objective-C++ 靜態庫應該可以正常工作。

編輯您可以在此處找到 Xcode 的現成工作區...
github:在靜態庫中結合 cpp swift 和 Objective-c

來自 StackO 用戶的解決方案:Asperi

  1. 清空頂層文件夾

  2. 在頂部文件夾 1.1 創建工作區將干凈的應用程序和庫復制到頂部文件夾

  3. 使用 Xcode > File > Add files ... 將 ObjC lib .xcodeproj 添加到工作區

  4. 將 ObjC 應用程序 .xcodeproj 添加到工作區

  5. 通過工作區 a 添加了 sim_backend_UI 對 lib 的依賴。 app proj > 常規選項卡 > 框架、庫.. > + b. 選擇 lib .a c。 添加。

  6. 將 Some.swift(您想要的任何 swift 文件)添加到 sim_backend_UI,只是為了 Xcode 添加所需的系統 swift 動態庫(靜態庫中的 swift 部分也需要這些庫)...並確認在出現的對話框中創建橋. 新文件 > Swift > Some.swift b. 創建橋接頭 c. 添加到 Some.swift ...

進口基金會

結構一些{}

  1. 將 Xcode 活動方案設置為應用程序和目標設備
  2. 構建“成功”

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM