我是一个快速的初学者,所以要温柔......
我无法将功能指定为参数。
我已经定义了这个结构:
struct dispatchItem {
let description: String
let f: ()->Void
init(description: String, f: @escaping ()->()) {
self.description = description
self.f = f
}
}
我在一个名为MasterDispatchController
的类中使用它,如下所示:
class MasterDispatchController: UITableViewController {
let dispatchItems = [
dispatchItem(description: "Static Table", f: testStaticTable),
dispatchItem(description: "Editable Table", f: testEditableTable)
]
func testEditableTable() {
//some code
}
func testStaticTable() {
//some code
}
等
然后我在我的代码中有一个表格视图,它会发送给所点击的任何一个函数(不仅仅是我在上面的代码中展示的那两个,但这并不重要),如此
override func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
dispatchItems[indexPath.row].f()
}
所以...编译器对此并不满意。它说当我定义dispatchItems let语句时:
无法转换类型'(MasterDispatchController)的值 - > () - > ()'预期参数类型'() - > ()'
我想......好吧......我不确定我是否完全理解这一点,但似乎编译器想要知道回调函数将来自哪个类。我明白为什么它可能需要它。所以我有点盲目地遵循编译器给我的模式,并将我的结构改为:
struct dispatchItem {
let description: String
let f: (MasterDispatchController)->()->Void
init(description: String, f: @escaping (MasterDispatchController)->()->()) {
self.description = description
self.f = f
}
}
编译器非常满意,但现在当我尝试用dispatchItems[indexPath.row].f()
调用该函数时,它说:
在通话中缺少参数#1
该功能没有参数,所以我感到困惑......
我想也许是在向我询问有问题的物体的实例,这有点意义......那就是"自我"在我的例子中,所以我尝试了dispatchItems[indexPath.row].f(self)
,但后来我收到了一个错误:
表达式解析为未使用的函数
所以我有点卡住了。
对不起,如果这是一个愚蠢的问题。谢谢你的帮助。
答案 0 :(得分:2)
问题是您在 {{1}之前尝试在实例属性的初始化中引用实例方法testStaticTable
和testEditableTable
完全初始化。因此,编译器不能将这些方法部分应用self
作为隐式参数,而只能self
- 类型为(MasterDispatchController) -> () -> ()
。
可能有人试图将dispatchItems
属性标记为offer you the curried versions,以便属性初始化程序在属性的首次访问上运行,而{{1} 已完全初始化。
self
(请注意,我将您的结构重命名为符合Swift命名约定)
现在编译,因为你现在可以引用方法的部分应用版本(即类型class MasterDispatchController : UITableViewController {
lazy private(set) var dispatchItems: [DispatchItem] = [
DispatchItem(description: "Static Table", f: self.testStaticTable),
DispatchItem(description: "Editable Table", f: self.testEditableTable)
]
// ...
}
),并可以将它们称为:
() -> Void
然而,您现在有一个保留周期,因为您在dispatchItems[indexPath.row].f()
上存储了强烈捕获self
的闭包。这是因为当用作值时,self
会解析为强烈捕获self.someInstanceMethod
的部分应用的闭包。
您已经接近实现的一个解决方案是使用方法的curried版本 - 不强烈捕获self
,但是而是必须应用给定的实例进行操作。
self
这些函数现在将struct DispatchItem<Target> {
let description: String
let f: (Target) -> () -> Void
init(description: String, f: @escaping (Target) -> () -> Void) {
self.description = description
self.f = f
}
}
class MasterDispatchController : UITableViewController {
let dispatchItems = [
DispatchItem(description: "Static Table", f: testStaticTable),
DispatchItem(description: "Editable Table", f: testEditableTable)
]
override func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
dispatchItems[indexPath.row].f(self)()
}
func testEditableTable() {}
func testStaticTable() {}
}
的给定实例作为参数,并返回正确的实例方法以调用该给定实例。因此,您需要首先使用MasterDispatchController
应用它们,然后说self
以获取要调用的实例方法,然后使用f(self)
调用结果函数。
虽然使用()
持续应用这些功能可能不方便(或者您甚至无法访问self
)。更通用的解决方案是将self
作为self
属性存储在weak
,以及咖喱功能 - 然后您可以按需应用它[&#39]:
DispatchItem
这确保您没有保留周期,因为struct DispatchItem<Target : AnyObject> {
let description: String
private let _action: (Target) -> () -> Void
weak var target: Target?
init(description: String, target: Target, action: @escaping (Target) -> () -> Void) {
self.description = description
self._action = action
}
func action() {
// if we still have a reference to the target (it hasn't been deallocated),
// get the reference, and pass it into _action, giving us the instance
// method to call, which we then do with ().
if let target = target {
_action(target)()
}
}
}
class MasterDispatchController : UITableViewController {
// note that we've made the property lazy again so we can access 'self' when
// the property is first accessed, after it has been fully initialised.
lazy private(set) var dispatchItems: [DispatchItem<MasterDispatchController>] = [
DispatchItem(description: "Static Table", target: self, action: testStaticTable),
DispatchItem(description: "Editable Table", target: self, action: testEditableTable)
]
override func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
dispatchItems[indexPath.row].action()
}
func testEditableTable() {}
func testStaticTable() {}
}
没有对DispatchItem
的强引用。
当然,您可以对self
使用unowned
个引用,例如显示lazy
。但是,如果您保证您的self
个实例不会超过DispatchItem
(您希望self
成为{{}},那么您应该这样做{1}}一个属性。)
答案 1 :(得分:1)
这里的问题是Swift在不同的方面对待类方法和函数。类方法获得一个隐藏的self
参数(类似于它在Python中的工作方式),它允许他们知道调用它们的类实例。这就是为什么即使您将testEditableCode
声明为() -> ()
,实际函数的类型为(MasterDispatchController) -> () -> ()
。它需要知道它被调用的对象实例。
正确的做法是创建一个调用正确方法的闭包,例如:
class MasterDispatchController: UITableViewController {
let dispatchItems = [
dispatchItem(description: "Static Table", f: {() in
self.testStaticTable()
}),
dispatchItem(description: "Editable Table", f: {() in
self.testEditableTable()
})
]
func testEditableTable() {
//some code
}
func testStaticTable() {
//some code
}
如果您熟悉JavaScript,{() in ...code...}
符号与Python中的function() { ...code... }
或lambda: ...code...
相同。
答案 2 :(得分:0)
这是达到你想要的方式。
使用您想要的参数实现协议:
protocol Testable {
func perfromAction()
var description: String { get set }
weak var viewController: YourViewController? { get set } //lets assume it is fine for testing
}
像这样访问你的UIViewController
是不对的,但现在还可以。您可以访问标签,segues等。
为每个所需的测试创建Class
:
class TestA: Testable {
var description: String
weak var viewController: YourViewController?
func perfromAction() {
print(description) //do something
viewController?.testCallback(description: description) //accessing your UIViewController
}
init(viewController: YourViewController, description: String) {
self.viewController = viewController
self.description = description
}
}
class TestB: Testable {
var description: String
weak var viewController: YourViewController?
func perfromAction() {
print(description) //do something
viewController?.testCallback(description: description) //accessing your UIViewController
}
init(viewController: YourViewController, description: String) {
self.viewController = viewController
self.description = description
}
}
您可以为每个Class
添加一些自定义参数,但需要协议中的3个。
然后你的UIViewController
就像
class YourViewController: UIViewController {
var arrayOfTest: [Testable] = []
override func viewDidLoad() {
super.viewDidLoad()
arrayOfTest.append(TestA(viewController: self, description: "testA"))
arrayOfTest.append(TestB(viewController: self, description: "testB"))
arrayOfTest[0].perfromAction()
arrayOfTest[1].perfromAction()
}
func testCallback(description: String) {
print("I am called from \(description)")
}
}