티스토리 뷰
안녕하세요 :) Zedd입니다.
<Access Control(접근제어) - (1)>글에서 Swift에서의 5가지 접근 레벨들을 간단하게 봤는데..
오늘은! The Swift Programming Language (Swift 4.0.3) - Access Control에 정리된 조금 세세한 내용을 보려고 합니당! 저번시간에 정리한 부분들은 넘어갈게요 :)
반드시 <Access Control(접근제어) - (1)>글이나 다른 접근제어 글을 읽고오시는 글을 추천드립니당 :) 각 접근수준이 어떤 특징을 가졌는지는 다시 안볼려고해요!!!
Access Control (2)
Swift의 접근 수준(level)에는 전반적인 guiding principle이 있습니다.
: 더 낮은(더 제한적인) 접근 수준을 갖는 다른 엔티티에 대해 엔티티를 정의할 수 없습니다.
이게 번역이..참......뭐라고 해야할지.........죄송.........
일단 무슨소리인지 예를 들어서 보면,
1. internal, fileprivate, private타입안에서 public변수를 정의할 수 없습니다. 왜냐하면 해당 타입은 public변수가 사용되는 모든곳(내부모듈, 외부모듈)에서 사용 할 수 없기 때문입니다.
2. 함수는 해당 매개변수 타입 및 리턴 타입보다 더 높은(덜 제한적인) 접근 수준을 가질 수 없습니다. 왜냐하면 해당 타입을 주변 코드에서 사용할 수 없는 상황에서 함수를 사용할 수 있기 때문입니다.
Default Access Levels
Access Levels for Single-Target Apps
Access Levels for Frameworks
Access Levels for Unit Test Targets
Access Control Syntax
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() {}
이렇게 앞에, 내가 원하는 접근 수정자를 붙히면 된답니다. 클래스 앞, 구조체 앞, 열거형 앞, ...등등
class SomeInternalClass {} // implicitly internal
let someInternalConstant = 0 // implicitly internal
이렇게되면 암시적으로 internal이라고 그랬죠?
Custom Types
fileprivate class FileprivateClass {
func someMethod() {}
}
이렇게 해주면, 저 someMethod앞는 아무 접근 수준을 지정해주지 않았다고 해서 internal이구나~!~가 아니라!!!!
fileprivate가 되는것이죠.
타입의 접근 수준을 internal 또는 public으로 정의하거나(접근 수준을 명시적으로 지정하지 않고, 기본 접근 레벨을 사용하는 경우 => internal이겠죠?) 타입 구성원의 기본 접근 수준은 internal입니다.
이게 무슨말이냐면, internal일 경우에는 구성원들이 기본적으로 internal로 되는건 당연하지만 public??...public도 인가요?
(지금 해보니..open으로 해놔도 기본 접근 수준이 internal이네요)
그러니까 여기서 헷갈릴 수 있는 점은, internal, file-private, private로 접근 수준을 지정해 놓으면 그 안 구성원들은 자동으로 해당 타입의 접근 수준을 따라가지만, public과 open의 경우에는 따로 명시해놓지 않으면 internal로 기본 접근 수준이 정해진다는 소리입니다. 각각 public과 open이 되는 것이 아니라요.
예를들어, 이런 코드가 있었다고 생각해볼게요.
public class PublicClass {
init(){}
func someMethod() {}
}
이 PublicClass는 접근수준이 public이기때문에, 내부모듈은 물론이고, 외부모듈에서 접근이 가능하죠.
하지만 이니셜라이저와 메소드에는 아무 접근 수준을 명시적으로 지정해주지 않았어요.
====> 이러면 뭐다?????기본적으로 internal이 된다 ㅇㅇㅇ
즉!!!! 외부모듈에서 이 PublicClass가 인식은 되지만!!!!! 이니셜라이저가 기본적으로 internal이기때문에, 외부모듈에서는 이 PublicClass의 인스턴스를 만들 수가 없습니다.
이렇게요. 저 클래스의 접근 수준이 open이어도 똑같은 일이 발생합니다.
그럼 어떻게 해야한다? 명시적으로 접근 수준을 지정해주면 된다!!
public class PublicClass {
public init(){}
func someMethod() {}
}
이렇게 명시적으로 지정을 해주면, 이 PublicClass의 인스턴스를 만들 수 있게 되는 것이죠.
이렇게 만든 인스턴스로 someMethod()를 호출 할 수 있다?
==> 없습니다. 왜냐??? internal이기 때문이죠. 외부모듈에서는 internal접근수준에 접근 할 수 없는건 당연하죠?
이것도 마찬가지로 public으로 해주면 접근이 가능해질거에요.
하지만 이게 다~~~외부모듈에서만 이렇고, 내부모듈에서는 전부 할 수 있겠죠? 왜냐? internal이니까 ㅇㅇ
note :
public타입은 기본적으로 internal 구성원이 있고, public구성원이 없습니다. 타입 멤버를 public으로 만들고 싶으면, 명시적으로 표시해야합니다.
이 요구사항은 타입에 대한 public-facing API가 게시하기로 선택하고 실수로 public API로 타입 내부 작동을 표시 하지 않도록 합니다.
Tuple Types
Function Types
func someFunction() -> (SomeInternalClass, SomePrivateClass) {
// function implementation goes here
}
이 함수의 접근 수준은 매개변수나 리턴타입에서 가장 제한 적인 접근수준으로서 계산된다고 그랬죠? 일단 매개변수가 없고, 리턴타입이 튜플타입인데, 튜플타입도 위에서 구성원 중 가장 제한적인 접근수준으로 계산이 된다고 했으니..리턴타입은 private가 되겠고,
그러면 이 함수의 접근 수준은 private겠네요!
하지만 이렇게 이 함수의 접근수준이 private로 계산이 되었는데, 앞에 접근 지정자를 설정하지 않거나 (=internal), public 접근 수준을 지정하는 것은 유효하지 않기 때문에!!
private func someFunction() -> (SomeInternalClass, SomePrivateClass) {
// function implementation goes here
}
이렇게 앞에 private를 붙혀주는 것이 올바릅니다.
Enumeration Types
public enum CompassPoint{
private case North //Error!
}
이렇게 되면 error라는 것이죠. North는 무조건 public이어야합니다.
Raw Values and Associated Values
Nested Types
open class SomeOpenClass{
public init() {}
struct myStruct{
var name : String
}
}
이렇게 되어있다면, 외부 모듈에서
이런게 불가능하다는 것이죠. 왜냐? myStruct는 internal 접근 수준이기 때문입니다.
Subclassing
class SomeInternalClass { }
public class PublicClass : SomeInternalClass{ //Error!
}
이렇게 하면 오류가 난다는 것이죠. 당연하죠? <Inheritance(상속)>글에서도 배웠듯이 서브클래스는 슈퍼클래스의 멤버에 접근이 가능해지게 되는데, 이 서브클래스가 더 덜 제한적인 접근 수준을 가지게 되면, 슈퍼클래스 멤버에 접근이 안될수도 있으니까요. 그럼 반대로 서브클래스가 더 제한적인 접근 수준은 가질 수 있다..라는게 되죠.
public class A {
}
internal class B: A {
}
이렇게요 :)
또한, 특정 접근 컨텍스트에서 볼 수 있는 클래스 멤버(메소드, 프로퍼티, 이니셜라이져 또는 서브스크립트)를 override(재정의) 할 수 있습니다.
override를 사용하면, 상속된 클래스 멤버가 슈퍼클래스 버전보다 더 쉽게 접근 할 수 있습니다. (그러니까 덜 제한적인 접근 수준을 가질 수 있다는 소리)
public class A {
fileprivate func someMethod() {}
}
internal class B: A {
override internal func someMethod() {}
}
someMethod()는 분명 슈퍼클래스에는 fileprivate였는데...서브클래스에서 override될 때는 internal이 될 수 있다는 의미죠.
서브클래스 멤버가 서브클래스 멤버보다 액세스 권한이 더 제한적인 슈퍼 클래스 멤버를 호출하는것도 유효합니다.
public class A {
fileprivate func someMethod() {}
}
internal class B: A {
override internal func someMethod() {
super.someMethod()
}
}
같은 파일내에 있다면, 일단 someMethod()를 override하는 게 가능할 테고!!! 근데 override할 때 덜 제한적인 접근 수준으로 만들 수 있다고 했죠?
그 override(재정의)내에서!!!! 내 슈퍼클래스의 원래 구현을 호출할 수 있다는 겁니다. 물론 해당 메소드나 프로퍼티의 접근 수준에 접근 할 수 있는 컨텍스트 안에 있다고 했을때요! 지금은 같은 소스파일 내에 있으니, 슈퍼클래스의 someMethod()를 호출 할 수 있는 거겠죠?
Constants, Variables, Properties, and Subscripts
예를들어,
class B{
init() {}
}
public var instance = B() //Error!
이럴 수는 없다는거죠.
B클래스는 internal인데, B보다 덜 제한적인 public접근 수준의 변수는 만들 수 없습니다.
var instance = B()
fileprivate var instance = B()
private var instance = B()
위 3가지 경우만 가능하다는 것이죠.
그리고 만약 B가 file-private거나 private접근 수준이면, 꼭!!
fileprivate var instance = B()
private var instance = B()
앞에 이렇게 file-private나 private를 붙혀줘야합니다.
궁금해서 몇개 해봤는데,
private class B{
init() {}
}
fileprivate var instance = B()
이건 되네요.
Getters and Setters
note :
이 규칙은 저장 프로퍼티 뿐만 아니라 연산 프로퍼티 특성에도 적용됩니다. 저장 프로퍼티에 대한 명시적인 getter 및 setter를 작성하지 않더라도, Swift는 저장프로퍼티의 백업 저장소에 접근 할 수 있도록, 암시적 getter 및 setter를 합성합니다. 연산 프로퍼티의 명시적 setter와 동일한 방식으로, 이 합성된 setter의 접근 수준을 변경하려면 fileprivate (set), private (set) 및 internal (set)을 사용하세요.
자..예제를 보도록 하겠습니다. TrackedString이라는 구조체를 하나 만들건데, 이 TrackedString은 해당 문자열이 수정된 횟수를 추적하는 구조체입니다.
struct TrackedString {
private(set) var numberOfEdits = 0
var value: String = "" {
didSet {
numberOfEdits += 1
}
}
}
TrackedString구조체는 value라는 저장프로퍼티를 하나 가지고 있죠? 초기값은 "". 즉 빈 문자열이에요. (didSet이라는 프로퍼티 옵저버가 붙었다고 연산프로퍼티거나 그런게 아닙니다!!)
그리고 바뀌고 난 직후에 numberOfEdits을 1증가시켜주네요.
자.. TrackedString과 value라는 저장프로퍼티는 일단 아무런 접근 수준을 지정하지 않았으니...기본적으로 internal 접근 수준을 가지고 있겠죠? 근데!! numberOfEdits앞에는 특이하게 private(set)이 붙은 것을 알 수 있습니다. 이게 무슨뜻이냐!!!!!!!! 이 numberOfEdits은 TrackedString구조체 코드 내에서만 설정(set)할 수 있다는 것을 의미합니다. 하지만 set!!!만이 private죠. 즉 읽기는 구조체 정의 외부에서도 접근이 가능하다는 것이죠. 자.. TrackedString은 기본적으로 internal이니 같은 모듈안에서 접근이 가능하겠네요. 즉 TrackedString이 정의 된 같은 소스파일이 아니더라도 접근이 가능합니다.
적하는 구조체입니다.
var trackedString = TrackedString()
자..이게 가능하겠죠?
var trackedString = TrackedString()
print(trackedString.numberOfEdits)
numberOfEdits을 읽는 작업도 역시 가능할거에요. 하지만
trackedString.numberOfEdits = 10 //Error! Cannot assign to property: 'numberOfEdits' setter is inaccessible
이렇게 직접 값을 넣는 set작업을 하려고 하면 에러가 나게 됩니다. 왜냐?? set작업은 private으로, TrackedString 구조체 정의 내에서만 set작업을 할 수 있기 때문이죠.
만약 numberOfEdits이 internal(set)으로 되어있었다면, 위 작업도 가능했겠죠?
하지만, 필요한 경우, getter및 setter에 대해 명시적인 접근 수준을 지정할 수 있어요.
public struct TrackedString {
public private(set) var numberOfEdits = 0
public var value: String = "" {
didSet {
numberOfEdits += 1
}
}
public init() {}
}
자..이게 또 무슨말인가 싶죠? 갑자기 private(set)앞에 public이 붙었네요;;;
(여기서 퀴즈. 구조체를 public으로 하고 아무 접근 지정자를 안붙히면 뭐라고 그랬죠? ==> internal)
이제 이 TrackedString이라는 구조체를 외부모듈에서 접근 할 수 있다는 거네요!
**외부 모듈**
var trackedString = TrackedString()
이렇게요! 그리고 value도 public으로 접근수준이 지정되었으니
var trackedString = TrackedString()
trackedString.value = "Zedd"
이것도 가능해질테고(원래 외부 모듈에서는 안됐었죠? internal이었으니까) 역시나 numberOfEdits도 get이 가능해집니다! 왜냐???
public private(set) var numberOfEdits = 0
public 이기 때문...set만!!! set작업만!!!!!!!!private이기때문에 TrackedString구조체 안에서 변경을 해야합니다.
그리고... private(get)..처럼 안에 get이 들어가는 건 없습니다 ㅎㅎ..
오직 setter만이 getter보다 더 낮은(더 제한적인) 접근 수준을 가질 수 있어요.
private internal(set) var numberOfEdits = 0 //Error!
이런거 된다?
안된다 ㅇㅇ setter가 getter보다 더 높은 접근 수준을 가졌기 때문이죠.
Initializers
사용자 정의 이니셜라이저는 이니셜라이저 하는 타입보다 작거나(더 제한적인) 같은 접근 수준을 지정할 수 있습니다.
유일한 예외는 required initializers입니다. required initializers는 자신이 속한 클래스와 동일한 접근 수준을 가져야합니다.
Default Initializers
Default Initializers에서 설명한대로(클래스에서 초기값이 있으면 이니셜라이저가 없어도 된다는거), Swift는 모든 프로퍼티에 기본값을 제공하고, 최소한 하나의 이니셜라이저를 제공하지 않는 클래스 또는 구조체에 대해, 기본 이니셜라이저를 제공합니다.
기본 이니셜라이저는 해당 타입이 public으로 정의되어있지 않으면, 초기화 하는 타입과 동일한 접근 수준을 같습니다.
public으로 정의된 타입의 경우, 기본 이니셜라이저는 internal로 간주됩니다.
다른 모듈에서 사용 할 때, 파라미터가 없는 이니셜라이저로 초기화 할 수 있게 하려면, 타입 정의의 일부로 public 이니셜라이저를 명시적으로 제공해야합니다.
Default Memberwise Initializers for Structure Types
Protocols
private protocol myProtocol{
func hello()
}
이렇게 됐을 때, hello()는 반드시 private입니다. 다른 접근 수준을 지정하면 오류가 나요!
Protocol Inheritance
Protocol Conformance
디게 어렵게 써있지만 간단합니다. public class가 internal프로토콜을 준수하는 코드를 봅시다.
internal protocol myProtocol{
func someMethod()
}
public class MyClass : myProtocol{
public init(){}
func someMethod() {
}
}
protocol이 internal이니 someMethod요구사항도 internal접근 수준을 가지겠네요.
위에서 "타입의 각 프로토콜 요구사항 구현은 최소한 internal이어야합니다. "라고 그랬죠? 저 someMethod라는 메소드는 internal보다 더 제한적인 접근 수준을 가질 수 없다는 뜻이 됩니다. 즉, open, public, internal까지만 접근수준을 지정할 수 있다는 것이죠.
예를 하나 더 보면,
fileprivate protocol MyProtocol{
func hello()
}
class MyType : MyProtocol{
public func hello() {
}
}
이렇게 될 수 있다는 것이죠. file-private보다 더 제한적인 private를 제외한 모든 접근 지정자가 사용가능합니다.
Extensions
extension기능을 사용하여 프로토콜 준수를 추가하는 경우, extension기능에 명시적인 접근 수준 수정자를 제공 할 수는 없습니다.
대신 프로토콜 자체의 접근 수준은, extension내에서 각 프로토콜 요구사항 구현에 대한 기본 접근 수준을 제공하는 데 사용됩니다.
Private Members in Extensions
protocol SomeProtocol {
func doSomething()
}
다음과 같이 extension을 사용하여 프로토콜 준수를 추가할 수 있습니다.
struct SomeStruct {
private var privateVariable = 12
}
extension SomeStruct: SomeProtocol {
func doSomething() {
print(privateVariable)
}
}
이건 원래..Swift3이었으면 안됐을 텐데..Swift4에서 가능한 일이죠?
Generics
Type Aliases
public protocol SomeProtocol { }
private typealias myType = SomeProtocol // OK
fileprivate protocol SomeProtocol { }
public typealias myType = SomeProtocol //Error!
프로토콜로 했지만, 클래스나 구조체나 다 똑같이 적용됩니다.
오른쪽에 오는 타입의 접근 수준이 왼쪽보다 더 제한적이거나 같아야합니다.
난 분명히;;file-private로 타입을 만들었는데!! 그보다 덜 제한적인거로 타입 별칭(Type Aliase)을 줄 수는 없겠죠?
와...접근제어가 이렇게 많은 내용을 담고 있을 줄이야...제가 처음 아는 내용들도 많았습니당 :)
접근제어를 이해하는데 도움이 꼭 되었으면 좋겠네요 XD
'Swift' 카테고리의 다른 글
Swift ) 왕초보를 위한 Codable - CodingKey (5) | 2018.01.23 |
---|---|
Swift ) NSCountedSet (2) | 2018.01.22 |
Swift ) Sequences와 Lazy (2) | 2018.01.16 |
Swift ) Inheritance(상속) (7) | 2018.01.15 |
Swift ) Access Control(접근 제어) - (1) (4) | 2018.01.13 |
- swift tutorial
- 스위프트 문법
- Swift
- swift 공부
- github
- swift3
- iOS delegate
- ios 13
- 피아노
- np-complete
- 스위프트
- Combine
- Xcode
- 회고
- 제이슨 파싱
- Git
- IOS
- SwiftUI
- WKWebView
- swift sort
- FLUTTER
- fastlane
- actor
- UIBezierPath
- WidgetKit
- WWDC
- Accessibility
- swift delegate
- swift array
- np-hard
- Total
- Today
- Yesterday