在 XPC 协议中使用自定义 class
Using custom class in XPC protocol
我正在尝试在 withReply
签名上使用我自己的类型编写 XPC 服务。 type/class 拥有主应用程序和 XPC 服务的 Xcode 的“目标成员资格”。但是,即使在 withReply
签名中使用了相同的 class,但我在调试输出中得到了 incompatible reply block signature
,但是 Xcode 目标不同,我将在下面解释。
注意:这是在 Swift 中完成的,使用 this project 让我开始。除了那里他们使用 NSData
而不是自定义类型。
详情
为了这个问题的目的,我将使用下面的例子
- 自定义class -
Tweet
- 这个class符合NSSecureCoding
协议所以可以在主app和XPC服务之间传递
- XPC 协议 -
TweetTransfer
需要一种方法 func take(_ count: Int, withReply: ((Tweet) -> Void))
然后是所有常用的 XPC 样板文件,我在其中导出符合 TweetTransfer
的对象。 XPC 服务似乎已启动,但随后在它与主应用程序之间传输失败并显示
XPCWorker[11069:402719] <NSXPCConnection: 0x61800010e220> connection from pid 11066 received an undecodable message
下面是完整的消息[1],但“wire”和“local”之间的唯一区别是参数一是
- 电线 -
_TtC17MainApp5Tweet
- 本地 -
_TtC23XPCWorker5Tweet
其中 Xcode 目标不同。这足以把它扔掉吗?那么如何在应用程序和它的 XPC 服务之间共享代码?
[1] 完整错误文本
<NSXPCConnection: 0x61800010e220> connection from pid 11066 received an undecodable message (incompatible reply block signature (wire: <NSMethodSignature: 0x618000074ec0>
number of arguments = 2
frame size = 224
is special struct return? NO
return value: -------- -------- -------- --------
type encoding (v) 'v'
flags {}
modifiers {}
frame {offset = 0, offset adjust = 0, size = 0, size adjust = 0}
memory {offset = 0, size = 0}
argument 0: -------- -------- -------- --------
type encoding (@) '@?'
flags {isObject, isBlock}
modifiers {}
frame {offset = 0, offset adjust = 0, size = 8, size adjust = 0}
memory {offset = 0, size = 8}
argument 1: -------- -------- -------- --------
type encoding (@) '@"_TtC17MainApp5Tweet"'
flags {isObject}
modifiers {}
frame {offset = 8, offset adjust = 0, size = 8, size adjust = 0}
memory {offset = 0, size = 8}
class '_TtC17MainApp5Tweet'
vs local: <NSMethodSignature: 0x610000074740>
number of arguments = 2
frame size = 224
is special struct return? NO
return value: -------- -------- -------- --------
type encoding (v) 'v'
flags {}
modifiers {}
frame {offset = 0, offset adjust = 0, size = 0, size adjust = 0}
memory {offset = 0, size = 0}
argument 0: -------- -------- -------- --------
type encoding (@) '@?'
flags {isObject, isBlock}
modifiers {}
frame {offset = 0, offset adjust = 0, size = 8, size adjust = 0}
memory {offset = 0, size = 8}
argument 1: -------- -------- -------- --------
type encoding (@) '@"_TtC23XPCWorker5Tweet"'
flags {isObject}
modifiers {}
frame {offset = 8, offset adjust = 0, size = 8, size adjust = 0}
memory {offset = 0, size = 8}
class '_TtC23XPCWorker5Tweet'
)
更新
关于协议、remoteObjectProxy 连接和 Tweet 对象的更多信息。这是用于 XPC 调用的协议:
@objc(TweetTransfer)
protocol TweetTransfer {
func take(_ count: Int, withReply: replyType)
}
typealias replyType = ((Tweet) -> Void)
为了方便起见,我使用了类型别名。然后 Tweet
对象非常简单,仅用于测试(尽管支持 NSSecureCoding 有点复杂):
final class Tweet: NSObject, NSSecureCoding {
var name: String
var text: String
static var supportsSecureCoding = true
init(name: String, text: String) {
self.name = name
self.text = text
}
init?(coder aDecoder: NSCoder) {
guard let name = aDecoder.decodeObject(forKey: "name") as? String else {
fatalError("Could not deserialise name!")
}
guard let text = aDecoder.decodeObject(forKey: "text") as? String else {
fatalError("Could not deseralise text!")
}
self.name = name
self.text = text
}
func encode(with aCoder: NSCoder) {
aCoder.encode(name, forKey: "name")
aCoder.encode(text, forKey: "text")
}
}
最后是我们调用 remoteObjectProxy 的点
guard let loader = workerConnection.remoteObjectProxyWithErrorHandler(handler) as? TweetTransfer else {
fatalError("Could not map worker to TweetTransfer protocol!")
}
var tweets = [Tweet]()
loader.take(1) { tweet in
tweets.append(tweet)
}
The full message is below but the only difference between the “wire” and “local” is that argument one is
- wire -
_TtC17MainApp5Tweet
- local -
_TtC23XPCWorker5Tweet
Where the Xcode target is different. Is that enough to throw it off? How then do I share code between an app and it's XPC service?
这确实足以摆脱它。 Swift 的命名空间使归档对象显示为不同的 class。您可以通过声明您的 Tweet 对象来禁用名称间距;
@objc(Tweet) class Tweet: NSObject, NSSecureCoding { ... }
@objc(name)
中的 name 通常作为在 objc 与 Swift 中呈现不同名称的一种方式呈现,但它也具有以下效果禁用 Swift 的名称间距。
来自Using Swift with Cocoa and Objective-C
When you use the @objc(name) attribute on a Swift class, the class is made available in Objective-C without any namespacing. As a result, this attribute can also be useful when migrating an archivable Objective-C class to Swift. Because archived objects store the name of their class in the archive, you should use the @objc(name) attribute to specify the same name as your Objective-C class so that older archives can be unarchived by your new Swift class.
另一种选择是将您的自定义对象移至框架。该框架目标随后成为命名空间,XPC 和 App 将在框架中引用相同的 namespace/class。
我正在尝试在 withReply
签名上使用我自己的类型编写 XPC 服务。 type/class 拥有主应用程序和 XPC 服务的 Xcode 的“目标成员资格”。但是,即使在 withReply
签名中使用了相同的 class,但我在调试输出中得到了 incompatible reply block signature
,但是 Xcode 目标不同,我将在下面解释。
注意:这是在 Swift 中完成的,使用 this project 让我开始。除了那里他们使用 NSData
而不是自定义类型。
详情
为了这个问题的目的,我将使用下面的例子
- 自定义class -
Tweet
- 这个class符合NSSecureCoding
协议所以可以在主app和XPC服务之间传递 - XPC 协议 -
TweetTransfer
需要一种方法func take(_ count: Int, withReply: ((Tweet) -> Void))
然后是所有常用的 XPC 样板文件,我在其中导出符合 TweetTransfer
的对象。 XPC 服务似乎已启动,但随后在它与主应用程序之间传输失败并显示
XPCWorker[11069:402719] <NSXPCConnection: 0x61800010e220> connection from pid 11066 received an undecodable message
下面是完整的消息[1],但“wire”和“local”之间的唯一区别是参数一是
- 电线 -
_TtC17MainApp5Tweet
- 本地 -
_TtC23XPCWorker5Tweet
其中 Xcode 目标不同。这足以把它扔掉吗?那么如何在应用程序和它的 XPC 服务之间共享代码?
[1] 完整错误文本
<NSXPCConnection: 0x61800010e220> connection from pid 11066 received an undecodable message (incompatible reply block signature (wire: <NSMethodSignature: 0x618000074ec0>
number of arguments = 2
frame size = 224
is special struct return? NO
return value: -------- -------- -------- --------
type encoding (v) 'v'
flags {}
modifiers {}
frame {offset = 0, offset adjust = 0, size = 0, size adjust = 0}
memory {offset = 0, size = 0}
argument 0: -------- -------- -------- --------
type encoding (@) '@?'
flags {isObject, isBlock}
modifiers {}
frame {offset = 0, offset adjust = 0, size = 8, size adjust = 0}
memory {offset = 0, size = 8}
argument 1: -------- -------- -------- --------
type encoding (@) '@"_TtC17MainApp5Tweet"'
flags {isObject}
modifiers {}
frame {offset = 8, offset adjust = 0, size = 8, size adjust = 0}
memory {offset = 0, size = 8}
class '_TtC17MainApp5Tweet'
vs local: <NSMethodSignature: 0x610000074740>
number of arguments = 2
frame size = 224
is special struct return? NO
return value: -------- -------- -------- --------
type encoding (v) 'v'
flags {}
modifiers {}
frame {offset = 0, offset adjust = 0, size = 0, size adjust = 0}
memory {offset = 0, size = 0}
argument 0: -------- -------- -------- --------
type encoding (@) '@?'
flags {isObject, isBlock}
modifiers {}
frame {offset = 0, offset adjust = 0, size = 8, size adjust = 0}
memory {offset = 0, size = 8}
argument 1: -------- -------- -------- --------
type encoding (@) '@"_TtC23XPCWorker5Tweet"'
flags {isObject}
modifiers {}
frame {offset = 8, offset adjust = 0, size = 8, size adjust = 0}
memory {offset = 0, size = 8}
class '_TtC23XPCWorker5Tweet'
)
更新
关于协议、remoteObjectProxy 连接和 Tweet 对象的更多信息。这是用于 XPC 调用的协议:
@objc(TweetTransfer)
protocol TweetTransfer {
func take(_ count: Int, withReply: replyType)
}
typealias replyType = ((Tweet) -> Void)
为了方便起见,我使用了类型别名。然后 Tweet
对象非常简单,仅用于测试(尽管支持 NSSecureCoding 有点复杂):
final class Tweet: NSObject, NSSecureCoding {
var name: String
var text: String
static var supportsSecureCoding = true
init(name: String, text: String) {
self.name = name
self.text = text
}
init?(coder aDecoder: NSCoder) {
guard let name = aDecoder.decodeObject(forKey: "name") as? String else {
fatalError("Could not deserialise name!")
}
guard let text = aDecoder.decodeObject(forKey: "text") as? String else {
fatalError("Could not deseralise text!")
}
self.name = name
self.text = text
}
func encode(with aCoder: NSCoder) {
aCoder.encode(name, forKey: "name")
aCoder.encode(text, forKey: "text")
}
}
最后是我们调用 remoteObjectProxy 的点
guard let loader = workerConnection.remoteObjectProxyWithErrorHandler(handler) as? TweetTransfer else {
fatalError("Could not map worker to TweetTransfer protocol!")
}
var tweets = [Tweet]()
loader.take(1) { tweet in
tweets.append(tweet)
}
The full message is below but the only difference between the “wire” and “local” is that argument one is
- wire -
_TtC17MainApp5Tweet
- local -
_TtC23XPCWorker5Tweet
Where the Xcode target is different. Is that enough to throw it off? How then do I share code between an app and it's XPC service?
这确实足以摆脱它。 Swift 的命名空间使归档对象显示为不同的 class。您可以通过声明您的 Tweet 对象来禁用名称间距;
@objc(Tweet) class Tweet: NSObject, NSSecureCoding { ... }
@objc(name)
中的 name 通常作为在 objc 与 Swift 中呈现不同名称的一种方式呈现,但它也具有以下效果禁用 Swift 的名称间距。
来自Using Swift with Cocoa and Objective-C
When you use the @objc(name) attribute on a Swift class, the class is made available in Objective-C without any namespacing. As a result, this attribute can also be useful when migrating an archivable Objective-C class to Swift. Because archived objects store the name of their class in the archive, you should use the @objc(name) attribute to specify the same name as your Objective-C class so that older archives can be unarchived by your new Swift class.
另一种选择是将您的自定义对象移至框架。该框架目标随后成为命名空间,XPC 和 App 将在框架中引用相同的 namespace/class。