티스토리 뷰

반응형

안녕하세요 :) 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

코드의 모든 엔티티(뒷 부분에서 설명할테지만, 몇가지 예외가 있음)는 명시적 접근 수준을 직접 지정하지 않으면, 기본 접근 수준은 internal입니다. 
따라서 많은 경우, 코드에 명시적인 접근 수준을 지정 할 필요가 없습니다.


Access Levels for Single-Target Apps

간단한 Single-Target App을 작성할 때는 대게 App내에서 자체적으로 코드를 입력하기 때문에, App모듈 외부에서 코드를 사용할 필요가 없습니다. 
internal의 기본 접근 수준은 요구사항과 이미 일치합니다.(internal은 내부 모듈에서만 접근할 수 있었죠?) 따라서, 사용자 지정 접근 수준을 지정할 필요가 없습니다. 그러나 App의 모듈에 포함된 다른 코드에 구현 세부 정보를 숨기기 위해, 코드 일부를 fileprivate 또는 private로 표기할 수 있습니다.


Access Levels for Frameworks

Framework를 개발 할 때 , Framework를  가져오는 App과 같은 다른 모듈에서 접근 할 수 있도록 해당 Framework에 대해, public-facing interface를 open 또는 public으로 표시합니다. 이 public-facing interface(공용 인터페이스)는 Framework의 application programming interface(API)입니다.

note:
Framework의 모든 내부 구현은 여전히 internal접근 수준을 사용할 수 있으며, Framework의 내부 코드를 숨기려는 경우, fileprivate 또는 private로 표시할 수 있습니다. 
Framework의 API에 포함시키려는 엔티티를 open또는 public엔티티로 표시해야합니다.


Access Levels for Unit Test Targets

단위테스트(Unit Test)타겟이 있는 App을 작성할 때는 테스트 할 수 있도록 App의 코드를 모듈에서 사용할 수 있어야 합니다. (다른 모듈로 인식이 되나 봅니다.)
기본적으로 open 또는 public으로 표시된 엔티티만 다른 모듈에서 접근 할 수 있습니다. 그러나 단위 테스트 타켓이 모든 internal 엔티티에 대해 접근 할 수 있습니다. 이 경우, @testable 속성을 표시해주고, 테스트가 활성화 된 상태에서 해당 제품 모듈( product module )의 모든 internal 엔티티에 접근 할 수 있습니다. (한마디로,  @testable속성을 표시해주면, internal은 단위테스트 모듈쪽에서 접근이 가능하다는 소리)





Access Control Syntax

저번에 어떻게 접근 지정자를 붙히는지도 봤었는데!! 


  1. 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() {}



이렇게 앞에, 내가 원하는 접근 수정자를 붙히면 된답니다. 클래스 앞, 구조체 앞, 열거형 앞, ...등등



  1. class SomeInternalClass {}              // implicitly internal

    let someInternalConstant = 0            // implicitly internal



이렇게되면 암시적으로 internal이라고 그랬죠?




Custom Types

사용자 정의 타입에 대한 명시적인 접근 수준을 지정하려면 타입을 정의하는 시점에 이를 수행하세요.
그런 다음, 접근 유형이 허용하는 모든곳에서 새로 만든 타입을 사용 할 수 있습니다. 
예를들어, file-private클래스를 정의하는 경우, 해당 클래스는 file-private클래스가 정의 된 소스파일에서, 프로퍼티타입 또는 함수 매개변수 또는 리턴타입으로만 사용할 수 있습니다.

타입의 접근 제어 수준은 해당 타입의 구성원(프로퍼티, 메소드, 이니셜라이저, 서브스크립트)의 기본 접근 수준에도 영향을 줍니다. 
타입의 접근 수준을 file-private 또는 private으로 정의하면, 해당 구성원의 기본 접근 수준도 file-private 또는 private가 됩니다. 


  1. 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이 되는 것이 아니라요. 



