c++ - 使用 .mm 文件中的 Objective-C++ 和 C++ 类将 Apple 的 ClassKit 和 swift 类合并到现有应用程序中时出错
问题描述
我正在做一个测试项目,看看 Apple 的 ClassKit 是否可以合并到现有的 ios 教育应用程序中,其中包含 .mm 文件中的 Objective-C++ 和 C++ 类,并且在 ...-Swift.h 桥接头中出现错误(在我添加 swift 文件时由 xcode 自动创建),甚至在将桥接头导入到 ObjectiveC++ 文件以允许它们访问 Swift 类之前。
作为一个简单的测试项目,ObjectiveC-Test,我创建了一个类:
// Quiz.swift
import Foundation
import ClassKit
@objcMembers public class Quiz : NSObject
{
var mTitle : String
var mNumberOfProblems : Double
var mNumberCorrect : Double
var context = CLSContext (type : CLSContextType.quiz, identifier : "Test", title : "Parent Test")
override init ()
{
self.mTitle = ""
self.mNumberOfProblems = 0
self.mNumberCorrect = 0
super.init()
}
init (title : String)
{
self.mTitle = title
self.mNumberOfProblems = 0
self.mNumberCorrect = 0
}
} // end class
protocol Node
{
var parent : Node? { get }
var children : [Node]? { get }
var identifier : String { get }
var contextType : CLSContextType { get }
}
extension Quiz : Node
{
var parent : Node? { return nil }
var children : [Node]? { return nil }
var identifier : String { return mTitle }
var contextType : CLSContextType { return .quiz }
}
“protocol Node”和“extension Quiz : Node”取自 Apple GreatPlays 项目,该项目说明了如何将 ClassKit 合并到项目中,但严格来说是 Swift 代码。CLSContext 是一个 ClassKit 类,不会导致任何问题,但是“CLSContextType”,ClassKit 中的枚举会导致 ...-Swift.h 标头中的错误:“未知类型名称 'CLSContextType'。
但是,在测验类中,“var context = CLSContext (...)”中的“CLSContextType.quiz”不会导致任何问题。但是,如果我在测验类中添加“var contextType = CLSContextType.quiz”行,我会收到错误消息。
xcode 生成的 swift 桥接头 (ObjectiveC_Test-Swift.h) 中的相关代码为:
@class CLSContext;
SWIFT_CLASS("_TtC15ObjectiveC_Test4Quiz")
@interface Quiz : NSObject
@property (nonatomic, copy) NSString * _Nonnull mTitle;
@property (nonatomic) double mNumberOfProblems;
@property (nonatomic) double mNumberCorrect;
@property (nonatomic, strong) CLSContext * _Nonnull context;
- (nonnull instancetype)init OBJC_DESIGNATED_INITIALIZER;
- (nonnull instancetype)initWithTitle:(NSString * _Nonnull)title OBJC_DESIGNATED_INITIALIZER;
@end
@interface Quiz (SWIFT_EXTENSION(ObjectiveC_Test))
@property (nonatomic, readonly, copy) NSString * _Nonnull identifier;
@property (nonatomic, readonly) CLSContextType contextType;
@end
错误发生在倒数第二行:“CLSContextType contextType”:“未知类型名称'CLSContextType'”。它还会产生一个错误:“接口类型不能被静态分配”但我认为这是由于第一个错误。第八行的“CLSContext”不会导致任何错误。
如果我在协议和节点扩展中注释掉 CLSContextType 行,项目运行良好。
该项目在 xcode 10.0 和 ios 11.4 下。
构建设置包括:
Packaging
Defines Module: Yes
Build Options
Always Embed Swift Standard Libraries: Yes
Swift Compiler - General:
Install Objective-C Compatibility Header: Yes
Objective-C Bridging Header: ObjectiveC-Test/ObjectiveC-Test-Bridging-Header.h
Objective-C-Generated Interface Header Name: ObjectiveC_Test-Swift.h
Apple Clang - Language - C++
C++ Language Dialect: GNU++14 [std=gnu++14]
C++ Standard Library: libcc++ (LLVM C++ standard library with C++11 support)
任何帮助将不胜感激。
更新:
从 Quiz 类中删除“@objcMembers”消除了 xcode 创建的桥接头中的 CLSContextType 错误,但随后无法从 Objective-C++ .mm 文件中访问类属性。将“@obj”添加到那些已修复但不适用于 contextType。
然后可以在 Objective-C++ .mm 文件中访问和打印类属性。
协议属性,包括 CLSContextType,可以从 swift 函数 (start) 和节点扩展函数 (printContextTypeFromNodeExtension) 打印到 Objective-C++ .mm 文件中。
// @objcMembers public class Quiz : NSObject
public class Quiz : NSObject
{
@objc var mTitle : String
@objc var mNumberOfProblems : Double
@objc var mNumberCorrect : Double
// @objc var mLessonContextType : CLSContextType { return .lesson } // doesn't work
// causes same bridging error
@objc func start ()
{
print ("printing title from start() in swift: \(mTitle)")
print ("printing identifier from start() in swift: \(identifier)")
print ("printing CLSContextType from start() in swift: \(contextType.rawValue)")
printContextTypeFromNodeExtension ()
}
} // end class
extension Node
{
func printContextTypeFromNodeExtension ()
{
print ("printing CLSContextType from Node extension: \(contextType.rawValue)")
}
}
Objective-C++ .mm 文件:
Quiz * Q1 = [ [Quiz alloc] init];
Q1.mTitle = @"Place Value Blocks";
Q1.mNumberOfProblems = 10;
Q1.mNumberCorrect = 3;
printf ("printing mTitle from objc: %s \n", [Q1.mTitle UTF8String] );
printf ("printing mNumberCorrect from objc: %f \n", Q1.mNumberCorrect);
[Q1 start];
Results:
printing mTitle from objc: Place Value Blocks
printing mNumberCorrect from objc: 3.000000
printing title from start() in swift: Place Value Blocks
printing identifier from start() in swift: Place Value Blocks
printing CLSContextType from start() in swift: 8
printing CLSContextType from Node extension: 8
将“@objc”添加到 Node 协议允许在 Objective-C++ .mm 文件中访问除 CLSContextType 之外的所有 Node 属性。如果保留,则返回快速桥接头错误。
@objc protocol Node
{
var parent : Node? { get }
var children : [Node]? { get }
var identifier : String { get }
// var contextType : CLSContextType { get } // error if included when "@objc" added
}
extension Quiz : Node
{
var parent : Node? { return nil }
var children : [Node]? { return nil }
var identifier : String { return mTitle }
// var contextType : CLSContextType { return .quiz }
}
Objective-C++ .mm file:
printf ("printing parent from objc: %p \n", [Q1 parent] );
printf ("printing children from objc: %p \n", [Q1 children] );
printf ("printing identifier from objc: %s \n", [Q1.identifier UTF8String] );
Results:
printing parent from objc: 0x0
printing children from objc: 0x0
printing identifier from objc: Place Value Blocks
结论:我在另一篇文章中发现了一条评论,说在 Swift 中定义的枚举和结构在 Objective-C 中不可用,但找不到参考,因此最好使用类而不是结构并处理枚举仅在 swift 文件中。
解决方案
您的 ObjC 标头是否导入 ClassKit?
协议,需要用@objc 声明。
@objc protocol Node
推荐阅读
- eslint - 无法关闭 eslint no-unused-expressions
- python - Matplotlib - 为什么图形会被保存两次?
- localization - 嵌入式脚本语言的编译时本地化
- javascript - 当我声明一个函数的属性时,我在哪里存储一个值?
- ios - 在 iOS 中流式传输谷歌照片视频不起作用(在 Safari 中也不起作用)
- css - 在两行文本后添加省略号和工具提示 - React
- python - 如何使用 pymunk 的碰撞处理程序调用函数?
- java - 如何使用自定义事件侦听器从 Java 中的多线程接收数据?
- php - 从 Chrome 访问服务器时 PHP 命令执行两次
- python - 当有很多这样的元素时,如何找到列表中出现次数最多的元素?