구조체

구조체는 구조화된 데이터와 기능을 만듦으로써 하나의 사용자 정의 데이터타입을 만들어준다. 구조체의 인스턴스는 값 타입이다.

struct 구조체이름 {
	프로퍼티(=멤버변수)와 메서드(=멤버함수)
}

구조체는 따로 생성자를 만들지 않아도 Swift가 알아서 자동으로 멤버변수들을 참조하여 생성자를 준비시켜준다!

struct User {
	// 멤버변수
	var nickname: String
	var age: Int
	
	// 멤버함수
	func info() {
		print("\(nickname) is \(age)-year old")
	}
}

var user = User(nickname: "Gunter", age: 35)
user.nickname
// "Gunter"

user.nickname = "Albert"
user.nickname
// "Albert"

user.info()
// "Albert is 35-year old"

클래스

클래스도 구조체와 마찬가지로 구조화된 데이터와 기능을 만듦으로써 하나의 사용자 정의 데이터타입을 만들어준다. 단, 구조체와 달리 클래스의 인스턴스는 참조 타입이다.

class 클래스 이름 {
	프로퍼티(=멤버변수)와 메서드(=멤버함수)
}

클래스도 구조체와 동일한 방법으로, 그것의 인스턴스가 멤버변수와 멤버함수에 자유롭게 접근하는 것이 가능하다.

class Dog {
	var name: String = ""
	var age: Int = 0
	
	func intro() {
		print("The name is \(name), age is \(age)")
	}
}

var dog = Dog()
dog.name = "Coco"
dog.age = 2
dog.intro()
// "The name is Coco, age is 2"

초기화

클래스, 구조체 또는 열거형의 인스턴스를 사용하기 위한 조치를 초기화(Initializer)라고 한다. 흔히 생성자(constructor)라고 부르는 그것이다.

init(매개변수: 타입, ...) {
	프로퍼티(=멤버변수) 초기화
	인스턴스 생성시 필요한 설정 내용
}

초기화는 반드시 init 구문을 사용해야 하는데, 이때 매개변수를 다양하게 할 때마다 생성자도 그만큼 다양하게 존재할 수 있다.

class User {
	var nickname: String
	var age: Int
	
	// 멤버변수 nickname, age를 모두 사용하는 생성자
	init(nickname: String, age: Int) {
		self.nickname = nickname
		self.age = age
	}
	
	// 멤버변수 age만 사용하는 생성자
	init(age: Int) {
		self.nickname = "Albert"
		self.age = age
	}
}

var user = User(nickname: "Gunter", age: 35)
user.nickname
// "Gunter"
user.age
// 35

var user2 = User(age: 23)
user2.nickname
// "Albert"
user2.age
// 23

한편, init 구문과 정반대의 효과를 내는 구문(de-initializer)도 존재한다. 이를 deinit 구문이라고 하며, 인스턴스가 메모리에 해제되기 직전에 호출되어 생성자로 만들어졌던 인스턴스와 그 멤버변수 값이 null이 되면 이것들을 모두 회수해버린다. 따라서 이러한 deinit 구문은 메모리를 '참조'하는 클래스-인스턴스 관계에서만 활용되며, 프로그래머가 원하는 정리작업을 구현해준다.

class User {
	var nickname: String
	var age: Int
	
	init(nickname: String, age: Int) {
		self.nickname = nickname
		self.age = age
	}
	
	init(age: Int) {
		self.nickname = "Albert"
		self.age = age
	}
	
	// de-initializer
	deinit {
		print("de-init user!")
	}
}

var user3: User? = User(age: 10)
user3 = nil
// de-init user!

프로퍼티

클래스, 구조체 또는 열거형 등에 관련된 값으로, 인스턴스의 속성을 의미한다.

저장 프로퍼티

클래스, 구조체 또는 열거형 안에서 선언된 상수 또는 변수이다. 구조체 안에서 상수로 선언된 저장 프로퍼티는 인스턴스에서 그 값을 바꿀 수 없다. 또한, 마찬가지로 상수로 선언된 인스턴스는 저장 프로퍼티들의 값을 바꿀 수 없다.

struct Dog {
	// 구조체의 저장 프로퍼티 name, gender
	var name: String
	let gender: String
}

var dog = Dog(name: "Coco", gender: "male")
print(dog)
// Dog(name: "Coco", gender: "male")
dog.gender = "female"
// Cannot assign to property: 'gender' is a 'let' constant

let dog2 = Dog(name: "Kuku", gender: "male")
dog2.name = "Tata"
// Cannot assign to property: 'dog2' is a 'let' constant

