티스토리 뷰

배열이 아닌 struct에서 []로 접근했을 때 동작(?)하길래 훑어본 subscript..

 

우선 subscript 부터 알아보자

 

https://jusung.gitbook.io/the-swift-language-guide/language-guide/12-subscripts

 

서브스크립트 (Subscripts) - The Swift Language Guide (한국어)

클래스, 구조체 그리고 열거형에서 스크립트를 정의해 사용할 수 있습니다. 서브스크립트란 콜렉션, 리스트, 시퀀스 등 집합의 특정 멤버 엘리먼트에 간단하게 접근할 수 있는 문법입니다. 서브

jusung.gitbook.io

 

 

다 알아보았습니다

 

 

저는 여기서 몇 개 발췌하고 배열이 아닌 struct에 []로 접근했을 때, 동작을 알아보겠습니다.

 

 

서브스크립트란?

ㄴ콜렉션, 리스트, 시퀀스 등 집합의 특정 멤버 엘리먼트에 간단하게 접근할 수 있는 문법입니다.

 

 

한 마디로 평소에 우리가 배열 접근할 때!

array[1] 이런식으로 [ ] '대괄호' 를 사용했는데요. 이 문법 자체를 "서브스크립트 subscript" 라고 합니다.

 

배열(Array) 인스턴스의 특정 엘리먼트는 someArray[index]문법으로,

사전(Dictionary) 인스턴스의 특정 엘리먼트는 someDictionary[key]로 접근할 수 있습니다.

 

이 글의 핵심은 >> 하나의 타입에 여러 서브스크립트를 정의할 수 있고 오버로드(Overload)도 가능합니다.  << 사실 여기에 있는데요.

 

subscript(index: Int) -> Int {
    get {
        // 적절한 반환 값
    }
    set(newValue) {
        // 적절한 set 액션
    }
}

서브스크립트의 set에 대한 인자 값을 따로 지정하지 않으면 기본 값(default value)로 newValue를 사용합니다. 읽기 전용으로 선언하려면 get, set을 지우고 따로 지정하지 않으면 get으로 동작하게 되서 읽기 전용으로 선언됩니다.

subscript(index: Int) -> Int {
    // 적절한 반환 값
}

위는 읽기 전용 subscript 입니다.

 

읽기 전용 + struct 로 확인 할 수 있는, 제가 찾던 예시가 위에 첨부된 링크에 있었는데요.

struct TimesTable {
    let multiplier: Int
    subscript(index: Int) -> Int {
        return multiplier * index
    }
}
let threeTimesTable = TimesTable(multiplier: 3)
print("six times three is \(threeTimesTable[6])")
// "six times three is 18" 출력

threeTimesTable이 배열이 아닌데 [6]이 가능한가 ? --> 가능. 왜냐면 [ ] 이 대괄호 자체가 subscript의 문법임 ㅇㅇ

[ ] 는 배열 접근시에만 사용하는게 아님. 배열 접근시 [ ] 도 사실은 subscript 인거임

 

그래서 threeTimesTable[6] 하면 이 6이라는 숫자가 재정의된 subscript(index: Int)의 index로 들어가게 되고,

그 return 값으로 처음 초기화시 지정해준 multiplier 3과 곱해져 18 이라는 Int 값으로 나오게 됩니다.

 

 

이제 여기에 설정자의 접근수준이 추가된 예시를 보겠습니다.

사실 이 코드에서는 접근수준이 아무 .. 효과(?)가 없습니다. 왜냐면 오류날법한 코드는 다 빼놔서.. 사실 의미가 없음

public struct SomeType {
    private var count: Int = 0
    
    public var publicStoredProperty: Int = 0
    
    public private(set) var publicGetOnlyStoredProperty: Int = 0
    
    internal var internalComputedProperty: Int {
        get {
            return count
        }
        set {
            count += 1
        }
    }
    
    internal private(set) var internalGetOnlyComputedProperty: Int {
        get {
            return count
        }
        set {
            count += 1
        }
    }
    
    public subscript() -> Int {
        get {
            return count
        }
        set {
            count += 1
        }
    }
    
    public internal(set) subscript(some: Int) -> Int {
        get {
            return count * some
        }
        set {
            count = some
        }
    }
}

var someInstance: SomeType = SomeType()

print(someInstance.internalComputedProperty) //0
someInstance.internalComputedProperty = 100
print(someInstance.internalComputedProperty) //1

print(someInstance[]) //1
someInstance[] = 100
print(someInstance) //SomeType(count: 2, publicStoredProperty: 0, publicGetOnlyStoredProperty: 0)
print(someInstance[], someInstance[10], someInstance[100]) //2 20 200
someInstance[100] = 1
print(someInstance) //SomeType(count: 100, publicStoredProperty: 0, publicGetOnlyStoredProperty: 0)

이 코드의 출력값은

