Xcode6-beta4がリリースされ、beta1の段階で言及されていたアクセスコントロールが満を持して追加されました!
そこで早速アクセスコントロールを試してみました。
Swiftには3種類の修飾子が用意されています。
public | どこからでもアクセスできる |
internal | 同じターゲットからアクセスできる |
private | 同じファイル内のみアクセスできる |
上記について、SwiftのObjectをSwift、ObjCからアクセスした時にどうなるのか調べました。
同一ターゲットの場合
下記のような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
のものが呼び出せます。
let pub = MyPublicClass()
pub.publicMethod()
pub.internalMethod()
let inter = MyInternalClass()
inter.publicMethod()
inter.internalMethod()
pub.task()
同様に同じターゲット内のObjCのクラスもpublic
,internal
のものが呼び出せます。
@implementation MyObjCClass
-(void)task
{
MyPublicClass *pub = [MyPublicClass new];
[pub publicMethod];
[pub internalMethod];
MyInternalClass *inter = [MyInternalClass new];
[inter publicMethod];
[inter internalMethod];
}
@end
別ターゲットの場合
別途Frameworkを作り同様に同じようなクラスを作ります
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
にアクセスできます。
class MyFwOtherClass: NSObject {
func task() {
let fwPub = MyFwPublicClass()
fwPub.publicMethod()
fwPub.internalMethod()
let fwInter = MyFwInternalClass()
fwInter.publicMethod()
fwInter.internalMethod()
}
}
しかし、同じフレームワーク内の別ObjCファイルからはpublic
にしかアクセスできませんでした。Frameworkだと同一モジュールでもObjCからinternal
へのアクセスができません。
#import <MyFramework/MyFramework-Swift.h>
@implementation MyFwObjCClass
-(void)task {
MyFwPublicClass *pub = [MyFwPublicClass new];
[pub publicMethod];
}
@end
別ターゲットのSwiftを呼び出す
最後に別ターゲットのSwiftを呼び出します。
let fwPub = MyFwPublicClass()
fwPub.publicMethod()
記述通りpublic
のみを呼び出すことができます。
ObjCでも同様です。
MyFwPublicClass *fwPub = [MyFwPublicClass new];
[fwPub publicMethod];
しかしここで1つ罠がありました。
アクセス修飾子を指定しないとデフォルトでinternal
になります。
そしてinitを明示的に宣言しないと必然的にinit
もinternal
になってしまい、仮にモジュール内でpublic
宣言していたとしてもinit
がinternal
のため オブジェクトが生成できません!
なのでFrameworkを作る際はクラスとともにinit
もPublic宣言を必ずしてください!
public class MyFwPublicClass:NSObject {
public init() {
}
・・・
まとめ
- 同一アプリ内ならばSwiftのアクセス修飾子はSwift/ObjCによらず通用する
- Framework内ならばSwift同士ならばアクセス修飾子通りにアクセスできるが、ObjCのクラスからは
public
のみのアクセスになる - 別モジュールから呼び出すときは
public
のみアクセスできる。この時initも明示的にpublicにしないとオブジェクトの生成ができなくなるので必ずpublic
にする
かなり適当な検証なので間違っていたらご連絡ください。