触发c++回调函数时如何更新swift变量

问题描述

我想在我的 .cpp 函数中发生变化或触发事件时通知 swift 代码

我的应用程序遵循以下结构。

(.cpp)[.hpp] ->(.mm)[.h] -> .swift

我可以通过以下方式处理 swift 代码中的 changeThisstring:

let swiftString = CPPWrapper().MyMethodWrapper()

按钮单击/viewDidLoad 没问题,但每当我从 C++ 设置它时,我都想更新此值。

如果 C++ 将新字符串传递给 swift,它不应该等待按钮点击它应该像监听器一样工作。

我会很高兴为您提供任何帮助,谢谢。

示例:

my.cpp:

std::string changeThisstring = "";

...

virtual void myCallBack(MyCallBackParam &paramter){
    changeThisstring = "I WANT TO SEE THIS MESSAGE ON MY APP!"
}

std::string MyClass::MyMethod() {
    return changeThisstring;
}

my.hpp:

#include <string>

class MyClass{
public:
    std::string MyMethod();
};

wrapper.mm

#import "wrapper.h"
#import "my.hpp"

    @implementation CPPWrapper
    
    MyClass myClass;
    
    - (Nsstring*) MyMethodWrapper {
        Nsstring* result = [Nsstring stringWithUTF8String:myClass.MyMethod().c_str()];
        return result;
    }
    @end

Wrapper.h

#import <Foundation/Foundation.h>

@interface CPPWrapper : NSObject
-(Nsstring*) MyMethodWrapper;
@end

.swift

let swiftString = CPPWrapper().MyMethodWrapper()

解决方法

这是触发合并通知的 C 回调示例。

(在 GitHub 上移至此处:https://github.com/moosefactory/C-callback-to-Swift

此示例更改 C 库中 C 字符串的值并将其显示在 SwiftUI 视图的字段中。

enter image description here

你不需要通过 Objective-C。

  • 第一部分是 C 库(很少改动以使其适用于 C++)

  • 第二部分是 CSwift 类

我认为有一个对象可以在 C 代码和 swift 应用程序之间架起桥梁,从应用程序代码中删除深奥的语法,这很好。在本例中,文件MyCLibraryInterface完成了这项工作。

这个类是一个可观察对象,它将使用 combine 发布值更改,因此它有点超出问题 - 一旦您进入回调块,您可以停在那里并做您想做的事情。请注意,我们无法在 c 调用中捕获 swift 上下文(没有调用 self 或在堆上声明的变量)

  • 第三部分是一个简单的 SwiftUI 应用程序,用于接收更改和更新界面

C 库

  • MyCLibrary.h
#ifndef MyCLibrary_h
#define MyCLibrary_h

#include <stdio.h>
#include <dispatch/dispatch.h>
#include <stdlib.h>


/// The callback to call when the string is changed
typedef void callback_t(const char* string);

void setCallBack(callback_t _callback);

/// A function that will change the string
void setString(const char* string);

void startTimer(void);

void cancelTimer(void);

#endif /* MyCLibrary_h */
  • MyCLibrary.c
#include "MyCLibrary.h"

const char* myString;

dispatch_queue_t queue;
dispatch_source_t timer;
bool running;

callback_t* callback;

void setCallBack(callback_t _callback) {
    callback = _callback;
}

void setString(const char* string) {
    myString = string;
    callback(myString);
}

/// A function that will start a timer that changes string

int ticks = 0;

void idle(dispatch_source_t timer)
{
    ticks++;
    char ticksStr[32];
    sprintf(ticksStr,"Time : %ds",ticks);
    setString(ticksStr);
}

void startTimer() {
    if (running) { cancelTimer(); sleep(1); }
    
    queue = dispatch_queue_create("timerQueue",0);

    // Create dispatch timer source
    timer = dispatch_source_create(DISPATCH_SOURCE_TYPE_TIMER,queue);

    dispatch_source_set_event_handler(timer,^{idle(timer);});

    dispatch_source_set_cancel_handler(timer,^{
        dispatch_release(timer);
        dispatch_release(queue);
    });

    dispatch_time_t start = dispatch_time(DISPATCH_TIME_NOW,0);

    // Set timer
    dispatch_source_set_timer(timer,start,NSEC_PER_SEC,0);

    ticks = 0;
    running = true;
    dispatch_resume(timer);
}

void cancelTimer() {
    running = false;
    dispatch_source_cancel(timer);
    char ticksStr[32];
    sprintf(ticksStr,"Canceled after %ds",ticks);
    setString(ticksStr);
}

CSwift 部分

  • MyApp-Bridging-Header.h
#import "MyCLibrary.h"
  • MyCLibraryInterface.swift
import Foundation

class MyCLibraryInterface: ObservableObject {
    @Published var string: String = "This is a string"
    
    static let shared = MyCLibraryInterface()
    
    init() {
        setCallBack { stringPtr in
            let newString = CFStringCreateWithCString(kCFAllocatorDefault,stringPtr,kCFStringEncodingASCII) ?? "" as CFString
            DispatchQueue.main.async {
                MyCLibraryInterface.shared.string = newString as String
            }
        }
    }
    
    func setLibString(string: String) {
        string.withCString { stringPointer in
            setString(stringPointer)
        }
    }
    
    func startLibTimer() {
        startTimer()
    }

    func cancelLibTimer() {
        cancelTimer()
    }
}

SwiftUI 示例

此示例应用程序显示初始字符串和按钮。单击或点击时,会在 CLibrary 中调用 setString 函数,调用 swift 回调并在 ObservableObject 修改后更新视图

import SwiftUI

struct ContentView: View {
    @ObservedObject var myCLibInterface: MyCLibraryInterface = MyCLibraryInterface.shared
    
    var body: some View {
        VStack {
            Text(myCLibInterface.string).frame(width:150).padding()
            Button("Reset") {
                myCLibInterface.setLibString(string: "C Timer Example")
            }.padding()
            Button("Start Timer") {
                myCLibInterface.startLibTimer()
            }.padding()
            Button("Cancel") {
                myCLibInterface.cancelLibTimer()
            }.padding()
        }.padding(20)
    }
}

struct ContentView_Previews: PreviewProvider {
    static var previews: some View {
        ContentView()
    }
}

相关问答

Selenium Web驱动程序和Java。元素在(x,y)点处不可单击。其...
Python-如何使用点“。” 访问字典成员?
Java 字符串是不可变的。到底是什么意思?
Java中的“ final”关键字如何工作?(我仍然可以修改对象。...
“loop:”在Java代码中。这是什么,为什么要编译?
java.lang.ClassNotFoundException:sun.jdbc.odbc.JdbcOdbc...