protocolの@class_protocolと@objcの違いを調べてみた


@class_protocolと@objcの違いがいまいち理解出来なかったので調べてみました。

The Swift Programming Languageには、@class_protocolと@objcはクラス以外には採用できないとあるので確認してみます。


> class_protocol
>Apply this attribute to a protocol to indicate that the protocol can be adopted by class types only.

>If you apply the objc attribute to a protocol, the class_protocol attribute is implicitly applied to that protocol; there’s no need to mark the protocol with the class_protocol attribute explicitly.

ref:[The Swift Programming Language - Attributes](https://developer.apple.com/library/prerelease/ios/documentation/Swift/Conceptual/Swift_Programming_Language/Attributes.html)

# 実装から違いを確認してみる
## protocol

通常のprotocolを利用してClass、Struct、Enumに採用してみます。
Class、Struct、Enumに問題なくprotocolが採用できます。

```swift_protocol.swift
import Foundation

protocol Prot {
    func protFunc()
}

// classにprotocolを採用
class Impl : Prot {
    func protFunc() {
        println("Impl:Prot->protFunc protFunc")
    }
}

// classにprotocolを採用
class Impl2 : Prot {
    func protFunc() {
        println("Impl2:Prot->protFunc protFunc")
    }
}

// structにprotocolを採用
struct SomeStructure: Prot {
    func protFunc() {
        println("SomeStructure:Prot->protFunc protFunc")
    }
}

// enumにprotocolを採用
enum SomeEnum: Prot {
    case Off, On
    func protFunc() {
        println("SomeEnum:Prot->protFunc protFunc")
    }
}

// それぞれのprotocolを指定した変数を代入するための構造体を定義
struct StructWithRef {
	// 変数をweakで宣言するとシンタックスエラーとなる
    // 'weak' cannot be applied to non-class type 'cProto1'
    var cProto1 : Prot?
    var cProto2 : Prot?
    var strProt  : Prot?
    var enumProt : Prot?
}

func testFunc() {
    var cimpl1 = Impl()
    var cimpl2 = Impl2()
    var simpl = SomeStructure()
    var eimpl = SomeEnum.On
    var structWithRef = StructWithRef(cProto1: cimpl1, cProto2: cimpl2, strProt: simpl ,enumProt:eimpl)
    
	// Impl:Prot->protFunc protFunc　が出力される
    structWithRef.cProto1?.protFunc()
	// Impl2:Prot->protFunc protFunc　が出力される
    structWithRef.cProto2?.protFunc()
	// SomeStructure:Prot->protFunc protFunc　が出力される
    structWithRef.strProt?.protFunc()
	// SomeEnum:Prot->protFunc protFunc　が出力される
    structWithRef.enumProt?.protFunc()
    
}

```


## @class_protocol

@class_protocolをClass、Struct、Enumで試してみると、Class以外にはProtocolを採用できなとある通り Enum と Struct がシンタックスエラーになりました。
@class_protocolを採用した変数がweakで宣言出来てしまいますが、実行時に EXC_BAD_ACCESS が発生して実行時エラーになりました。

```class_protocol.swift
import Foundation

@class_protocol protocol ClaassProto {
    func classProtoFunc()
}

// 問題ない
class ClassImpl : ClaassProto {
    func classProtoFunc() {
        println("ClassImpl:ClaassProto->func classProtoFunc")
    }
}

class ClassImpl2 : ClaassProto {
    func classProtoFunc() {
        println("ClassImpl2:ClaassProto->func classProtoFunc")
    }
}

// Structはシンタックスエラーになる
// Non-class type 'SomeStructure' cannot conform to class protocol 'ClaassProto'
struct SomeStructure: ClaassProto {
    func classProtoFunc() {
        println("SomeStructure:ClaassProto->func classProtoFunc")
    }
}

// Enumもシンタックスエラーになる
// Non-class type 'SomeEnum' cannot conform to class protocol 'ClaassProto'
enum SomeEnum: ClaassProto {
    func classProtoFunc() {
        println("SomeEnum:ClaassProto->func classProtoFunc")
    }
}

struct StructWithRef {
    var classProto1 : ClaassProto?
	var classProto2 : ClaassProto?
	// なぜかweakで変数を定義できてしまう
    weak var wclassProto : ClaassProto?
}

func testFunc() {
    var cimpl1 = ClassImpl()
    var cimpl2 = ClassImpl2()
    var structWithRef = StructWithRef(classProto1: cimpl1, classProto2: cimpl2, wclassProto: cimpl1)
    
	// ClassImpl:ClaassProto->func classProtoFunc　が出力される
	structWithRef.classProto1?.classProtoFunc() 
	// ClassImpl2:ClaassProto->func classProtoFunc　が出力される
    structWithRef.classProto2?.classProtoFunc()
	// EXC_BAD_ACCESS になり実行時エラーになる
    structWithRef.wclassProto?.classProtoFunc()
}

```



## @objc

@class_protocolと同様に、Class以外にはProtocolを採用できなとある通り Enum と Struct がシンタックスエラーになりました。
@class_protocolと違い weak で定義した変数にプロトコルを採用が可能だし、実行時エラーも発生せずに実行が可能でした。

```objc_protocol.swift
import Foundation

@objc protocol ObjcProto {
    func objcProtoFunc()
}

// 問題ない
class ObjcImpl : ObjcProto {
    func objcProtoFunc() {
        println("ObjcImpl:ObjcProto->func objcProtoFunc")
    }
}

class ObjcImpl2 : ObjcProto {
    func objcProtoFunc() {
        println("ObjcImpl:ObjcProto->func objcProtoFunc")
    }
}

// Structはシンタックスエラーになる
// Non-class type 'SomeStructure' cannot conform to class protocol 'ObjcProto'
struct SomeStructure: ObjcProto {
    func objcProtoFunc() {
        println("SomeStructure:ObjcProto->func objcProtoFunc")
    }
}

// Enumもシンタックスエラーになる
// Non-class type 'SomeEnum' cannot conform to class protocol 'ObjcProto'
enum SomeEnum: ObjcProto {
    func objcProtoFunc() {
        println("SomeEnum:ObjcProto->func objcProtoFunc")
    }
}

struct StructWithRef {
    var objcProto1 : ObjcProto?
    var objcProto2 : ObjcProto?
    // weakで変数を定義可能
    weak var wobjcProto : ObjcProto?
}

func testFunc() {
    var cimpl1 = ObjcImpl()
    var cimpl2 = ObjcImpl2()
    var structWithRef = StructWithRef(objcProto1: cimpl1, objcProto2: cimpl2, wobjcProto: cimpl1)
    
    // ClassImpl:ClaassProto->func classProtoFunc　が出力される
    structWithRef.objcProto1?.objcProtoFunc()
    // ClassImpl2:ClaassProto->func classProtoFunc　が出力される
    structWithRef.objcProto2?.objcProtoFunc()
    // ObjcImpl:ObjcProto->func objcProtoFunc　が出力される
    structWithRef.wobjcProto?.objcProtoFunc()
}

testFunc()
```



# LLVM的な違いを見てみる

## 事前準備
Protocolの定義のみを書いた状態で、中間言語を生成してどのようになっているのか確認してみます。
swiftをコマンドラインから実行できるように、事前にxcrunのパスを通しておきます。

```
export PATH=/Applications/Xcode6-Beta3.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/bin:$PATH
```

確認には、下記のようなコマンドを利用しています。

```
xcrun swift -emit-ir objc_protocol.swift > objc_protocol_ir.txt
```

## プロトコルの宣言における差異

通常のprotcol、@class_protocol、@objc の宣言については、下記のような結果になります。

```swift_protocol_ir.txt（抜粋）
%swift.protocol = type { i8*, i8*, i8*, i8*, i8*, i8*, i8*, i8*, i32, i32 }

@0 = private unnamed_addr constant [24 x i8] c"P14swift_protocol4Prot_\00"
@_TMp14swift_protocol4Prot = constant %swift.protocol { i8* null, i8* getelementptr inbounds ([24 x i8]* @0, i64 0, i64 0), i8* null, i8* null, i8* null, i8* null, i8* null, i8* null, i32 72, i32 7 }

```


```class_protocol_ir.txt（抜粋）
%swift.protocol = type { i8*, i8*, i8*, i8*, i8*, i8*, i8*, i8*, i32, i32 }

@0 = private unnamed_addr constant [32 x i8] c"P14class_protocol11ClaassProto_\00"
@_TMp14class_protocol11ClaassProto = constant %swift.protocol { i8* null, i8* getelementptr inbounds ([32 x i8]* @0, i64 0, i64 0), i8* null, i8* null, i8* null, i8* null, i8* null, i8* null, i32 72, i32 5 }
```

```objc_protocol_ir.txt（抜粋）
@"\01L_selector_data(objcProtoFunc)" = internal constant [14 x i8] c"objcProtoFunc\00", section "__TEXT,__objc_methname,cstring_literals", align 1
@0 = private unnamed_addr constant [8 x i8] c"v16@0:8\00"
@1 = private unnamed_addr constant [31 x i8] c"_TtP13objc_protocol9ObjcProto_\00"
@_PROTOCOL_INSTANCE_METHODS__TtP13objc_protocol9ObjcProto_ = private constant { i32, i32, [1 x { i8*, i8*, i8* }] } { i32 24, i32 1, [1 x { i8*, i8*, i8* }] [{ i8*, i8*, i8* } { i8* getelementptr inbounds ([14 x i8]* @"\01L_selector_data(objcProtoFunc)", i64 0, i64 0), i8* getelementptr inbounds ([8 x i8]* @0, i64 0, i64 0), i8* null }] }, section "__DATA, __objc_const", align 8
@_PROTOCOL_METHOD_TYPES__TtP13objc_protocol9ObjcProto_ = private constant { [1 x i8*] } { [1 x i8*] [i8* getelementptr inbounds ([8 x i8]* @0, i64 0, i64 0)] }, section "__DATA, __objc_const", align 8
@_PROTOCOL__TtP13objc_protocol9ObjcProto_ = private constant { i8*, i8*, i8*, { i32, i32, [1 x { i8*, i8*, i8* }] }*, i8*, i8*, i8*, i8*, i32, i32, { [1 x i8*] }* } { i8* null, i8* getelementptr inbounds ([31 x i8]* @1, i64 0, i64 0), i8* null, { i32, i32, [1 x { i8*, i8*, i8* }] }* @_PROTOCOL_INSTANCE_METHODS__TtP13objc_protocol9ObjcProto_, i8* null, i8* null, i8* null, i8* null, i32 80, i32 1, { [1 x i8*] }* @_PROTOCOL_METHOD_TYPES__TtP13objc_protocol9ObjcProto_ }, section "__DATA, __objc_const", align 8
@"\01l_OBJC_LABEL_PROTOCOL_$__TtP13objc_protocol9ObjcProto_" = weak hidden global i8* bitcast ({ i8*, i8*, i8*, { i32, i32, [1 x { i8*, i8*, i8* }] }*, i8*, i8*, i8*, i8*, i32, i32, { [1 x i8*] }* }* @_PROTOCOL__TtP13objc_protocol9ObjcProto_ to i8*), section "__DATA,__objc_protolist,coalesced,no_dead_strip", align 8
@"\01l_OBJC_PROTOCOL_REFERENCE_$__TtP13objc_protocol9ObjcProto_" = weak hidden global i8* bitcast ({ i8*, i8*, i8*, { i32, i32, [1 x { i8*, i8*, i8* }] }*, i8*, i8*, i8*, i8*, i32, i32, { [1 x i8*] }* }* @_PROTOCOL__TtP13objc_protocol9ObjcProto_ to i8*), section "__DATA,__objc_protorefs,coalesced,no_dead_strip", align 8
```

宣言部分だけから中間言語を生成すると、通常のprotocolと@class_protocolの違いはほとんどないようですが、@objcの場合は大幅に違いがあることがわかります。

## プロトコルの採用における差異

宣言だけだとそれほど差がないprotocolと@class_protocolですが、ClassにProtocolを採用すると少し差異が見られます。
それぞれ、Protocolの宣言とProtocolを採用

```class_protocol_ir.txt（抜粋）
@_TWPC14class_protocol9ClassImplS_11ClaassProto = constant [1 x i8*] [i8* bitcast (void (%C14class_protocol9ClassImpl*, %swift.type*)* @_TTWC14class_protocol9ClassImplS_11ClaassProtoFS1_14classProtoFuncUS1___fQPS1_FT_T_ to i8*)], align 8

...(省略)

define void @_TTWC14class_protocol9ClassImplS_11ClaassProtoFS1_14classProtoFuncUS1___fQPS1_FT_T_(%C14class_protocol9ClassImpl*, %swift.type* %Self) {
entry:
  %1 = getelementptr inbounds %C14class_protocol9ClassImpl* %0, i32 0, i32 0, i32 0
  %.metadata = load %swift.type** %1, align 8
  %2 = bitcast %swift.type* %.metadata to void (%C14class_protocol9ClassImpl*)**
  %3 = getelementptr inbounds void (%C14class_protocol9ClassImpl*)** %2, i64 8
  %4 = load void (%C14class_protocol9ClassImpl*)** %3, align 8
  %5 = bitcast void (%C14class_protocol9ClassImpl*)* %4 to i8*
  %6 = bitcast i8* %5 to void (%C14class_protocol9ClassImpl*)*
  call void %6(%C14class_protocol9ClassImpl* %0)
  ret void
}
```


```swift_protocol_ir.txt（抜粋）
@_TWPC14swift_protocol4ImplS_4Prot = constant [1 x i8*] [i8* bitcast (void (%C14swift_protocol4Impl**, %swift.type*)* @_TTWC14swift_protocol4ImplS_4ProtFS1_8protFuncUS1___fRQPS1_FT_T_ to i8*)], align 8

...(省略)

define void @_TTWC14swift_protocol4ImplS_4ProtFS1_8protFuncUS1___fRQPS1_FT_T_(%C14swift_protocol4Impl** noalias, %swift.type* %Self) {
entry:
  %1 = load %C14swift_protocol4Impl** %0, align 8
  %2 = bitcast %C14swift_protocol4Impl* %1 to %swift.refcounted*
  call void @swift_retain_noresult(%swift.refcounted* %2) #0
  %3 = getelementptr inbounds %C14swift_protocol4Impl* %1, i32 0, i32 0, i32 0
  %.metadata = load %swift.type** %3, align 8
  %4 = bitcast %swift.type* %.metadata to void (%C14swift_protocol4Impl*)**
  %5 = getelementptr inbounds void (%C14swift_protocol4Impl*)** %4, i64 8
  %6 = load void (%C14swift_protocol4Impl*)** %5, align 8
  %7 = bitcast void (%C14swift_protocol4Impl*)* %6 to i8*
  %8 = bitcast i8* %7 to void (%C14swift_protocol4Impl*)*
  call void %8(%C14swift_protocol4Impl* %1)
  ret void
}
```



# 感想

@class_protocolと@objcはクラス以外には採用できないので、クラス以外に採用する際には通常のrotocolを利用する必要があることが確認できました。

LLVMてきにみると、protocolと@class_protocolは宣言ではほとんど差がないのは意外でした。


# 参考資料


* [The Swift Programming Language - Attributes](https://developer.apple.com/library/prerelease/ios/documentation/Swift/Conceptual/Swift_Programming_Language/Attributes.html)
* [Inside Swift](http://www.eswick.com/2014/06/inside-swift/)
* [Swift reverse engineering:swift function name rule?](http://stackoverflow.com/questions/24283804/swift-reverse-engineeringswift-function-name-rule)