Swift에서도 구조체와 클래스가 있습니다. 오늘은 Struct
인 구조체에서 알아봅시다. 구조체는 사용자 정의 데이터 타입인데요. 구조체를 사용하면 서로 관계가 있는 것을 묶어서 표현할 수 있게 됩니다. 관계있는 것들은 어떻게 표현할 수 있을까요? 바로 프로퍼티와 메서드를 이용하여 만들어 줄 수 있습니다. Struct는 다른 기본 데이터 타입과 마찬가지로 Value Type
입니다. 즉 값이 Copy
가 되는 것이죠. 차근차근 알아봅시다.
Struct
struct [구조체의 이름] {
// 프로퍼티...
// 메서드...
}
struct Position {
let x: Int
let y: Int
}
Struct
는 프로퍼티와 메서드를 사용하여서 만들 수 있다고 했습니다. Position
은 Int 타입 2개 x, y를 가지는 구조체입니다. 이 멤버 변수인 데이터를 프로퍼티라고 부릅니다.
Struc 생성
let myPosition: Position = Position(x: 10, y: 10)
var testPosition = myPosition;
//testPosition.x -> 10
//testPosition.y -> 10
testPosition.x = 5
testPosition.y = 8
정의한 구조체 이름이 구조체의 타입이 됩니다. 구조체를 생성할 때에는 swift 기본 데이터 타입과 동일하게 타입을 선언해주고 값을 할당해 주면 됩니다. 앞에서 구조체는 Copy가 된다고 했습니다. testPosition
에 myPosition
을 할당하게 되면 값만 복사됩니다. 따라서 testPosition의 값을 변경하더라도 기존 데이터의 변화가 없습니다. 나중에 알아볼 Class
와의 큰 차이점입니다.
Struc Method
struct Position {
let x: Double = 0
let y: Double = 0
// 거리를 구하는 Method
func distance(other: Position) -> Double {
let distanceX = (self.x - other.x)
let distanceY = (self.y - other.y)
return sqrt((distanceX * distanceX) + (distanceY * distanceY))
}
}
let myPosition: Position = Position(x: 10, y: 10)
var zero = Position(x: 0, y: 0)
myPosition.distance(other: zero) //구조체 Method 사용
구조체 내부에 메서드도 만들 수 있습니다. Position 구조체에 distance
를 만들어 다른 위치와의 거리를 구하는 기능을 구현했습니다. 구조체의 정의된 프로퍼티를 메서드에서 사용할 수 있습니다.
프로퍼티 (Property)
struct Position {
let x: Double = 0 // Stored Property
let x: Double = 0 // Stored Property
var description: String { //Computed Property
return "x : \(x), y : \(y)"
}
static let zero: Position = Position(x: 0, y: 0) //Type Property
}
let myPosition: Position = Position(x: 10, y: 10)
myPosition.description // 똑같은 방식으로 호출 할 수 있습니다.
// 결과 : "x : 10.0, y : 10.0"
Position.zero.description
// 결과 : "x : 0.0, y : 0.0"
프로퍼티는 크게 2가지가 있습니다. 값을 변수로 저장해서 가지고 있는 프로퍼티 Stored Property
와 직접 값을 저장하진 않고 저장된 정보를 이용해 계산된 값을 사용하는 Computed Property
입니다. Computed Property는 매번 접근할 때마다 다시 계산이 됩니다. Position 구조체의 description
이 Computed Property이며 기본적으로 읽기 전용입니다.
x, y의 값이 변하게 되면 description호출 시 새롭게 계산이 된 것을 볼 수 있습니다. 이 2가지 외에 Type Property
라는 것이 있습니다. C++에서도 static
을 많이 사용하고 있는데요. 바로 정적멤버변수
라고 불렸는데요. 구조체 타입의 고유 프로퍼티입니다. 구조체의 인스턴스와 상관이 없는 값이죠. 인스턴스의 수와 상관없이 하나만 존재하는 값입니다.
Position은 (0, 0)
원점이라는 타입 프로퍼티로 만들어 줄 수 있습니다. 타입 프로퍼티 호출을 할 때에는 인스턴스와 상관이 없기 때문에 구조체 타입을 이용해서 호출하면 됩니다. myPosition
으로 zero
를 호출하게 되면 에러가 발생합니다. (인스턴스와 무관한 프로퍼티)
Static member 'zero' cannot be used on instance of type 'Position'
set, get
struct Position {
var description: String {
get {
return "x : \(x), y : \(y)"
}
set { // 문자열을 받아서 x, y에 값을 할당 하는 setter 예시 코드...
// , 를 기준으로 잘라서 세팅
if let x = newValue.components(separatedBy: ",").first {
self.x = Double(x)!
}
if let y = newValue.components(separatedBy: ", ").last {
self.y = Double(y)!
}
}
}
}
var myPosition: Position = Position(x: 10, y: 10)
myPosition.description = "1, 2" // set을 만들지않으면 에러 발생합니다. (기본 읽기전용)
myPosition.x // --> 1
myPosition.y // --> 2
set, get의 의미는 많이 아실듯합니다. Computed Property에서는 set, get을 정의해 줄 수 있습니다. set을 정의해주면 프로퍼티 쓰기를 할 수 있게 됩니다. 코드를 보면 set에서 newValue가 있는데 이게 대입된 값"1, 2"
입니다. 앞에서 언급했듯이 읽기 전용일 경우 명시가 되어있지 않아도 get입니다. (option을 눌러서 변수를 확인해보면 볼 수 있습니다.)
Property Observers
struct Position {
var x: Double {
willSet {
print("willSet oldPosition x: \(x) --> newPosition x: \(newValue)")
}
didSet {
print("didSet oldPosition x: \(oldValue) --> newPosition x: \(x)")
}
}
var y: Double = 0
}
var myPosition: Position = Position(x: 10, y: 10)
myPosition.x = 5
// willSet oldPosition x: 10.0 --> newPosition x: 5.0
// didSet oldPosition x: 10.0 --> newPosition x: 5.0
프로퍼티에는 willSet
과 didSet
키워드가 있습니다. willSet의 경우 프로퍼티에 값이 대입되기 전에 호출, didSet은 프로퍼티에 값이 대입되고 나서 호출되는 코드입니다. 즉 프로퍼티 값의 변화를 확인할 수 있습니다. willSet이 먼저 호출되고 didSet이 호출되는 걸 볼 수 있습니다.
lazy Property
struct Position {
var x: Double = 0
var y: Double = 0
lazy var length: Double = {
print("calculate length")
return sqrt(x * x + y * y)
}()
}
var myPosition: Position = Position(x: 10, y: 10)
myPosition.length
// 결과 : calculate length
// 결과 : 14.142...
myPosition.x = 5
myPosition.y = 6
myPosition.length
// 결과 : 14.142...
lazy Property
는 인스턴스가 될 때에 생성되지 않고 프로퍼티를 실제로 호출하여 접근될 때에 생성이 되는 프로퍼티입니다. 필요하지만 굳이 바로 사용되지 않을 프로퍼티일 경우 사용할 수 있습니다. 만약 해당 프로퍼티의 비용이 클 경우 인스턴스 할 때부터 큰 비용이 필요합니다. 하지만 초기화되고 바로 사용되지 않거나 특정 유저 행동이 있을 때만 사용되어질 수도 있죠 즉 비용 낭비를 줄이기 위해서 사용됩니다.
특이한 점은 lazy는 처음 접근하여 사용될 때에 값을 메모리에 올리고 그 이후에는 참조해서 사용됩니다. 코드를 보게 되면 lazy
키워드를 사용하여서 length
길이를 구하는 프로퍼티를 만들었습니다. x, y 10으로 인스턴스 후 length
를 호출하면 "calculate length"
가 출력이 되고 값을 메모리에 저장하게 됩니다. x, y 값 변경 후 다시 호출하게 되면 코드가 실행되지 않고 메모리에 있는 값을 참조하게 됩니다.
Property vs. Method
프로퍼티와 메서드를 사용하는 기준입니다. 저는 C++을 계속 사용 해와서 프로퍼티의 기능들이 신기합니다. 프로퍼티는 내부 실행 로직이 있을 수 있지만 결국 저장된 값을 하나 반환하는 것이고, 메서드는 이름처럼 기능, 작업을 하는 것이죠. 따라서 복잡한 계산이나 연산에 다른 데이터가 필요한 경우에는 Method를 사용하면 됩니다. Setter가 필요하면 당연히 프로퍼티를 사용하면 됩니다. 물론 프로퍼티의 기능을 메서드로도 표현이 가능합니다.
swift의 구조체의 신기한 부분이 많아서 재밌게 공부했습니다. 드디어 다음에는 클래스를 알아볼 수 있겠네요!