예를들어, 이런 코드가 있었다고 생각해볼게요. 


  1. public class PublicClass {

         init(){}

         func someMethod() {}

    }



이 PublicClass는 접근수준이 public이기때문에, 내부모듈은 물론이고, 외부모듈에서 접근이 가능하죠. 

하지만 이니셜라이저와 메소드에는 아무 접근 수준을 명시적으로 지정해주지 않았어요. 

====> 이러면 뭐다?????기본적으로 internal이 된다 ㅇㅇㅇ


즉!!!! 외부모듈에서 이 PublicClass가 인식은 되지만!!!!!  이니셜라이저가 기본적으로 internal이기때문에, 외부모듈에서는 이 PublicClass의 인스턴스를 만들 수가 없습니다.



이렇게요. 저 클래스의 접근 수준이 open이어도 똑같은 일이 발생합니다.

그럼 어떻게 해야한다? 명시적으로 접근 수준을 지정해주면 된다!!


  1. 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

튜플 타입에 대한 접근 수준은 해당 튜플에 사용된 모든 타입 중 가장 제한적인 접근 수준입니다. 
예를들어, internal 접근 권한과 private 접근 권한이 있는 두가지 타입의 튜플을 작성하는 경우, 해당 복한 튜플 타입의 접근 수준은 private가 됩니다. 
튜플 타입은 클래스, 구조체, 열거형 및 함수의 방식에서 독립형 정의가 없습니다. 튜플 타입의 접근 레벨은 튜플 타입이 사용될 때 자동으로 추론되며, 명시적으로 지정할 수 없습니다. 



Function Types

함수 타입에 대한 접근 수준은 함수의 매개변수 타입 및 리턴 타입에 대해 가장 제한적인 접근 수준으로서 계산됩니다. 
함수의 계산된 접근 수준이 컨텍스트 기본값과 일치하지 않으면, 접근 정의 수준을 함수 정의의 일부로 명시적으로 지정해야합니다.

예를들어, 이런 함수가 있었다고 할 때, 

  1. func someFunction() -> (SomeInternalClass, SomePrivateClass) {

            // function implementation goes here

        }


이 함수의 접근 수준은 매개변수나 리턴타입에서 가장 제한 적인 접근수준으로서 계산된다고 그랬죠? 일단 매개변수가 없고, 리턴타입이 튜플타입인데, 튜플타입도 위에서 구성원 중 가장 제한적인 접근수준으로 계산이 된다고 했으니..리턴타입은 private가 되겠고, 

그러면 이 함수의 접근 수준은 private겠네요!

하지만 이렇게 이 함수의 접근수준이 private로 계산이 되었는데, 앞에 접근 지정자를 설정하지 않거나 (=internal), public 접근 수준을 지정하는 것은 유효하지 않기 때문에!! 


  1. private func someFunction() -> (SomeInternalClass, SomePrivateClass) {

            // function implementation goes here

        }



이렇게 앞에 private를 붙혀주는 것이 올바릅니다. 



Enumeration Types

열거형의 개별 case는 자동으로 해당 열거형과 동일한 수준의 접근수준을 받습니다. 
개별 열거 case에 대해 다른 접근 수준을 지정 할 수 없습니다.


  1. public enum CompassPoint{

        private case North //Error!

    }



이렇게 되면 error라는 것이죠. North는 무조건 public이어야합니다. 



Raw Values and Associated Values

열거형 정의의 Raw Value또는 Associated Value값에 사용되는 타입은 열거형 접근 수준 이상의 접근 수준을 가져야합니다. internal 접근 수준이 있는 열거형의 Raw Value타입으로 private를 사용할 수 없습니다. 
(Raw Value또는 Associated Value값에 사용되는 타입은 열거형의 접근 수준보다 덜 제한적이어야 한다는 뜻)


Nested Types

