Swift의 Associatedtype & Typealias

June·2023년 3월 17일
0

Swift

목록 보기
15/18
post-thumbnail

Associatedtype & Typealias - 프로토콜에서의 제네릭

구조체나 클래스에서처럼 제네릭으로 프로토콜을 선언할 수는 없다.
시도하면 "프로토콜은 제네릭 파라미터를 허용하지 않는다"는 에러가 표시된다.
이 때에는 associatedtype 키워드를 사용하면 된다.

associatedtype

protocol GameScore{
    
    // 1. associatedtype을 선언해 모든 타입이 될 수 있음을 나타냄
    associatedtype TeamScore    // String, Int, Array 등 무엇이든 될 수 있음
    
    func calculateWinner(teamOne: TeamScore, teamTwo: TeamScore) -> String
}

struct FootballGame: GameScore {
    
    // 2. 프로토콜을 준수할 때 타입 선언을 위해 typealias를 사용함
    typealias TeamScore = Int
    
    // 이 함수는 TeamScore의 모든 타입을 사용해, 어느쪽이 이기는지 계산함
    func calculateWinner(teamOne: TeamScore, teamTwo: TeamScore) -> String {
        if teamOne > teamTwo {
            return "Team one wins"
        } else if teamOne == teamTwo {
            return "The teams tied"
        }
        return "Team two wins "
    }
}

struct AssociatedTypeIntro: View {
    
    var game = FootballGame()
    private var team1 = Int.random(in: 1..<50)
    private var team2 = Int.random(in: 1..<50)
    @State private var winner = ""
    
    var body: some View {
        
        VStack(spacing: 20) {
            
            Text("애플 문서를 보면 associated type이 많이 사용되는 것을 볼 수 있는데, 이것은 프로토콜 채택시 지정하는 유형의 플레이스홀더이다.")
            
            HStack(spacing: 40) {
                Text("Team One: \(team1)")
                Text("Team Two: \(team1)")
            }
            
            Button("Calculate Winner") {
                winner = game.calculateWinner(teamOne: team1, teamTwo: team2)
            }
            
            Text(winner)
            
            Spacer()
        }
        .font(.title)
    }
}

typealias

struct FootballGame: GameScore {
    
//    typealias TeamScore = Int   // 아래에 타입을 명시적으로 설정할 경우 불필요함
    
    func calculateWinner(teamOne: Int, teamTwo: Int) -> String {
        if teamOne > teamTwo {
            return "Team one wins"
        } else if teamOne == teamTwo {
            return "The teams tied"
        }
        return "Team two wins "
    }
}

typealias를 사용해 프로토콜의 associated types에 대한 타입을 설정할 수 있지만, 항상 사용할 필요는 없다.

struct SoccerGame: GameScore {
    typealias TeamScore = String
    
    func calculateWinner(teamOne: String, teamTwo: String) -> String {
        // ....
    }
}

TeamScore는 모든 타입으로 설정할 수 있다.


타입 제약 설정

제네릭과 마찬가지로, 특정 프로토콜과 일치하는 특정 타입만 사용하도록 타입 제약을 설정할 수 있다.

protocol Teams {
    
    // 타입 제약 설정함
    // Dictionary, Range, Set와 같은 모든 타입의 컬렉션이 될 수 있음
    associatedtype Team: Collection
    
    var team1: Team { get set }
    var team2: Team { get set }
    
    func compareTeamSizes() -> String
}

struct WeekendGame: Teams {
    
    // 여기서는 Team타입 정의에 type alias를 사용하지 않는 대신, Swift가 이해하고 타입을 설정할 String배열을 명시적으로 사용함
    var team1 = ["참가자1", "참가자2"]
    var team2 = ["참가자1", "참가자2", "참가자3"]
    
    func compareTeamSizes() -> String {
        if team1.count > team2.count {
            return "Team1의 인원이 더 많음"
        } else if team1.count == team2.count {
            return "두 팀의 인원수가 같음"
        }
        return "Team2의 인원이 더 많음"
    }
}

struct AssociatedType_Constraints: View {
    @State private var comparison = ""
    private let weekendGame = WeekendGame()
    
    var body: some View {
        VStack(spacing: 20) {
            Text("제네릭과 동일한 방식으로 연관 타입에 대한 제네릭 타입을 제한할 수 있음")
            
            Button("Evaluate Teams") {
                comparison = weekendGame.compareTeamSizes()
            }
            
            Text(comparison)
            
            Spacer()
        }
        .font(.title)
    }
}
profile
안다고 착각하지 말기

0개의 댓글