본문 바로가기

Apple/Swift

Swift 03 - 구조체(struct)

반응형

❑  Struct(구조체 : structure)

\Swift에서 String, Int 등과 같이 타입을 만들 수도 있는데, 가장 일반적인 방법은 구조체(struct)를 선언하는 것이다. struct에는 자체변수와 상수, 함수를 준다음 원하는대로 생성하고 사용할 수 있다.

아래 그림 처럼 String에 커맨드 + 클릭을 하면 String 타입에 대한 정의를 볼 수 있고....

String 타입이 struct임을 확인할 수 있다..

 

❑  Property, Stored Property

구조체에 내부에 선언한 변수를 프로퍼티(property)라고 한다. 구조체로 타입을 정의하였으면 구조체의 인스턴스를 만들어 사용할 수 있다. Tottenham struct에 선언한 playerName, backNumberr과 같이 값을 저장하는 프로퍼티를 stored property(저장 프로퍼티)라고 한다. 

// 토트넘 선수들의 이름과 등번호를 저장하는 struct
struct TottemhamPlayer {
    var playerName: String
    var backNumber: Int
}

var son = TottenhamPlayer(playerName: "heung min son", backNumber: 7)
print(son.playerName)

// output
// heung min son

 

son이라는 변수와 playerName, backNumber 변수를 모두 만들었기 때문에 일반적인 변수처럼 값을 바꿀 수 있다.

son.playerName = "harry kane"
son.backNumber = 10

print(son.playerName)
print(son.backNumber)

// output
// harry kane
// 10

 

❑  Computed Property(연산 프로퍼티)

위에 만들었던 Tottenham struct에 다른 종류의 property를 사용할 수 있다. 

struct TottenhamPlayer {
    var playerName: String
    var backNumber: Int
    
    // 추가
    var isAttacker: Bool
    
    // position은 타입은 일반적인 변수와 같이 문자열로 선언되었지만 isAttacker의 프로퍼티에 따라 다른 값을 반환한다.
    var position: String {
    	if isAttacker: {
        	return "\(playerName)은 공격수이다."
        } else {
        	return "\(playerName)은 공격수가 아니다."
        }
    }
}

// TottemhamPlayer struct의 인스턴스 사용
let son = TottenhamPlayer(playerName: "손흥민", backNumber: 7, isAttacker: true)
print(son.position)

//output
//손흥민은 공격수이다.

 

❑  Property Observer(프로퍼티 옵저버 -  didSet)

Property Observer(프로퍼티 옵저버)를 사용하면 프로퍼티 변경 전후에 코드를 실행할 수 있다. 아래와 같이 struct를 선언하고 인스턴스를 만들어 주고 실행한다고 했을 때,

struct Goal {
    var playerName: String
    var goalCount: Int
}

var goal = Goal(playerName: "손흥민", goalCount: 0)
goal.goalCount = 2
goal.goalCount = 7
goal.goalCount = 10

goalCount 변수에 값이 할당될때(set)마다 메시지가 출력되게 하기 위해 disSet이라는 property observer를 사용하면 코드가 변경될 때 마다 원하는 동작을 실행하게 할 수 있다. 아래 예시 처럼  didSet을 사용한 것 만으로 다른 함수 생성 없이 값이 들어갈 때마다 didSet안에 있는 print함수가 실행된다.

struct Goal {
    var playerName: String
    var goalCount: Int {
        didSet {
            print("\(playerName)이 이번 시즌에 \(goalCount)골을 넣었습니다.")
        }
    }
}

var goal = Goal(playerName: "손흥민", goalCount: 0)
goal.goalCount = 2
goal.goalCount = 7
goal.goalCount = 10
goal.goalCount = 10 // 위와 같은 값인 10을 한 번 더 넣어줘도 print 함수가 호출된다.

// output
// 손흥민이 이번 시즌에 2골을 넣었습니다.
// 손흥민이 이번 시즌에 7골을 넣었습니다.
// 손흥민이 이번 시즌에 10골을 넣었습니다.
// 손흥민이 이번 시즌에 10골을 넣었습니다.

 

❑  Struct 내부에 함수 작성

struct 프로퍼티 뿐만 아니라 함수도 작성할 수 있다. 작성된 함수는 필요할 때 struct의 property를 사용할 수 있다. struct 내부의 함수들은 메서드(method)라고 하고 func 키워드로 작성한다. 메서드는 struct에 속해 있기 때문에 struct의 인스턴스에 대해 메서드를 호출한다.

struct Salary {
    var weeklySalary: Int

    func yearSalaryCalculator() -> Int {
        let yearSalary = weeklySalary * 52
        print("선수의 주급은 \(weeklySalary)원이고 연봉은 \(yearSalary)입니다.")
        return yearSalary * 52
    }
}

let playerSalary = Salary(weeklySalary: 3000000)
playerSalary.yearSalaryCalculator()

 

❑  Mutating Method

struct에 변수(var) 프로퍼티가 있지만 struct는 상수로 생성되기 때문에 프로퍼티는 바뀔 수 없다. struct는 상수이고 모든 struct의 프로퍼티 또한 상수이다. Swift는 struct가 만들어질 때 상수로 만들어졌는지 변수로 만들어졌는지 알 수가 없기 때문에 기본적으로 안전한 접근방법을 택한다. Swift에서는 mutating 메서드를 사용하지 않는 한 프로퍼티를 변경하는 메서드를 사용할 수 없다. 

예를 들면, 아래와 같은 코드를 작성하면 에러가 난다.