private타입 내에서 정의된 중접 타입은 자동으로 private접근 레벨을 가집니다. 
file-private타입 내에 정의도니 중첩 타입도 마찬가지로, 자동으로 file-private접근 수준을 가집니다. 
open, public타입 또는 internal타입에 정의 된 중첩 타입은 자동으로 internal접근 레벨을 가집니다. 

  1. open class SomeOpenClass{

        public init() {}

        struct myStruct{

            var name : String

        }

    }



이렇게 되어있다면, 외부 모듈에서 



이런게 불가능하다는 것이죠. 왜냐? myStruct는 internal 접근 수준이기 때문입니다. 




Subclassing

현재 접근 컨텍스트에서 접근 할 수 있는 클래스를 서브클래싱 할 수 있습니다.
서브클래스는 슈퍼클래스보다 높은 접근 레벨을 가질 수 없습니다. 예를들어, internal 슈퍼클래스로, public서브클래스를 작성 할 수 없습니다. 


  1. class SomeInternalClass { }

    public class PublicClass : SomeInternalClass{ //Error!


    }




이렇게 하면 오류가 난다는 것이죠. 당연하죠? <Inheritance(상속)>글에서도 배웠듯이 서브클래스는 슈퍼클래스의 멤버에 접근이 가능해지게 되는데, 이 서브클래스가 더 덜 제한적인 접근 수준을 가지게 되면, 슈퍼클래스 멤버에 접근이 안될수도 있으니까요. 그럼 반대로 서브클래스가 더 제한적인 접근 수준은 가질 수 있다..라는게 되죠.


  1. public class A {


    }

    internal class B: A {


    }


이렇게요 :)


또한, 특정 접근 컨텍스트에서 볼 수 있는 클래스 멤버(메소드, 프로퍼티, 이니셜라이져 또는 서브스크립트)를 override(재정의) 할 수 있습니다.


override를 사용하면, 상속된 클래스 멤버가 슈퍼클래스 버전보다 더 쉽게 접근 할 수 있습니다. (그러니까 덜 제한적인 접근 수준을 가질 수 있다는 소리)



  1. public class A {

        fileprivate func someMethod() {}

    }


    internal class B: A {

        override internal func someMethod() {}

    }


someMethod()는 분명 슈퍼클래스에는 fileprivate였는데...서브클래스에서 override될 때는 internal이 될 수 있다는 의미죠.


서브클래스 멤버가 서브클래스 멤버보다 액세스 권한이 더 제한적인 슈퍼 클래스 멤버를 호출하는것도 유효합니다.


  1. 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

상수, 변수 또는 프로퍼티는 해당 타입보다 덜 제한적일 수 없습니다. 
예를들어, private타입에 public프로퍼티를 작성하는것은 유효하지 않습니다.
마찬가지로 서브스크립트는 인덱스 타입 또는 리턴 타입보다 덜 제한적일 수 없습니다. 
상수, 변수, 프로퍼티 또는 하위스크립트가 private타입을 사용하는 경우, 상수, 변수, 프로퍼티 또는 서브스크립트도 private로 표시해야합니다. 

 예를들어, 


  1. class B{

        init() {}

    }


    public var instance = B() //Error!


이럴 수는 없다는거죠. 

B클래스는 internal인데, B보다 덜 제한적인 public접근 수준의 변수는 만들 수 없습니다. 


  1. var instance = B()

    fileprivate var instance = B()

    private var instance = B()


위 3가지 경우만 가능하다는 것이죠. 

그리고 만약  B가 file-private거나 private접근 수준이면, 꼭!! 



  1. fileprivate var instance = B()

    private var instance = B()



앞에 이렇게 file-private나 private를 붙혀줘야합니다. 


궁금해서 몇개 해봤는데, 


  1. private class B{

        init() {}

    }


    fileprivate var instance = B()


이건 되네요. 



Getters and Setters

