관리 메뉴

김종권의 iOS 앱 개발 알아가기

[iOS - swift 공식 문서] 26. Access Control (접근제한) 본문

swift 공식 문서

[iOS - swift 공식 문서] 26. Access Control (접근제한)

jake-kim 2021. 7. 28. 23:30

Access Control

  • 다른 소스 파일 또는 모듈의 코드에서 코드 부분에 대한 엑세스를 제한
  • 코드의 구현 세부 정보를 숨기고 해당 코드에 엑세스하고 사용할 수 있는 기본 인터페이스를 지정 가능

모듈과 소스파일 구분

  • 모듈: 배포 단위
    • 프레임워크 또는 응용프래그램을 import로 다른 모듈로 가져올 수 있는 성격
  • 소스파일: 단순히 소스코드 파일이므로 소스 파일에서 여러 유형에 대한 access control 정의가 가능

Access level

open 모든 소스 파일에서 해당 level 접근 가능 + 모든 곳에서 서브클래싱 가능
public 모든 소스 파일에서 해당 level 접근 가능 + 같은 모듈 내에서만 서브클래싱 가능
internal (외부 모듈에서 코드 확인 불가) 같은 모듈 내에서만 접근 가능
fileprivate (외부 모듈에서 코드 확인도 불가) 같은 소스파일 내에서만 접근 가능
private (외부 모듈에서 코드 확인도 불가) 클래스 내부에서만 접근 가능

디폴트가 internal인 이유

  • 일반적으로 단일 대상으로 특정 앱을 만드는데, 이 앱의 접근은 앱 모듈 외부에서 사용하게 만들 필요가 없기 때문
  • 단, 외부에서 해당 모듈의 코드를 비공개하고 싶은 경우 fileprivate, private 둘 중 하나로 설정해야 가능

Unit test 관한 접근제한

  • 일반적으로 unit test에서는 다른 코드들을 open과 public만 접근 가능하지만, @testable 속성을 이용하면 테스트가 활성화된 모든 코드 접근 가능

블록 내부에서의 접근제한자

  • 바깥 블록에서 선언한 access control의 값보다 블록 안에 있는 access control보다 private쪽에 가깝다면 default는 바깥 블록에서 설정한 값으로 자동 세팅
public class SomePublicClass {                  // explicitly public class
    public var somePublicProperty = 0            // explicitly public class member
    var someInternalProperty = 0                 // implicitly internal class member
    fileprivate func someFilePrivateMethod() {}  // explicitly file-private class member
    private func somePrivateMethod() {}          // explicitly private class member
}

class SomeInternalClass {                       // implicitly internal class
    var someInternalProperty = 0                 // implicitly internal class member
    fileprivate func someFilePrivateMethod() {}  // explicitly file-private class member
    private func somePrivateMethod() {}          // explicitly private class member
}

fileprivate class SomeFilePrivateClass {        // explicitly file-private class
    func someFilePrivateMethod() {}              // implicitly file-private class member
    private func somePrivateMethod() {}          // explicitly private class member
}

private class SomePrivateClass {                // explicitly private class
    func somePrivateMethod() {}                  // implicitly private class member
}
  • 주의사항: 클래스에다 붙이는 접근 제한자의 의미는 조금 다른것을 주의
    • open class: 다른 모듈에서 해당 클래스를 상속, override 가능
    • public class: 다른 모듈에서 접근만 가능하고 상속, override 불가능
    • fileprivate class: 해당 클래스의 프로퍼티, 함수, 반환 타입 모두 fileprivate 
    • swift4.0부터는 private으로 선언해도 extension에서 접근 가능 (단, 같은 파일에서만 가능하고 다른 파일은 불가)
  • private 클래스로 선언해도 같은 파일내에서 해당 클래스 접근 가능
    • -> private 개념은 `블럭 내부`에서 접근 가능하다는 의미는 잘못되었고, `같은 블럭에 위치하는 `에서 모두 접근이 가능 
    • 주의: private class여도 내부에 internal 인스턴스 메소드가 있으면 접근 가능
// A.swift

private class A {
    func abc() {

    }
}

class B {
    func a() {
        let s = A() // private 클래스로 선언해도 같은 파일내에서 해당 클래스 접근 가능
        s.abc() // private class로 만든 객체를 통해 내부에 internal인 인스턴스 메소드가 있으면 접근 가능
    }
}
  • private class vs fileprivate class 차이점?: 최상위 fileprivate class는 private class와 동일
    • fileprivate class, private class의 차이는 nested 클래스에서 사용할때 영향력을 발휘
private class A {

    fileprivate class C {

    }

    private class D {

    }

    func abc() {

    }
}

class B {
    func a() {
        let s = A()
        s.abc()
        
        let c = A.C() // 가능
        let d = A.D // error
    }
}

getter, setter

  • setter에  해당 getter보다 낮은 액세스 수준을 부여하여 범위 제한
    • set만 따로 괄호쳐진 키워드를 사용
fileprivate(set) var a = 0
private(set) var b = 0
internal(set) var c = 0
  • stored property 접근 시 외부에서는 읽기만 되고 쓰기는 해당 property가 아닌 computed-property를 이용하도록 하는 방법
// 외부에서는 읽기 전용(internal) stored property 만드는 예시

struct TrackedString {
    /// 읽기는 internal 속성 (생략된 형태)
    private(set) var numberOfEdits = 0
    var value: String = "" {
        didSet {
            numberOfEdits += 1
        }
    }
}
// 읽기는 public으로 설정하는 예시

public struct TrackedString {
    public private(set) var numberOfEdits = 0
    public var value: String = "" {
        didSet {
            numberOfEdits += 1
        }
    }
    public init() {}
}

초기화 (Initializer)

  • 사용자 지정 init은 초기화하는 유형보다 작거나 같은 액세스 수준을 할당 가능
    • 주의: required init은 해당 이니셜라이저에 속한 클래스와 동일한 액세스 수준을 가지지 않으면 컴파일에러 발생

Extension

  • class, struct, enum을 사용할 수 있는 모든 액세스 컨텍스트에서 모두 extension 가능
  • extension으로 추가된 모든 member는 기존에 있던 default access level을 따름
  • fileprivate 유형을 extension > 새 유형 멤버도 모두 fileprivate
  • extension으로 private 프로퍼티에 접근할 수 있는 것을 주의
// extension에서 private property 접근이 가능

class Temp {
    private var a = 0
}

extension Temp {
    func printPrivateProperty() {
        print(a)
    }
}

Typealias

  • typealias는 별칭을 지정하려는 유형의 access level보다 작거나 같도록 설정 가능

* 참고

https://docs.swift.org/swift-book/LanguageGuide/AccessControl.html

Comments