为什么'@objc'需要检查Swift中的协议一致性?

时间:2014-10-24 18:27:24

标签: objective-c oop swift

Apple的Swift Programming Language Guide表示

  

只有标记了协议,才能检查协议一致性   使用@objc属性

如果我不与Objective-C互操作,为什么这是必要的?

2 个答案:

答案 0 :(得分:8)

Swift 1.2的更新

  • 正如RyanM所指出的那样,语言更改消除了对@objc关键字的需求。

实际上,以下简单示例现在无法使用@objc关键字:

protocol Ap {
    func hello()
}

class A: Ap {
    func hello() {
        println("hello, world")     
    }
}

var a = A()
if (a as AnyObject) is Ap {
    a.hello()
} else {
    println("nope")
}

// hello, world

此外,现在联系只是这样:

protocol-conformance-1-2:
    /usr/lib/libSystem.B.dylib (compatibility version 1.0.0, current version 1213.0.0)
    /usr/lib/libobjc.A.dylib (compatibility version 1.0.0, current version 228.0.0)
    @rpath/libswiftCore.dylib (compatibility version 0.0.0, current version 0.0.0)

ORIGINAL:

让我们看一个例子。请注意,我还使用了额外的(varName as AnyObject)调用,否则编译器会抱怨'is' test is always true - 因为它确切地知道编译时类型是什么。

import Foundation

protocol Swifty {
    func s()
    //  protocol-conformance.swift:5:2: error: 'optional' can only be applied to members of an @objc protocol
    //          optional var a: Int { get }
    //          ^
    /*
    optional var a: Int { get }
    */
}

protocol SwiftyClass: class {
    func scl()
    //  protocol-conformance.swift:13:2: error: 'optional' can only be applied to members of an @objc protocol
    //          optional var a: Int { get }
    //          ^
    /*
    optional var a: Int { get }
    */
}

@objc protocol SwiftyConformance {
    func scon()
    optional var a: Int { get }
}

class SwiftyOnly: Swifty {
    func s() {
        println("s")
    }
}
class SwiftyClassOnly: SwiftyClass {
    func scl() {
        println("scl")
    }
}
class SwiftyConformanceOnly: SwiftyConformance {
    func scon() {
        println("scon")
    }
}
class SwiftyConformanceWithOptional: SwiftyConformance {
    func scon() {
        println("sconwo")
    }
    var a: Int {
        get { return 1; }
    }
}

println("swifty")
var swifty = SwiftyOnly()
//protocol-conformance.swift:49:26: error: cannot downcast from 'AnyObject' to non-@objc protocol type 'Swifty'
//if (swifty as AnyObject) is Swifty {
//   ~~~~~~~~~~~~~~~~~~~~~ ^  ~~~~~~
/*
if (swifty as AnyObject) is Swifty {
    println("swifty is Swifty")
}
*/
//  protocol-conformance.swift:47:34: error: cannot downcast from 'AnyObject' to non-@objc protocol type 'Swifty'
//  if let s = (swifty as AnyObject) as? Swifty {
//             ~~~~~~~~~~~~~~~~~~~~~ ^   ~~~~~~
/*
if let s = (swifty as AnyObject) as? Swifty {
    s.s()
}
*/
println("")

println("swiftyClass")
var swiftyClass = SwiftyClassOnly()
//protocol-conformance.swift:61:31: error: cannot downcast from 'AnyObject' to non-@objc protocol type 'SwiftyClass'
/*
if (swiftyClass as AnyObject) is SwiftyClass {
    println("swiftyClass is SwiftyClass")
}
*/
//protocol-conformance.swift:80:39: error: cannot downcast from 'AnyObject' to non-@objc protocol type 'SwiftyClass'
//if let s = (swiftyClass as AnyObject) as? SwiftyClass {
//           ~~~~~~~~~~~~~~~~~~~~~~~~~~ ^   ~~~~~~~~~~~
/*
if let s = (swiftyClass as AnyObject) as? SwiftyClass {
    s.scl()
}
*/
println("")

println("swiftyConformanceOnly")
var swiftyConformanceOnly = SwiftyConformanceOnly()
if (swiftyConformanceOnly as AnyObject) is SwiftyConformance {
    println("swiftyConformanceOnly is SwiftyConformance")
}
if let s  = (swiftyConformanceOnly as AnyObject) as? SwiftyConformance {
    s.scon()
    if let a = s.a? {
        println("a: \(a)")
    }
}
println("")

println("swiftyConformanceWithOptional")
var swiftyConformanceWithOptional = SwiftyConformanceWithOptional()
if (swiftyConformanceWithOptional as AnyObject) is SwiftyConformance {
    println("swiftyConformanceWithOptional is SwiftyConformance")
}
if let s  = (swiftyConformanceWithOptional as AnyObject) as? SwiftyConformance {
    s.scon()
    if let a = s.a? {
        println("a: \(a)")
    }
}
println("")

...和(在不取消破解代码测试用例的情况下),输出为:

swifty

swiftyClass