상수, 변수, 프로퍼티 및 서브스크립트에 대한 getter 및 setter는 자동으로 상수, 변수, 프로퍼티 또는 서브스크립트에 속한것과 동일한 접근 수준을 받습니다.

setter에게 해당 getter보다 낮은 접근 수준을 부여하여, 해당 변수, 프로퍼티 또는 서브스크립트의 읽기/쓰기 범위를 제한 할 수 는 있습니다.
var또는 subscript introducer 앞에 fileprivate (set), private (set) 또는 internal (set)을 작성하여 더 낮은 액세스 수준을 할당합니다.

note : 

이 규칙은 저장 프로퍼티 뿐만 아니라 연산 프로퍼티 특성에도 적용됩니다. 저장 프로퍼티에 대한 명시적인 getter 및 setter를 작성하지 않더라도, Swift는 저장프로퍼티의 백업 저장소에 접근 할 수 있도록, 암시적 getter 및 setter를 합성합니다. 연산 프로퍼티의 명시적 setter와 동일한 방식으로, 이 합성된 setter의 접근 수준을 변경하려면 fileprivate (set), private (set) 및 internal (set)을 사용하세요.



자..예제를 보도록 하겠습니다. TrackedString이라는 구조체를 하나 만들건데, 이 TrackedString은 해당 문자열이 수정된 횟수를 추적하는 구조체입니다. 


  1. 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이 정의 된 같은 소스파일이 아니더라도 접근이 가능합니다.

 적하는 구조체입니다. 


  1. var trackedString = TrackedString()


자..이게 가능하겠죠? 


  1. var trackedString = TrackedString()

    print(trackedString.numberOfEdits)


numberOfEdits을 읽는 작업도 역시 가능할거에요. 하지만 


  1. trackedString.numberOfEdits = 10 //Error! Cannot assign to property: 'numberOfEdits' setter is inaccessible


이렇게 직접 값을 넣는 set작업을  하려고 하면 에러가 나게 됩니다. 왜냐?? set작업은 private으로, TrackedString 구조체 정의 내에서만 set작업을 할 수 있기 때문이죠. 

만약 numberOfEdits이 internal(set)으로 되어있었다면, 위 작업도 가능했겠죠?


하지만, 필요한 경우, getter및 setter에 대해 명시적인 접근 수준을 지정할 수 있어요.


  1. public struct TrackedString {

        public private(set) var numberOfEdits = 0

        public var value: String = "" {

            didSet {

                numberOfEdits += 1

            }

        }

        public init() {}

    }



자..이게 또 무슨말인가 싶죠? 갑자기 private(set)앞에 public이 붙었네요;;;

(여기서 퀴즈. 구조체를 public으로 하고 아무 접근 지정자를 안붙히면 뭐라고 그랬죠? ==> internal)


이제 이 TrackedString이라는 구조체를 외부모듈에서 접근 할 수 있다는 거네요!


**외부 모듈**

  1.  var trackedString = TrackedString()


이렇게요! 그리고 value도 public으로 접근수준이 지정되었으니 


  1. var trackedString = TrackedString()


    trackedString.value = "Zedd"


이것도 가능해질테고(원래 외부 모듈에서는 안됐었죠? internal이었으니까) 역시나 numberOfEdits도 get이 가능해집니다! 왜냐???


  1. public private(set) var numberOfEdits = 0


public 이기 때문...set만!!! set작업만!!!!!!!!private이기때문에  TrackedString구조체 안에서 변경을 해야합니다.


그리고... private(get)..처럼 안에 get이 들어가는 건 없습니다 ㅎㅎ..

오직 setter만이 getter보다 더 낮은(더 제한적인) 접근 수준을 가질 수 있어요. 


  1.  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

구조체의 저장프로퍼티 중 하나라도 private인 경우, 구조체의 기본 memberwise 이니셜라이저는 private로 간주됩니다. 마찬가지로, 구조체의 저장 프로퍼티 중 하나라도 file-private인 경우, 이니셜라이저는 file-private입니다. 그렇지 않으면 이니셜라이저의 기본 접근 수준은 internal입니다.

