Swift-无法调用选择器..为什么?

时间:2019-04-01 10:02:38

标签: ios swift

好吧,这对我来说很奇怪,有人可以向我解释为什么handleDismiss只能被称为一种方式吗?

请考虑以下内容:

import UIKit
class MenuLanucher: NSObject, UICollectionViewDataSource, UICollectionViewDelegate, UICollectionViewDelegateFlowLayout
{

    //[...] stuff

    let menuItems: [MenuCellSetting] = {
       return [
           MenuCellSetting(name: "Exit Application", imageName: "hamburger", ontap: {
               print("it is exit")
               MenuLanucher.handleDismiss() //<-- 2. this is illegal: 'instance member 'handleDismiss' cannot be used on type 'MenuLanucher'; did you mean to use a value of this type instead?'
           }),
            MenuCellSetting(name: "Create", imageName: "gear", ontap: {
               print("it is job")
               self?.HandleDismiss() //<-- 2. illegal : 'Cannot use optional chaining on non-optional value of type '(MenuLanucher) -> () -> (MenuLanucher)''
           }),
           MenuCellSetting(name: "Cancel", imageName: "gear", ontap: {
               print("it is nothing")
               perform(#selector(MenuLanucher.handleDismiss)) //<-- 3. crashes on run time 'unrecognized selector sent to class'
           })
        ]
      }()

     //[...] yet

     func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath) {
         menuItems[indexPath.item].ontap()
         handleDismiss() //<--1. works
     }

      @objc func handleDismiss(){
         print("dismiss works")
     }
}

class MenuCellSetting: NSObject {
     let name: String
    let imageName: String
    let ontap: ()->Void
    init(name: String, imageName: String, ontap: @escaping ()->Void){
         self.name = name
         self.imageName = imageName
         self.ontap = ontap
     }
 }

在此示例中

  1. 失败:在运行时说“无法识别的选择器发送给类”
  2. 失败:在编译时说'实例成员'handleDismiss'不能用于'MenuLanucher'类型;您是不是要使用这种类型的值?'
  3. 有效

我的问题是:为什么有区别?发生了什么事?

编辑:self.?handleDismiss()也失败(参见图片)

enter image description here

3 个答案:

答案 0 :(得分:2)

替换

let menuItems: [MenuCellSetting] = {
  ...
}()

使用

lazy var menuItems: [MenuCellSetting] = {
  ...
}()

问题在于实例常量在self甚至还没有可用之前就已初始化,因此闭包中的self实际上意味着与您期望的不同。 lazy var是在首次调用时分配的,即在self初始化之后,它们可以安全地访问self

实际上,为防止内存泄漏,您还必须使用[weak self]

MenuCellSetting(name: "Create", imageName: "gear", ontap: { [weak self] in
    self?.HandleDismiss()
})

答案 1 :(得分:-1)

由于“ handleDismiss”不是类方法,因此无法通过类名称访问它。我们需要首先创建一个对象,然后可以通过点运算符访问它。请在类名前面添加括号以访问“ handleDismiss”方法。

此外,由于“ handleDismiss”方法位于同一类中,因此您不必提供类名。您可以直接调用方法名称。您可能必须使用self,因为调用处于关闭状态。

let menuItems: [MenuCellSetting] = {
    return [
        MenuCellSetting(name: "Exit Application", imageName: "hamburger", ontap: {
            print("it is exit")
            self.handleDismiss() //<-- 2. this is illegal: 'instance member 'handleDismiss' cannot be used on type 'MenuLanucher'; did you mean to use a value of this type instead?'
        }),
         MenuCellSetting(name: "Create", imageName: "gear", ontap: {
            print("it is job")
        }),
        MenuCellSetting(name: "Cancel", imageName: "gear", ontap: {
            print("it is nothing")
            perform(#selector(self.handleDismiss)) //<-- 3. crashes on run time 'unrecognized selector sent to class'
        })
    ]
}()

答案 2 :(得分:-1)

首先handleDismiss不是类函数,因此您需要有一个类对象才能调用handleDismiss()

第二,您不应该只是在menuItems中创建一个新对象,否则将在此新对象上调用此方法,并且不会对当前对象产生任何影响。

示例代码

struct MenuCellSetting {
    var name: String
    var imageName: String
    var ontap: () -> ()

    init(name: String, imageName: String, ontap: @escaping (() -> ())) {
        self.name = name
        self.imageName = imageName
        self.ontap = ontap
    }
}
class MenuLauncher {
    let menuItems: [MenuCellSetting] = {
        return [
            MenuCellSetting(name: "Exit Application", imageName: "hamburger", ontap: { [weak self] in // Want to make sure that you only capture weak reference, otherwise it will create a memory-leak due to cyclic reference
                print("it is exit")
                self?.handleDismiss()
            }),
            MenuCellSetting(name: "Create", imageName: "gear", ontap: {
                print("it is job")
            }),
            MenuCellSetting(name: "Cancel", imageName: "gear", ontap: { [weak self] in
                print("it is nothing")
                self?.handleDismiss()
            })
        ]
    }()

    func handleDismiss() {
        print("Dismissing")
    }
}