【问题标题】:EXC_BAD_ACCESS using self.performSelectorEXC_BAD_ACCESS 使用 self.performSelector
【发布时间】:2016-07-19 13:48:24
【问题描述】:

这是一个简单的学术而非真正的代码。

我想使用 performSelector 函数运行打印方法。但是如果我在操场上运行这段代码,就会抛出异常:

EXC_BAD_ACCESS(代码=EXC_I386_GPFLT)。

代码:

//: Playground - noun: a place where people can play

import UIKit

@objc(Foo)
class Foo: NSObject {

    func timer() {
        self.performSelector( #selector(Foo.print))
    }

    @objc func print() {
        NSLog("print")
    }
}

let instance = Foo()
instance.timer()     // <-- EXC_BAD_ACCESS (code=EXC_I386_GPFLT)

问题出在哪里?

【问题讨论】:

  • 奇怪,它在 Playground 中崩溃,但在项目中有效。

标签: swift swift-playground performselector


【解决方案1】:

尝试将您的 Foo.print() 更改为如下内容:

    @objc func print() -> AnyObject? {
        NSLog("print")
        return nil
    }

我相信代码也可以在 Playground 中运行。

performSelectors 返回类型不是Void

- performSelector:

func performSelector(_ aSelector: Selector) -> Unmanaged<AnyObject>!

因此,Playground 尝试获取要显示的结果值。实际上没有返回。

【讨论】:

  • 我不这么认为,因为我们没有打印performSelector 的结果,我们打印的是timer 的结果,即Void
  • @JAL,这不是 Playground 对待每个表达式的方式。有任何事实支持你的假设吗?
  • 是的,将类更改为不继承自NSObject,并将timer() 更改为(self as AnyObject).performSelector(#selector(Foo.print))。没有崩溃,该行在 Playground 中的计算结果为 nil
  • @EricD,它与 Swift 生成什么样的机器代码有关。返回值设置为 CPU 寄存器,例如 x64 中的 RAX。对于Void 方法,Swift 不会生成代码来修改 RAX。因此,在某些情况下它可能是 nil,在其他情况下它可能包含一些垃圾,当用作指针时可能会导致崩溃。有人可以找到一个“幸运序列”,它不小心将 RAX 设置为 0 并保留该值,但不能保证。因此,在您的测试代码中,RAX 中设置的值(可能为 nil 或可能不是 nil)会被静默丢弃,因为未使用返回值。
  • @JAL,我上面的评论也是你的答案。
【解决方案2】:

这是一个不需要更改函数签名的解决方案:

class Foo {

    func timer() {
        (self as AnyObject).performSelector(#selector(Foo.print))
    }

    @objc func print() {
        NSLog("print")
    }
}

let instance = Foo()
instance.timer()

可能与 Objective-C API 桥接有关,仍在调查中……

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2015-02-17
    • 2011-10-31
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多