위의 기본 이니셜라이저에서와 같이, public구조체 타입을 다른 모듈에서 사용 할 때, memberwise이니셜라이저로 초기화하려면, 해당 타입 정의의 일부로 public  memberwise 이니셜라이저를 직접 제공해야합니다. 


Protocols

프로토콜 타입에 명시적 접근 수준을 지정하려면, 프로포토콜을 정의하는 시점에 접근수준을 지정하세요.
이렇게 하면 특정 접근 컨텍스트 내에서만 채택 할 수 있는 프로토콜을 만들 수 있습니다. 

프로토콜은 정의 내의 각 요구사항에 대한 접근 수준은 프로토콜과 동일한 접근 수준으로 자동 설정됩니다. 프로토콜 요구 사항을 지원하는 프로토콜과 다른접근 수준으로 설정 할 수 는 없습니다. 이렇게 하면 모든 프로토콜의 요구 사항이 프로토콜을 채택하는 모든 타입에서 볼 수 있습니다 .

  1. private protocol myProtocol{

         func hello() 

    }


이렇게 됐을 때, hello()는 반드시 private입니다. 다른 접근 수준을 지정하면 오류가 나요!



Protocol Inheritance

기존 프로토콜을 상속하는 새 프로토콜을 정의하면, 새 프로토콜은 상속받은 프로토콜과 동일한 접근 수준을 가질 수 있습니다. 예를들어, internal 프로토콜을 상속한 public프로토콜을 작성 할 수 는 없습니다. 
-> 클래스도 그랬듯이 프로토콜도 똑같나봐요!! 클래스에서도 서브클래스에 슈퍼클래스보다 덜 제한적인 접근 수준을 줄 수는 없었죠?


Protocol Conformance

타입의 타입 자체보다 낮은 접근 수준(더 제한적인)을 갖는 프로토콜을 따를 수 있습니다.
예를들어, 다른 모듈에서 사용할 수 있지만, internal프로토콜에 대한 준수가 internal 프로토콜 정의 모듈 내에서만 사용될 수 있는 public타입을 정의할 수 있습니다. 
타입이 특정 프로토콜을 준수하는 컨텍스트는, 타입의 접근 수준과 프로토콜의 접근 수준 중 최소값입니다.
타입이 public이지만, 준수하는 프로토콜이 internal프로토콜인 경우, 해당 타입에 대한 프로토콜 적합성도 internal입니다.

프로토콜을 따르기 위해, 타입을 작성하거나 extension할 때는 각 프로토콜 요구사항과 타입 구현에 대한 해당 프로토콜에 대한 타입 적합성과 최소한 동일한 접근 수준이 있는지 확인해야 합니다. 예를들어, public타입이 internal프로토콜을 준수하는 경우, 타입의 각 프로토콜 요구사항 구현은 최소한 internal이어야합니다. 

디게 어렵게 써있지만 간단합니다. public class가 internal프로토콜을 준수하는 코드를 봅시다.  


  1. internal protocol myProtocol{

        func someMethod()

    }

    public class MyClass : myProtocol{

        public init(){}

        func someMethod() {

       

        }

    }



protocol이 internal이니 someMethod요구사항도 internal접근 수준을 가지겠네요. 

위에서 "타입의 각 프로토콜 요구사항 구현은 최소한 internal이어야합니다. "라고 그랬죠? 저 someMethod라는 메소드는 internal보다 더 제한적인 접근 수준을 가질 수 없다는 뜻이 됩니다. 즉, open, public, internal까지만 접근수준을 지정할 수 있다는 것이죠. 

예를 하나 더 보면,


  1. fileprivate protocol MyProtocol{

          func hello()

    }

    class MyType : MyProtocol{

        public func hello() {

          

        }

    }