struct BestPlayer {
    var playerName: String
    
    func sonnyIsTheBest() {
        playerName = "sonny"
    }
}

//error
// Cannot assign to property: self is immutable

 mutating 메서드를 사용하면 struct의 내부 메서드에서 변수 프로퍼티에 접근해 값을 변경할 수 있다.

struct BestPlayer {
    var playerName: String
    
    //mutating 메서드
    mutating func sonnyIsTheBest() {
        playerName = "sonny"
    }
}


var whosTheBest = BestPlayer(playerName: "harry kane") // playerName 프로퍼티에 입력한 harry kane 문자열이
whosTheBest.sonnyIsTheBest() // playerName을 변경하는 메서드에 의해
print(whosTheBest.playerName) // sonny로 변경

// output
// sonny

 

❑  Initializer(이니셜라이저, 생성자 : init)

생성자라고 불리는 initializer는 struct 내부에서 특별히 사용되는 메서드이다. struct를 작성할 때 각 프로퍼티에 대한 값을 입력하도록 하는 initializer를 요청한다. initializer는 init이라는 키워드로 작성한다.

struct BestPlayer {
    var playerName: String
    
    init() {
        playerName = "이름없음"
        print("아직 베스트 플레이어는 미정입니다.")
    }
}

var sonny = BestPlayer() // playerName 프로퍼티에 값을 입력하지 않아도 인스턴스 생성이 된다.

// output
// 아직 베스트 플레이어는 미정입니다.

 

❑  self

 

메서드 내부에는 self라는 특수한 상수를 가지는데, 이 상수는 현재 사용중인 struct의 인스턴스를 가리킨다. self 값은 프로퍼티와 동일한 파라미터 이름을 가진 initializer를 작성할 때 유용하다. 예를 들면 아래의 코드에서 playerName 프로퍼티를 갖는 Goal struct에서는 name 파라미터를 갖는 initializer를 작성한다면, self는 initializer의 파라미터와 프로퍼티를 구분하는데 도움을 준다. 즉, self.playerName은 struct의 프로퍼티를 참조하고 initializer의 playerName은 init의 파라미터를 참조한다는 뜻.

struct Goal {
    var playerName: String
    
    init(playerName: String) {
        print("\(playerName)의 골!!!")
        // self.playerName은 struct의 프로퍼티를, 
        // playerName은 init메서드의 파라미터를 참조한다.
        self.playerName = playerName
    }
}

 

❑  Static Property

Static property(스태틱 프로퍼티: 정적 프로퍼티)

Static property는 생성된 모든 struct의 인스턴스에서 공유하는 프로퍼티이다. static property를 가져올 때 struct의 인스턴스가 아니라 struct 자체에 속하는 것이기 때문에 호출할 때 struct의 이름인 Player를 사용해야 static property의 값을 가져올 수 있다.

struct Player {
    var playerName: String
    static var playerCount: Int = 0
    
    init(playerName: String) {
        self.playerName = playerName
        Player.playerCount += 1 // struct의 인스턴스 생성 시 마다 플레이어 수를 추가함.
    }
}

let sonny = Player(playerName: "sonny")
print("토트넘의 선수는 \(Player.playerCount)명 이다.")
let kane = Player(playerName: "kane")
print("토트넘의 선수는 \(Player.playerCount)명 이다.")

// output
// 토트넘의 선수는 1명 이다.
// 토트넘의 선수는 2명 이다.

 

❑  접근제어(Access Control)

struct 내부의 값을 직접 읽거나 변경하는 것을 막기 위해 접근 제어를 통해 이를 방지 할 수 있다. 연봉은 중요한 문제이기 때문에 한번 인스턴스를 생성하면 외부에서 쓸 수 없게 해야한다.

struct PlayersSalary {
    var playerName: String
    private var playerSalary: Int
    
    init(playerName: String, playerSalary: Int) {
        self.playerName = playerName
        self.playerSalary = playerSalary
    }
}

let sonny = PlayersSalary(playerName: "sonny", playerSalary: 200000000)
print(sonny.playerName)
sonny.playerSalary = 10 // 누군가 sonny의 주급을 10달러로 변경하려 했다면?, private 선언으로 인해 값을 쓸 수 없다.
print(sonny.playerSalary)


// output
// sonny

// error
// playerSalary is inaccesible due to 'private' protection level 
// playerSalary is inaccesible due to 'private' protection level

읽기 위해서는 메서드를 생성해 읽는 방법을 선택할 수 있다.

struct PlayersSalary {
    var playerName: String
    private var playerSalary: Int
    
    init(playerName: String, playerSalary: Int) {
        self.playerName = playerName
        self.playerSalary = playerSalary
    }
    
    // playerSalary 프로퍼티를 읽기위한 메서드를 선언하면 playerSalary를 읽을 수 있다.
    func readSalary() {
        print( "\(playerName)선수의 주급은 \(String(playerSalary))달러 입니다.")
    }
}

let sonny = PlayersSalary(playerName: "sonny", playerSalary: 200000000)
sonny.readSalary()

// output
// sonny선수의 주급은 200000000달러 입니다.

 

반응형

'Apple > Swift' 카테고리의 다른 글

Swift 05 - 프로토콜(Protocol)  (0) 2022.03.12
Swift 04 - 클래스(class)  (0) 2022.03.12
Swift 02 - 클로저(Closure)  (0) 2022.03.12
Swift 01 - 함수(function)  (0) 2022.03.11
firebase install error/fail  (0) 2020.04.26