반면, 클래스는 참조타입이므로 상수로 선언된 인스턴스라고 할지라도 저장 프로퍼티가 변수형이라면, 인스턴스가 그 저장 프로퍼티의 값을 바꿀 수 있다. 물론, 구조체와 마찬가지로 애초에 상수로 선언된 저장 프로퍼티에 대해서는 인스턴스가 임의로 그 값을 바꿀 수 없다.

class Cat {
	// 클래스의 저장 프로퍼티 name, gender
	var name: String
	let gender: String
	
	init(name: String, gender: String) {
		self.name = name
		self.gender = gender
	}
}

let cat = Cat(name: "Latte", gender: "male")
cat.name = "Jason"
print(cat.name)
// "Jason"

연산 프로퍼티

Getter와 Setter를 활용하여 특정 연산을 실행하는 결과값이며, 특히, 인스턴스에서 연산 프로퍼티만 실행할 경우 자동으로 getter 구문의 내용이 실행된다.

struct Stock {
	var averagePrice: Int
	var quantity: Int
	
	// 구조체의 연산 프로퍼티
	var purchasePrice: Int {
		get {
			return averagePrice * quantity
		}
		set(newPrice) {
			averagePrice = newPrice / quantity
		}
	}
}

var stock = Stock(averagePrice: 2300, quantity: 3)

// 연산 프로퍼티 Getter를 활용한 경우
stock.purchasePrice
// 6900

// 연산 프로퍼티 Setter를 활용한 경우
stock.purchasePrice = 3000
stock.averagePrice
// 1000

프로퍼티 옵져버

프로퍼티가 변경되는 것을 주시하여 그에 따른 결과를 리턴하는 것으로, willSet 옵져버와 didSet 옵져버가 대표적이다.

class Account {
	var credit: Int = 0 {
		// 소괄호 이름 지정
		willSet {
			print("잔액이 \(credit)원에서 \(newValue)원으로 변경예정")
		}
		didSet {
			print("잔액이 \(oldValue)원에서 \(credit)원으로 변경완료")
		}
	}
}

var account = Account()
account.credit = 1000
// "잔액이 0원에서 1000원으로 변경예정"
// "잔액이 0원에서 1000원으로 변경완료"

타입 프로퍼티

인스턴스 생성 없이 객체 내 프로퍼티에 접근 가능하게 해주는 것으로, 프로퍼티를 애초에 static 키워드를 사용하여 선언해주면 된다

struct SomeStructure {
	// 스토어
	static var storedTypeProperty = "Some value"
	// 컴퓨티드
	static var computedTypeProperty: Int {
		return 1
	}
}

SomeStructure.computedTypeProperty
// 1
SomeStructure.storedTypeProperty
// "Some value"

SomeStructure.storedTypeProperty = "hello"
SomeStructure.storedTypeProperty
// "hello"

클래스와 구조체의 차이

공통점

  1. 값을 저장할 프로퍼티를 선언 가능
  2. 함수적 기능을 하는 메서드 선언 가능
  3. 내부 값에 .을 사용하여 접근 가능
  4. 생성자를 사용해 초기 상태를 설정 가능
  5. extension을 사용하여 기능확장 가능
  6. Protocol을 채택하여 기능설정 가능

차이점

  1. 클래스는 참조타입이고 구조체는 값타입
  2. 클래스는 참조타입이므로 힙 영역에 저장되고, 따라서 ARC로 메모리를 관리한다. 반면에 구조체는 값타입이므로 스택 영역에 저장되고, 따라서 ARC로 메모리 관리 되지 않는다.
  3. 클래스는 참조타입이므로 같은 클래스의 인스턴스를 여러 개의 변수에 할당한 뒤 값을 변경시켜 버리면 그 모든 변수들에게 영향을 준다. 반면에 구조체는 값타입이므로 같은 구조체를 여러 개의 변수에 할당한 뒤 값을 아무리 변경시켜도 다른 변수에 영향을 주지 않는다. 구조체를 새로운 변수에 할당할 때마다 새로운 구조체가 생성되는 것이다.
  4. 클래스는 상속 가능하고 구조체는 상속 불가
  5. 클래스는 타입 캐스팅을 통해 런타임에서 클래스 인스턴스의 타입을 확인할 수 있다.
  6. 클래스는 deinit을 사용하여 클래스 인스턴스의 메모리 할당을 해제할 수 있다.
class SomeClass {
	var count: Int = 0
}

struct SomeStruct {
	var count: Int = 0
}

var class1 = SomeClass()
var class2 = class1
var class3 = class1
class3.count = 2
class1.count
// 2

var struct1 = SomeStruct()
var struct2 = struct1
var struct3 = struct1
struct2.count = 3
struct3.count = 4
struct1.count
// 0
profile
전직 시스템엔지니어, 현직 데이터엔지니어, 하지만 하고 싶은건 iOS 개발

0개의 댓글