이렇게 될 수 있다는 것이죠. file-private보다 더 제한적인 private를 제외한 모든 접근 지정자가 사용가능합니다. 




Extensions

클래스, 구조체 또는 열거형을 사용할 수 있는 모든 접근 컨텍스트에서, 클래스, 구조체 또는 열거형을 extension할 수 있습니다.
extension에 추가된 모든 타입멤버는 원래 타입과 동일한 접근 수준을 가집니다. public 또는 internal타입을 extension하는 경우, 추가하는 모든 새 타입 구성원의 기본 접근 수준은 internal입니다. file-private타입을 확장하는 경우, 추가하는 모든 새 타입 구성원은 기본적으로 file-private접근 수준입니다. 
private타입을 확장하면, 추가하는 새 타입구성원의 기본 접근 수준은 private입니다.

또는 extension을 명시적 접근 수준 수정자(ex. private extension)로 표시하여, extension 내에서 정의 된 모든 구성원에 대해 새 기본 접근 수준을 설정 할 수 있습니다. 이 새 기본값은 개별 타입 멤버의 extension내에서 여전히 무시될 수 있습니다. 


extension기능을 사용하여 프로토콜 준수를 추가하는 경우, extension기능에 명시적인 접근 수준 수정자를 제공 할 수는 없습니다. 

대신 프로토콜 자체의 접근 수준은, extension내에서 각 프로토콜 요구사항 구현에 대한 기본 접근 수준을 제공하는 데 사용됩니다.



Private Members in Extensions

extension되는 클래스, 구조체 또는 열거형과 같은 파일에 있는 extension은 extension타입의 코드가 원래 타입의 일부로 작성된 것 처럼 동작합니다.
결과적으로 다음을 수행할 수 있습니다.

1. 원래 정의해서 private멤버를 선언하고, 동일한 파일의 extension에서 해당 멤버에 접근합니다.
2. 한 extension에서 private멤버를 정의하고, 같은 파일의 다른 extension에서 해당 멤버에 접근합니다.
3. extension에서 private멤버를 선언하고, 동일하 파일의 원래 선언에서 해당 멤버에 접근합니다.

이 동작은 타입에 비공개 엔티티가 있는지 여부에 관계없이, extension을 사용하여 코드를 구성하는 것과 같은 방식으로 사용 할 수 있음을 의미합니다.
예를들어, 다음과 같은 간단한 프로토콜이 주어진다고 생각해보겠습니다. 


  1. protocol SomeProtocol {

        func doSomething()

    }


다음과 같이 extension을 사용하여 프로토콜 준수를 추가할 수 있습니다. 


  1. struct SomeStruct {

        private var privateVariable = 12

    }


    extension SomeStruct: SomeProtocol {

        func doSomething() {

            print(privateVariable)

        }

    }


이건 원래..Swift3이었으면 안됐을 텐데..Swift4에서 가능한 일이죠? 




Generics

Generic타입 또는 Generic함수에 대한 접근 수준은 Generic타입 또는 Generic함수 자체의 접근 수준과 타입 매개변수에 대한 모든 타입 제약 조건의 접근 수준 중 최소 수준입니다. 


Type Aliases

정의한 모든 Type Aliase는 접근 제어를 위해 distinct type으로 처리됩니다. Type Aliase는 접근 수준이 Aliase인 타입의 접근 수준보다 작거나 같을 수 있습니다. 
예를들어, private Type Aliase는 private, file-private, internal, public, 또는 open 타입의 Aliase를 지정할 수 있지만, public Type Aliase는 nternal, file-private, 또는 private Type Aliase를 지정할 수 없습니다. 
이 규칙은 프로토콜 준수를 충족시키는데 사용되는 associated types에 대한 type aliase에도 적용됩니다. 

굉장히 어려워보이지만, 간단합니다. 

  1. 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