swiftyConformanceOnly
swiftyConformanceOnly is SwiftyConformance
scon

swiftyConformanceWithOptional
swiftyConformanceWithOptional is SwiftyConformance
sconwo
a: 1

所以,简单的答案就像文档所说的那样:protocol conformance testing(和optionals)需要@objc

在Swift中,objc只是一个声明属性,通常表示编译器的提示或修改代码的生成方式。

但是更长的答案就引出了一个问题:"但为什么语言或运行时以这种方式编写?",而且这很难解决;我的猜测是@objc属性将生成真正的Objective-C对象/协议引用,并且一致性测试只是在运行时使用它来实现。

您可以在上面的示例中对/ *和* / one之间的代码进行注释,并查看编译器抱怨的时间和位置。

更新:编译器和链接器更新

如果我们编译以上内容:xcrun swiftc -sdk $(xcrun --show-sdk-path --sdk macosx) protocol-conformance.swift并检查它与otool -L protocol-conformance的链接,我们会看到

协议一致性:

/usr/lib/libSystem.B.dylib (compatibility version 1.0.0, current version 1213.0.0)
/usr/lib/libobjc.A.dylib (compatibility version 1.0.0, current version 228.0.0)
@rpath/libswiftCore.dylib (compatibility version 0.0.0, current version 0.0.0)
@rpath/libswiftCoreGraphics.dylib (compatibility version 0.0.0, current version 0.0.0)
@rpath/libswiftDarwin.dylib (compatibility version 0.0.0, current version 0.0.0)
@rpath/libswiftDispatch.dylib (compatibility version 0.0.0, current version 0.0.0)
@rpath/libswiftFoundation.dylib (compatibility version 0.0.0, current version 0.0.0)
@rpath/libswiftObjectiveC.dylib (compatibility version 0.0.0, current version 0.0.0)
@rpath/libswiftSecurity.dylib (compatibility version 0.0.0, current version 0.0.0)

...所以我认为做这些一致性测试更公平,你需要与Objective-C运行时进行交互,但我不一定会说你需要互动使用Objective-C(对我来说,这意味着你需要写一些objc代码)。

查看一个非常简单的协议使用程序:

protocol Ap {
    func hello()
}
class A: Ap {
    func hello() {
        println("hello, world")     
    }
}
var a = A()
a.hello()

//$ otool -L hello-world
//hello-world:
//  /usr/lib/libSystem.B.dylib (compatibility version 1.0.0, current version 1213.0.0)
//  /usr/lib/libobjc.A.dylib (compatibility version 1.0.0, current version 228.0.0)
//  @rpath/libswiftCore.dylib (compatibility version 0.0.0, current version 0.0.0)

...但是如果只是尝试实现一个没有其他更改的@objc协议:

@objc protocol Ap {
    func hello()
}
class A: Ap {
    func hello() {
        println("hello, world")     
    }
}
var a = A()
a.hello()

//$ xcrun swiftc -sdk $(xcrun --show-sdk-path --sdk macosx) hello-world.swift 
//hello-world.swift:1:2: error: @objc attribute used without importing module 'Foundation'
//@objc protocol Ap {
// ^~~~

...然后如果我们导入基金会:

import Foundation

@objc protocol Ap {
    func hello()
}
class A: Ap {
    func hello() {
        println("hello, world")     
    }
}
var a = A()
a.hello()

//$ otool -L hello-world
//hello-world:
//  /usr/lib/libSystem.B.dylib (compatibility version 1.0.0, current version 1213.0.0)
//  /usr/lib/libobjc.A.dylib (compatibility version 1.0.0, current version 228.0.0)
//  @rpath/libswiftCore.dylib (compatibility version 0.0.0, current version 0.0.0)
//  @rpath/libswiftCoreGraphics.dylib (compatibility version 0.0.0, current version 0.0.0)
//  @rpath/libswiftDarwin.dylib (compatibility version 0.0.0, current version 0.0.0)
//  @rpath/libswiftDispatch.dylib (compatibility version 0.0.0, current version 0.0.0)
//  @rpath/libswiftFoundation.dylib (compatibility version 0.0.0, current version 0.0.0)
//  @rpath/libswiftObjectiveC.dylib (compatibility version 0.0.0, current version 0.0.0)
//  @rpath/libswiftSecurity.dylib (compatibility version 0.0.0, current version 0.0.0)

我甚至会说Swift标准库和运行时绝对使用Objective-C运行时,并期望访问defacto核心Objective-C框架,例如Foundation for core features。

答案 1 :(得分:1)

自从接受了答案后,斯威夫特已经发展了,现在的答案是:

不是。

来自The Swift Programming Language

  

您可以使用类型转换中描述的is和as运算符来检查协议一致性,并转换为特定协议。检查和转换为协议的语法与检查和转换为类型的语法完全相同...

protocol PersonBasedView {

    var person: Person? {get set}
}

class EmployeeView : UIView, PersonBasedView {

    var person: Person?
}

// Elsewhere

var view = ...

if view is PersonBasedView { ... }

// or
if let personView = view as? PersonBasedView { ... }