English | 简体中文 | 繁體中文 | Русский язык | Français | Español | Português | Deutsch | 日本語 | 한국어 | Italiano | بالعربية
접근 제어는 다른 소스 파일이나 모듈에서 코드가 자신의 코드에 접근할 수 있는 수준을 제한할 수 있습니다.
단일 타입(클래스, 구조체, 열거형)에 접근 수준을 명확히 설정할 수 있으며, 이 타입의 속성, 함수, 초기화 메서드, 기본 타입, 인덱스 등에도 접근 수준을 설정할 수 있습니다.
프로토콜도 특정 범위 내에서만 사용될 수 있으며, 이는 프로토콜 내의 전역 상수, 변수, 함수를 포함합니다.
접근 제어는 모듈과 소스 파일에 기반합니다.
모듈은 독립된 단위로 구축하고 배포되는 Framework이나 애플리케이션을 의미합니다. Swift에서 모듈은 import 키워드를 사용하여 다른 모듈을 포함할 수 있습니다.
소스 파일은 단일 소스 코드 파일로, 일반적으로 모듈에 속해 있으며, 소스 파일은 여러 클래스와 함수 정의를 포함할 수 있습니다.
Swift는 코드 내 엔티티에 대해 네 가지 다른 접근 수준을 제공합니다: public, internal, fileprivate, private.
접근 수준 | 정의 |
---|---|
public | 자신의 모듈 내 소스 파일에 있는 모든 엔티티에 접근할 수 있으며, 다른 사람도 이 모듈을 포함하여 소스 파일에 있는 모든 엔티티에 접근할 수 있습니다. |
internal | 자신의 모듈 내 소스 파일에 있는 모든 엔티티에 접근할 수 있지만, 다른 사람은 이 모듈의 소스 파일에 있는 엔티티에 접근할 수 없습니다. |
fileprivate | 파일 내에서만 프라이빗이며, 현재 소스 파일에서만 사용할 수 있습니다. |
private | 객체 내에서만 접근할 수 있으며, 이 클래스나 구조체의 범위를 벗어나면 접근할 수 없습니다. |
public이 가장 높은 접근 수준이고, private가 가장 낮은 접근 수준입니다。
public, internal, fileprivate, private 접근자로 엔티티의 접근 수준을 선언합니다:
public class SomePublicClass {} internal class SomeInternalClass {} fileprivate class SomeFilePrivateClass {} private class SomePrivateClass {} public var somePublicVariable = 0 internal let someInternalConstant = 0 fileprivate func someFilePrivateFunction() {} private func somePrivateFunction() {}
특별한 설명이 없는 경우, 엔티티는 기본 접근 수준 internal을 사용합니다。
class SomeInternalClass {} // 접근 수준이 internal let someInternalConstant = 0 // 접근 수준이 internal
함수 접근 수준은 함수의 매개변수 타입과 반환 타입의 접근 수준에 따라 결정되어야 합니다。
아래의 예제는 someFunction이라는 전역 함수를 정의하고 접근 수준을 명확하게 선언하지 않습니다。
func someFunction() -> (SomeInternalClass, SomePrivateClass) { // 함수 구현 }
함수 중 하나인 SomeInternalClass의 접근 수준이 internal이고, 다른 SomePrivateClass의 접근 수준이 private입니다. 따라서 튜플 접근 수준의 원칙에 따라, 이 튜플의 접근 수준은 private입니다(튜플의 접근 수준은 튜플 내에서 접근 수준이 가장 낮은 타입과 일치합니다)。
함수 반환 타입의 접근 수준이 private인 이유로, private 접근자를 사용하여 명확하게 함수를 선언해야 합니다:
private func someFunction() -> (SomeInternalClass, SomePrivateClass) { // 함수 구현 }
public 또는 internal으로 함수를 선언하거나 기본 접근 수준 internal을 사용하는 것은 잘못입니다. 왜냐하면 그렇게 하면 private 수준의 반환 값을 접근할 수 없기 때문입니다.
구조체 중 멤버의 접근 수준은 그 구조체에서 상속됩니다. 구조체의 멤버에 대해 독립적으로 다른 접근 수준을 선언할 수 없습니다.
예를 들어, 아래의 예시에서 enum Student는 명확하게 public 레벨로 선언되었으므로, 그 멤버 Name, Mark의 접근 수준도 public입니다:
public enum Student { case .Name(String) case .Mark(Int, Int, Int) } var studDetails = Student.Name("Swift") var studMarks = Student.Mark(98,97,95) switch studMarks { case .Name(let studName): print("학생 이름: (studName).") case .Mark(let Mark1, let Mark2, let Mark3) print("학생 성적: (Mark1),(Mark2),(Mark3)") }
위 프로그램 실행 결과는 다음과 같습니다:
학생 성적: 98,97,95
서브 클래스의 접근 수준은 부모 클래스의 접근 수준보다 높을 수 없습니다. 예를 들어, 부모 클래스의 접근 수준이 internal이면, 서브 클래스의 접근 수준은 public으로 선언될 수 없습니다.
public class SuperClass { fileprivate func show() { print("상위 클래스") } } // 접근 수준은 상위 클래스보다 높을 수 없습니다 internal > public internal class SubClass: SuperClass { override internal func show() { print("서브 클래스") } } let sup = SuperClass() sup.show() let sub = SubClass() sub.show()
위 프로그램 실행 결과는 다음과 같습니다:
상위 클래스 서브 클래스
가변량, 변수, 속성은 그들의 타입보다 높은 접근 수준을 가질 수 없습니다.
예를 들어, public 레벨의 속성을 정의했지만, 그 타입이 private 레벨인 경우는 컴파일러에 의해 허용되지 않습니다.
또한, 인덱스는 인덱스 타입이나 반환 타입보다 높은 접근 수준을 가질 수 없습니다.
가변량, 변수, 속성, 인덱스 인덱스의 정의 타입이 private 레벨이면, 그들은 명확하게 접근 수준을 private로 선언해야 합니다:
private var privateInstance = SomePrivateClass()
상수, 변수, 속성, 인덱스 인터셉터의 Getter와 Setter의 접근 수준은 그 소속 구성원의 접근 수준을 继承합니다.
Setter의 접근 수준은 Getter의 접근 수준보다 낮을 수 있습니다. 이렇게 하면 변수, 속성, 인덱스 인터셉터의 읽기와 쓰기 권한을 제어할 수 있습니다.
class Samplepgm { fileprivate var counter: Int = 0{ willSet(newTotal){ print("카운터: (newTotal)") } didSet{ if counter > oldValue { print("신규 증가량 (counter - oldValue)") } } } } let NewCounter = Samplepgm() NewCounter.counter = 100 NewCounter.counter = 800
카운터의 접근 수준은 fileprivate이며, 파일 내에서만 접근할 수 있습니다.
위 프로그램 실행 결과는 다음과 같습니다:
카운터: 100 신규 증가량 100 카운터: 800 신규 증가량 700
사용자 정의 초기화 메서드에 접근 수준을 지정할 수 있지만, 그 소속 클래스의 접근 수준보다 높지 않아야 합니다. 하지만 필수 생성자는 예외이며, 소속 클래스의 접근 수준과 동일해야 합니다.
함수나 메서드 매개변수와 마찬가지로, 초기화 메서드 매개변수의 접근 수준도 초기화 메서드의 접근 수준보다 낮을 수 없습니다.
Swift는 구조체와 클래스 모두에 기본的无参数 초기화 메서드를 제공하여 모든 속성에 초기화 값을 할당하지만, 특정 값을 제공하지 않습니다.
기본 초기화 메서드의 접근 수준은 소속 타입의 접근 수준과 동일합니다.
각 서브 클래스의 init() 메서드 앞에서 required 키워드를 사용하여 접근 권한을 선언합니다.
class classA { required init() { var a = 10 print(a) } } class classB: classA { required init() { var b = 30 print(b) } } let res = classA() let show = classB()
위 프로그램 실행 결과는 다음과 같습니다:
10 30 10
프로토콜에 명확한 접근 수준을 지정하려면, 그 프로토콜이만 지정한 접근 수준의 범위 내에서만 사용되도록 확실하게 해야 합니다.
public 접근 수준의 프로토콜을 정의하면, 이 프로토콜이 제공하는 필수 함수도 public 접근 수준입니다. 이는 다른 유형과 다릅니다. 예를 들어, public 접근 수준의 다른 유형의 멤버 접근 수준은 internal입니다.
public protocol TcpProtocol {}} init(no1: Int) } public class MainClass { var no1: Int // local storage init(no1: Int) { self.no1 = no1 // initialization } } class SubClass: MainClass, TcpProtocol { var no2: Int init(no1: Int, no2 : Int) { self.no2 = no2 super.init(no1:no1) } // 편리한 메서드는 하나의 매개변수만 요구합니다 required override convenience init(no1: Int) { self.init(no1:no1, no2:0) } } let res = MainClass(no1: 20) let show = SubClass(no1: 30, no2: 50) print("res is: \(res.no"1)") print("res is: \(show.no"1)") print("res is: \(show.no"2)")
위 프로그램 실행 결과는 다음과 같습니다:
res is: 20 res is: 30 res is: 50
제한된 조건하에서 클래스, 구조체, 열거형을 확장할 수 있습니다. 확장 멤버는 원래 클래스 멤버와 동일한 접근 수준을 가지게 되어야 합니다. 예를 들어, 공개된 타입을 확장한 경우, 새로 추가된 멤버는 원래 멤버와 동일한 기본 internal 접근 수준을 가지게 됩니다.
또는, 확장의 접근 수준을 명확히 지정할 수 있습니다. 예를 들어, private extension를 사용하여 확장 내 모든 멤버에 대해 새로운 기본 접근 수준을 지정할 수 있습니다. 이 새로운 기본 접근 수준은 단일 멤버에 의해 명시적으로 지정된 접근 수준에 의해 덮어질 수 있습니다.
가상 타입이나 가상 함수의 접근 수준은 가상 타입, 함수 자체, 가상 타입 매개변수 중 가장 낮은 접근 수준을 가져야 합니다.
public struct TOS<T> { var items = [T]() private mutating func push(item: T) { items.append(item) } mutating func pop() -> T { return items.removeLast() } } var tos = TOS<String>() tos.push("Swift") print(tos.items) tos.push("일반화") print(tos.items) tos.push("타입 매개변수") print(tos.items) tos.push("타입 매개변수 이름") print(tos.items) let deletetos = tos.pop()
위 프로그램 실행 결과는 다음과 같습니다:
["Swift"] ["Swift", "일반화"] ["Swift", "가상", "타입 매개변수"] ["Swift", "가상", "타입 매개변수", "타입 매개변수 이름"]
모든 정의한 타입 별명은 다른 타입으로 간주되어 접근 제어를 위해 사용됩니다. 타입 별명의 접근 수준은 원래 타입의 접근 수준보다 높을 수 없습니다.
예를 들어, private 수준의 타입 별명은 public, internal, private 수준의 타입에 설정할 수 있지만, public 수준의 타입 별명은 public 수준의 타입에만 설정할 수 있으며, internal이나 private 수준의 타입에 설정할 수 없습니다.
주의: 이 규칙은 협정 일관성을 위한 관련 타입의 별명 지정에도 적용됩니다.
public protocol Container { typealias ItemType mutating func append(item: ItemType) var count: Int { get } subscript(i: Int) -> ItemType { get } } struct Stack<T>: Container { // original Stack<T> implementation var items = [T]() mutating func push(item: T) { items.append(item) } mutating func pop() -> T { return items.removeLast() } // Container 프로토콜 준수 mutating func append(item: T) { self.push(item) } var count: Int { return items.count } subscript(i: Int) -> T { return items[i] } } func allItemsMatch< C1: Container, C2: Container where C1.ItemType == C2.ItemType, C1.ItemType: Equatable> (someContainer: C1, anotherContainer: C2) -> Bool { // 둘다의 컨테이너가 같은 수의 항목을 포함하고 있는지 확인하십시오 someContainer.count != anotherContainer.count { return false } // 각 항목 쌍을 확인하여 동일한지 확인하십시오 for i in 0..<someContainer.count { if someContainer[i] != anotherContainer[i] { return false } } // 모든 항목이 일치하므로 true를 반환 return true } var tos = Stack<String>() tos.push("Swift") print(tos.items) tos.push("일반화") print(tos.items) tos.push("Where 문") print(tos.items) var eos = ["Swift", "일반화", "Where 문"] print(eos)
위 프로그램 실행 결과는 다음과 같습니다:
["Swift"] ["Swift", "일반화"] ["Swift", "일반화", "Where 문"] ["Swift", "일반화", "Where 문"]