2016-04-25 87 views
9

這個問題是關於這一問題的Check for object type fails with "is not a type" error啓發mz2's answerAnyObject如何符合NSObjectProtocol?

考慮空雨燕類:

class MyClass { } 

嘗試調用這個類的一個實例任何NSObjectProtocol方法將導致編譯時錯誤:

let obj = MyClass() 
obj.isKindOfClass(MyClass.self) // Error: Value of type 'MyClass' has no member 'isKindOfClass' 

但是,如果我投實例爲AnyObject,我的對象現在符合NSObjectProtocol,我可以調用由協議定義的實例方法:

let obj: AnyObject = MyClass() 
obj.isKindOfClass(MyClass.self) // true 
obj.conformsToProtocol(NSObjectProtocol) // true 
obj.isKindOfClass(NSObject.self) // false 

我的對象不從NSObject繼承,但仍符合NSObjectProtocolAnyObject如何符合NSObjectProtocol

回答

5

在可可/ Objective-C的世界,AnyObject是id。將此對象投射到AnyObject後,您可以發送任何已知的Objective-C消息,例如isKindOfClassconformsToProtocol。現在,當你說isKindOfClassconformsToProtocol時,你再也沒有進入Swift世界;你正在用Objective-C與Cocoa交談。所以想想Objective-C如何看待這個對象。 Objective-C世界中的所有類都從一些基類中下來;像MyClass這樣的毫無根據的類是不可能的。 Objective-C世界中的每個基類都符合NSObject協議(Swift調用NSObjectProtocol);這就是基礎類的成就(或下降)!因此,要讓它進入Objective-C的世界裏,斯威夫特呈現MyClass的是從一個特殊的橋基類SwiftObject降這確實符合NSObjectProtocol(你可以在這裏看到:https://github.com/apple/swift/blob/master/stdlib/public/runtime/SwiftObject.mm)。

+0

但是'AnyObject'被定義爲'@objc public protocol AnyObject {}'這是否意味着所有'@ objc'協議符合'NSObjectProtocol'? 「Any」呢? – JAL

+0

這並不意味着。我在說我在說什麼,而不是其他的東西。 – matt

+0

你的意思是「像MyClass這樣的毫無根據的類是不可能的。」那麼你是說'MyClass'繼承自'SwiftObject'嗎?爲什麼只向'AnyObject'強制轉換符合'NSObjectProtocol'? – JAL

5

如果我理解正確此基礎上matt's答案,這個工程時,斯威夫特/ Objective-C的互操作可用,因爲其實雨燕類類型最終從SwiftObject,當Objective-C的互操作編譯在繼承,實際上涉及到一個Objective-C類(SwiftObject在SwiftObject.mm中實現,當使用Objective-C互操作時,它被編譯爲Objective-C++)。所以,將一個Swift類類型的對象作爲AnyObject類型「泄漏」該信息。

在從Swift source code執行一些相關的位偷看,文件swift/stdlib/public/runtime/SwiftObject.mm

#if SWIFT_OBJC_INTEROP 

// … 

@interface SwiftObject<NSObject> { 
    SwiftObject_s header; 
} 

// … 

@implementation SwiftObject 

// … 

- (BOOL)isKindOfClass:(Class)someClass { 
    for (auto isa = _swift_getClassOfAllocated(self); isa != nullptr; 
     isa = _swift_getSuperclass(isa)) 
    if (isa == (const ClassMetadata*) someClass) 
     return YES; 

    return NO; 
} 

// … 

// #endif 

正如這在Linux中(其中有沒有可用的Objective-C運行時爲雨燕的部分預測,隨着斯威夫特3運行時間&基金會實施,據我理解)從這個問題,並the earlier question & answer啓發這個問題失敗,出現以下錯誤編譯器錯誤示例代碼:

ERROR […] value of type 'AnyObject' has no member 'isKindOfClass' 
1

除了matt's答案,我認爲這是正確的:

Is isKindOfClass in this case actually sent as a dynamically dispatched message, even though the class itself is not an Objective-C visible type and does not use messaging based dispatch for its own methods?

沒有,isKindOfClass作爲一個動態分派方法因爲類本身一個Objective-C出現的類型和確實發使用基於消息的調度來處理它自己的方法。

它這樣做是因爲@objc@objc public protocol AnyObject {}

XCode中的AnyObject如果CMD-單擊您將在生成的頭

/// When used as a concrete type, all known `@objc` methods and 
/// properties are available, as implicitly-unwrapped-optional methods 
/// and properties respectively, on each instance of `AnyObject`. 

而在文檔在https://developer.apple.com/library/ios/documentation/Swift/Conceptual/BuildingCocoaApps/MixandMatch.html

看到這

To be accessible and usable in Objective-C, a Swift class must be a descendant of an Objective-C class or it must be marked @objc.

(我強調)

採用使用@objc標記的協議意味着您的類是@objc類,並且在上面的答案中是通過mz2指出的互操作機制橋接的ObjC。

+0

沒錯,但給出的所有記錄信息的令人驚訝的事情仍然是,這適用於沒有標記@objc並且沒有從Objective-C類繼承(以文檔方式)的Swift類。事實上,它們總是不總是(在沒有Objective-C互操作的情況下,AnyObject也存在於該語言中)。 – mz2

+0

任何時候當你使用AnyObject的時候你都在調用ObjC interop,它就像上面引用的那樣在文檔中說得很對。 –

+0

當然,當沒有ObjC interop甚至編譯到你的Swift副本中時,也有'AnyObject'。例如'class A {}; print(A()as AnyObject)'在Linux上的Swift 3中工作得很好。我想我們只是在同一點:它在ObjC可互操作平臺上這樣做,因爲未公開的基類與ObjC互操作符合NSObjectProtocol兼容的Objective-C類,並且效果(方法和屬性可以隱式正如你指出的那樣,記錄在案的選項)。 – mz2

2

在已經很好的答案中添加一些附加信息。

我創建三個方案,看着所產生的組件從各:

obj1.swift

import Foundation 
class MyClass { } 
let obj = MyClass() 

obj2.swift

import Foundation 
class MyClass { } 
let obj: AnyObject = MyClass() 

obj3.swift

import Foundation 
class MyClass { } 
let obj: AnyObject = MyClass() 
obj.isKindOfClass(MyClass.self) 

obj1和obj2之間的差異是微不足道的。涉及的對象類型的任何指令具有不同的值:

movq %rax, __Tv3obj3objCS_7MyClass(%rip) 

# ... 

globl __Tv3obj3objCS_7MyClass   .globl __Tv3obj3objPs9AnyObject_ 
.zerofill __DATA,__common,__Tv3obj3objCS_7MyClass,8,3 

# ... 

.no_dead_strip __Tv3obj3objCS_7MyClass 

VS

movq %rax, __Tv3obj3objPs9AnyObject_(%rip) 

# ... 

.globl __Tv3obj3objPs9AnyObject_ 
.zerofill __DATA,__common,__Tv3obj3objPs9AnyObject_,8,3 

# ... 

.no_dead_strip __Tv3obj3objPs9AnyObject_ 

完全兩種here

這對我來說很有意思。如果兩個文件之間的唯一區別是對象類型的名稱,爲什麼聲明爲AnyObject的對象可以執行Objective-C選擇器?

OBJ 3顯示了isKindOfClass:選擇是如何觸發:

OBJ 2和OBJ 3 here之間
LBB0_2: 
    # ... 
    movq __Tv3obj3objPs9AnyObject_(%rip), %rax 
    movq %rax, -32(%rbp) 
    callq _swift_getObjectType 
    movq %rax, -8(%rbp) 
    movq -32(%rbp), %rdi 
    callq _swift_unknownRetain 
    movq -24(%rbp), %rax 
    cmpq $14, (%rax) 
    movq %rax, -40(%rbp) 
    jne LBB0_4 
    movq -24(%rbp), %rax 
    movq 8(%rax), %rcx 
    movq %rcx, -40(%rbp) 
LBB0_4: 
    movq -40(%rbp), %rax 
    movq "L_selector(isKindOfClass:)"(%rip), %rsi 
    movq -32(%rbp), %rcx 
    movq %rcx, %rdi 
    movq %rax, %rdx 
    callq _objc_msgSend 
    movzbl %al, %edi 
    callq __TF10ObjectiveC22_convertObjCBoolToBoolFVS_8ObjCBoolSb 
    movq -32(%rbp), %rdi 
    movb %al, -41(%rbp) 
    callq _swift_unknownRelease 
    xorl %eax, %eax 
    addq $48, %rsp 

# ... 

LBB6_3: 
    .section __TEXT,__objc_methname,cstring_literals 
"L_selector_data(isKindOfClass:)": 
    .asciz "isKindOfClass:" 

    .section __DATA,__objc_selrefs,literal_pointers,no_dead_strip 
    .align 3 
"L_selector(isKindOfClass:)": 
    .quad "L_selector_data(isKindOfClass:)" 

差異。

isKindOfClass作爲動態調度方法發送,如_objc_msgSend所示。這兩個對象都暴露給Objective-C,如SwiftObject.quad _OBJC_METACLASS_$_SwiftObject),聲明對象的類型爲AnyObject完成了橋接到NSObjectProtocol

+0

我現在開始想知道,作爲一個完全獨立的問題,基於這個問題,它是如何導入基礎結果的:一個Swift類類型的對象轉換成AnyObject會拋出一個編譯器錯誤,如果你不導入Foundation'。在基於是否使用基礎的情況下,Objective-C interop是否在所有調製中使用? – mz2

相關問題