LoginSignup
46
44

More than 5 years have passed since last update.

Swiftのアクセス修飾子とObjCをためしてみたらFrameworkでちょっとした罠があった

Last updated at Posted at 2014-07-22

Xcode6-beta4がリリースされ、beta1の段階で言及されていたアクセスコントロールが満を持して追加されました!

そこで早速アクセスコントロールを試してみました。

Swiftには3種類の修飾子が用意されています。

public どこからでもアクセスできる
internal 同じターゲットからアクセスできる
private 同じファイル内のみアクセスできる

上記について、SwiftのObjectをSwift、ObjCからアクセスした時にどうなるのか調べました。

同一ターゲットの場合

下記のようなSwiftを書きました。

MyClass.swift
public class MyPublicClass:NSObject {
    public func publicMethod() {
        println("\(self.dynamicType.description()) public method")
    }

    internal func internalMethod() {
        println("\(self.dynamicType.description()) internal method")
    }

    private func privateMethod() {
        println("\(self.dynamicType.description()) private method")
    }

    func task() {
        MyPublicClass().privateMethod()
        MyInternalClass().privateMethod()
        MyPrivateClass().privateMethod()
    }
}

internal class MyInternalClass:NSObject {
    public func publicMethod() {
        println("\(self.dynamicType.description()) public method")
    }

    internal func internalMethod() {
        println("\(self.dynamicType.description()) internal method")
    }

    private func privateMethod() {
        println("\(self.dynamicType.description()) private method")
    }

}

private class MyPrivateClass:NSObject {
    public func publicMethod() {
        println("\(self.dynamicType.description()) public method")
    }

    internal func internalMethod() {
        println("\(self.dynamicType.description()) internal method")
    }

    private func privateMethod() {
        println("\(self.dynamicType.description()) private method")
    }
}

同じファイル内ではprivate宣言されたクラスやメソッドにアクセスできます。

別ファイルから呼び出す際はpublic,internalのものが呼び出せます。

Swift
        let pub = MyPublicClass()
        pub.publicMethod()
        pub.internalMethod()

        let inter = MyInternalClass()
        inter.publicMethod()
        inter.internalMethod()

        pub.task()

同様に同じターゲット内のObjCのクラスもpublic,internalのものが呼び出せます。

MyObjCClass.m
@implementation MyObjCClass

-(void)task
{
    MyPublicClass *pub = [MyPublicClass new];
    [pub publicMethod];
    [pub internalMethod];

    MyInternalClass *inter = [MyInternalClass new];
    [inter publicMethod];
    [inter internalMethod];
}

@end

別ターゲットの場合

別途Frameworkを作り同様に同じようなクラスを作ります

MyFwClass.swift
public class MyFwPublicClass:NSObject {
    public init()  {

    }

    public func publicMethod() {
        println("\(self.dynamicType.description()) public method")
    }

    internal func internalMethod() {
        println("\(self.dynamicType.description()) internal method")
    }

    private func privateMethod() {
        println("\(self.dynamicType.description()) private method")
    }

    func task() {
        MyFwInternalClass().privateMethod()
        MyFwPrivateClass().privateMethod()
    }
}

internal class MyFwInternalClass:NSObject {
    public func publicMethod() {
        println("\(self.dynamicType.description()) public method")
    }

    internal func internalMethod() {
        println("\(self.dynamicType.description()) internal method")
    }

    private func privateMethod() {
        println("\(self.dynamicType.description()) private method")
    }

}

private class MyFwPrivateClass:NSObject {
    public func publicMethod() {
        println("\(self.dynamicType.description()) public method")
    }

    internal func internalMethod() {
        println("\(self.dynamicType.description()) internal method")
    }

    private func privateMethod() {
        println("\(self.dynamicType.description()) private method")
    }

}

同じフレームワーク内の別Swiftファイルからはpublic,internalにアクセスできます。

MyFwOtherClass.swift
class MyFwOtherClass: NSObject {
    func task() {
        let fwPub = MyFwPublicClass()
        fwPub.publicMethod()
        fwPub.internalMethod()

        let fwInter = MyFwInternalClass()
        fwInter.publicMethod()
        fwInter.internalMethod()
    }
}

しかし、同じフレームワーク内の別ObjCファイルからはpublicにしかアクセスできませんでした。Frameworkだと同一モジュールでもObjCからinternalへのアクセスができません。

MyFwObjCClass.m
#import <MyFramework/MyFramework-Swift.h>

@implementation MyFwObjCClass

-(void)task {
    MyFwPublicClass *pub = [MyFwPublicClass new];
    [pub publicMethod];
}

@end

別ターゲットのSwiftを呼び出す

最後に別ターゲットのSwiftを呼び出します。

Swift
   let fwPub = MyFwPublicClass()
   fwPub.publicMethod()

記述通りpublicのみを呼び出すことができます。
ObjCでも同様です。

ObjC
    MyFwPublicClass *fwPub = [MyFwPublicClass new];
    [fwPub publicMethod];

しかしここで1つ罠がありました。

アクセス修飾子を指定しないとデフォルトでinternalになります。
そしてinitを明示的に宣言しないと必然的にinitinternalになってしまい、仮にモジュール内でpublic宣言していたとしてもinitinternalのため オブジェクトが生成できません!

なのでFrameworkを作る際はクラスとともにinitもPublic宣言を必ずしてください!

MyFwClass.swift
public class MyFwPublicClass:NSObject {
    public init()  {

    }
・・・

まとめ

  • 同一アプリ内ならばSwiftのアクセス修飾子はSwift/ObjCによらず通用する
  • Framework内ならばSwift同士ならばアクセス修飾子通りにアクセスできるが、ObjCのクラスからはpublicのみのアクセスになる
  • 別モジュールから呼び出すときはpublicのみアクセスできる。この時initも明示的にpublicにしないとオブジェクトの生成ができなくなるので必ずpublicにする

かなり適当な検証なので間違っていたらご連絡ください。

46
44
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
46
44