0
1
1
SomeType(count: 2, publicStoredProperty: 0, publicGetOnlyStoredProperty: 0)
2 20 200
SomeType(count: 100, publicStoredProperty: 0, publicGetOnlyStoredProperty: 0)

이렇게 됩니다.

 

조,,금,, 복잡하게 보일 수 있지만 출력값 하나씩 보겠습니다.

 

우선

print(someInstance.internalComputedProperty) // 0

 

internalComputedProperty는 getter 에서 count 값을 돌려보내주고 있기 때문에

초기값인 0을 보내주고 있습니다 -> 이해 완.

 

두 번째,

someInstance.internalComputedProperty = 100
print(someInstance.internalComputedProperty) //1

 

그 다음, internalComputedProperty에 100을 줬는데요.

이러면 internalComputedProperty의 setter가 동작합니다.

setter에서는 count+=1 을 하고 있죠. 준 값이 100이든 999든 9999든 관심없고 count만 1 올려주면 됩니다.

100, 999, 9999, 어쩌구 값은 날아가게 됩니다(?)

 

그래서 다시 internalComputedProperty를 print 했을 때는

0에서

100이라는 숫자를 이용해서 setter 접근 -> count+=1 됨

따라서 1 출력

 

여기까지 이해하셨을까요 ?

 

다음,

print(someInstance[]) //1

 

struct에 []를 붙임 = struct의 subscript를 부름.

이렇게 사고가 흘러가야합니다. 배열이라고 자꾸 생각하지말고 그냥 subscript를 부르려면 []를 써야한다고 생각해!!

그래서 someInstance에 []를 붙임으로서 someInstance의 subscript가 불려집니다.

 

근데 이 SomeType의 subscript에는 두 종류가 있는데요.

하나는 그냥,

하나는 some이라는 이름으로 Int값이 하나 더 들어오는 subscript

 

얘는 추가적인 Int값을 [] 사이에 넣어주지 않았으므로 위 코드의 그냥 subscript() -> Int 가 불리게 됩니다.

그리고 그 subscript는 count를 리턴해주기로 되어있죠.

그래서 출력값이 아까 count값 그대로인 1이 되게 됩니다.

 

그리고 다음,

someInstance[] = 100
print(someInstance) //SomeType(count: 2, publicStoredProperty: 0, publicGetOnlyStoredProperty: 0)

 

someInstance[] = 100

이 말은 즉슨

 

someInstance[] : someInstance의 subscript의 

= 100 :  100을 들고 setter를 부를게

 

라는 말로,

setter에 가면 근데. 또 단순히 count+=1 만 합니다. 그래서 다시 100은 그냥 날라가고

count는 2가 되죠.

 

그렇게 하고 현재 someInstance를 출력해보면

SomeType(count:2, publicStoredProperty: 0, publicGetOnlyStoredProperty: 0) 이라는 구조체 형태임을 알 수 있게 됩니다.

 

print(someInstance[], someInstance[10], someInstance[100]) //2 20 200

 

얘도 볼게요.

얘는 셋 다 []가 붙어있네요. --> subscript 부름

첫 번째는 아까 설명한 것처럼 추가적인 Int값을 [] 사이에 넣어주지 않았으므로 위 코드의 그냥 subscript() -> Int 가 불리게 됩니다.

단순히 return count 즉, 2가 나오겠죠.

 

두 번째는 [] 사이에 10이라는 Int를 전달해줬습니다. 따라서 subscript() -> Int가 아닌!!

subscript(some: Int) -> Int 얘가 불리게 됩니다. 그리고 10이라는 숫자는 자연히 some이라는 이름으로 getter에 들어가겠죠.

getter를 보면 return count * some 으로 2*10 = 20이라는 Int값이 나오게 됩니다.

 

세 번째도 같은 맥락으로 some=100, count*some = 2*100 = 200

이 출력됩니다.

 

이제..마지막...

someInstance[100] = 1
print(someInstance) //SomeType(count: 100, publicStoredProperty: 0, publicGetOnlyStoredProperty: 0)

 

someInstance[100] = 1

이 말을 해석해보겠습니다.

 

someInstance[100] : somInstance의 subscript(some: Int) -> Int 얘를 부를게. 그리고 some은 100이야

= 1 : 1이라는 숫자를 들고 setter로 갈게

 

setter로 갑니다.

그랬더니 count = some 이라고 하네요. 그러면 이제 count는 some = 100 이 됩니다.

들고간 1이라는 숫자는 ? 전혀 상관이 없게 되죠. 왜냐? 1을 쓰겠다고 안 했으니까. 우선 "들고" setter로 가봤더니 안 씀. 그냥 단순히 그런거임.

 

그래서 print(someInstance) 로 내 사고가 맞는지 확인해보았더니

 

SomeType(count: 100, publicStoredProperty: 0, publicGetOnlyStoredProperty: 0)

 

짠.

count가 100이 된 모습으로 반겨주고 있었습니다..

 

그럼 끝!!

댓글