09. 구조체와 클래스

JinStory77·2022년 5월 14일
0

Swift 공부_기초

목록 보기
9/11

구조체와 클래스

  • 구조체와 클래스는 프로퍼티와 메서드를 사용하여 구조화된 데이터와 기능을 가질 수 있다.
    • 구조체의 인스턴스는 값 타입
    • 클래스의 인스턴스는 참조 타입
  • 소스파일 하나에 여러개의 구조체와 여러 개의 클래스를 정의하고 구현해도 문제가 없다.
  • 중첩 함수와 마찬가지로 구조체 안에 구조체, 클래스 안에 클래스 등 중첩 타입의 정의 및 선언이 가능하다.


1. 구조체


1.1. 구조체 정의

  • struct 키워드로 정의한다.
  • 구조체를 정의한다 = 새로운 타입을 생성한다
    • 그렇기에 기본 타입 이름(Int, String, Bool 등)처럼 대문자 카멜케이스를 사용
    • 프로퍼티와 메서드는 소문자 카멜케이스를 사용
struct 구조체 이름 {
	프로퍼티와 메서드들
}
struct BasicInformation {
	var name: String
    var age: Int
}

1.2. 구조체 인스턴스의 생성 및 초기화

  • 구조체에 기본 생성된 이니셜라이저의 매개변수는 구조체의 프로퍼티 이름으로 자동지정된다.

  • BasicInformation 구조체의 인스턴스 생성 및 사용
struct BasicInformation {
    var name: String
    var age: Int
}
// String 타입인 name과 Int 타입인 age라는 저장 프로퍼티
// 프로퍼티 이름(name, age)으로 자동 생성된 이니셜라이저를 사용하여 구조체를 생성
var JinInfo: BasicInformation = BasicInformation(name: "Jin", age: 88)
JinInfo.age = 100       // 변경 가능
JinInfo.name = "Seba"   // 변경 가능

// 프로퍼티 이름(name, age)으로 자동 생성된 이니셜라이저를 사용하여 구조체 생성
let SebaInfo: BasicInformation = BasicInformation(name: "Seba", age: 99)
SebaInfo.age = 100       // 변경 불가 / 상수
JennyInfo.name = "Seba"   // 변경 불가 / Jenny 없음


2. 클래스


2.1. 클래스의 정의

  • class라는 키워드로 정의한다.
  • 스위프트의 클래스는 부모 클래스로부터 상속을 받을 수도, 상속 없이 단독으로 정의가 가능하다.
  • 구조체를 정의한다 = 새로운 타입을 생성한다
    • 그렇기에 기본 타입 이름(Int, String, Bool 등)처럼 대문자 카멜케이스를 사용
    • 프로퍼티와 메서드는 소문자 카멜케이스를 사용
  • 클래스의 인스턴스끼리 참조가 같은지 확인 할때 식별 연산자를 사용한다.
class 클래스 이름 {
	프로퍼티와 메서드들
}
  • 구조체와 정의하는 방법은 비슷하나, 클래스는 상속받을 수 있고 받을 때는 콜론(:) 후 부모클래스 이름을 명시한다.
class 클래스 이름: 부모클래스 이름 {
	프로퍼티와 메서드들
}
class Person {
	var height: Float = 0.0
    var weight: Float = 0.0
}
// Float 타입인 height와 weight 저장 프로퍼티가 있는 Person 클래스
// Person 클래스에서는 프로퍼티의 기본값이 지정되어 있으므로 전달 인자를 통해 따로 초깃값을 전달 해주지 않아도 된다.

2.2. 클래스 인스턴스의 생성과 초기화

class Person {
    var height: Float = 0.0
    var weight: Float = 0.0
}

var Jin: Person = Person()
Jin.height = 123.4
Jin.weight = 123.4

let Jenny: Person = Person()
Jenny.height = 123.4
Jenny.weight = 123.4
  • 인스턴스가 생성되고 초기화된 후(이니셜라이즈된 후) 프로퍼티 값에 접근하고 싶다면 마침표(.)를 사용하면 된다.
  • 구조체와 달리 클래스의 인스턴스는 참조 타입이므로 클래스의 인스턴스를 상수 let으로 선언해도 내부 프로퍼티 값 변경이 가능하다.

✏️ 인스턴스와 객체

  • 다른 프로그래밍 언어에서는 클래스의 인스턴스를 객체라 부른다. 스위프트에서도 틀린 것은 아니지만, 스위프트 공식 문서에는 좀 더 한정적인 인스턴스라고 부른다.

2.3. 클래스 인스턴스의 소멸

  • 클래스의 인스턴스는 참조 타입이므로 참조할 필요가 없을 때 메모리에서 해제된다. 이 과정을 소멸이라고 하며 소멸되기 직전 deinit이라는 메서드가 호출된다. 이렇게 호출된 deinit 메서드는 디이니셜라이저(Deinitializer)라고 부른다.
  • deinit 메서드는 클래스당 하나만 구현할 수 있으며, 매개변수와 반환 값을 가질 수 없다.
class Person {
    var height: Float = 0.0
    var weight: Float = 0.0
    
    deinit {
        print("Person 클래스의 인스턴스가 소멸됩니다.")
    }
}
var Jin: Person? = Person()
Jin = nil    //Person 클래스의 인스턴스가 소멸됩니다.


3. 구조체와 클래스의 차이

  • 같은 점

    • 값을 저장하기 위해 프로퍼티를 정의할 수 있다.
    • 기능 실행을 위해 메서드를 정의할 수 있다.
    • 서브스크립트 문법을 통해 구조체 또는 클래스가 갖는 값(프로퍼티)에 접근하도록 서브스크립트를 정의할 수 있다.
    • 초기화될 때 상태를 지정하기 위해 이니셜라이저를 정의할 수 있다.
    • 초기구현과 더불어 새로운 기능 추가를 위해 익스텐션을 통해 확장 가능하다.
    • 특정 기능을 실행하기 위해 특정 프로토콜을 준수할 수 있다.
  • 차이점

    • 구조체는 상속할 수 없다.
    • 타입캐스팅은 클래스의 인스턴스에만 허용된다.
    • 디이니셜라이저는 클래스의 인스턴스에만 활용할 수 있다.
    • 참조 횟수 계산은 클래스의 인스턴스에만 적용된다.
  • 정리

    • 구조체는 값 타입이고, 클래스는 참조 타입이다.(무엇이 전달되느냐 차이)
    • 함수의 전달인자로 값 타입의 값을 넘긴다면, 전달될 값이 복사되어 전달된다.
    • 함수의 전달인자로 참조 타입을 넘기면, 값을 복사하지않고 참조(주소)가 전달된다.
  • 구조체와 클래스의 차이(값 타입과 참조 타입의 차이)

// 예시 1
// 구조체로 만든 경우 (위 생략)
var iPhone1 = Phone(modelName: "iPhone 13", manufacturer: "Apple")
var iPhone2 = iPhone1
iPhone1.modelName = "iPhone 14"
print(iPhone2.modelName)
print(iPhone1.modelName)
// iPhone 13 / iPhone 14
// iPhone2는 iPhone1의 값을 '복사'했기에 이후 iPhone1의 값이 변경되어도 영향을 받지 않음. 

// 클래스로 만든 경우 (위 생략)
var jrDeveloper1 = iOSDeveloper(name: "John", hours: 8)
var jrDeveloper2 = jrDeveloper1
jrDeveloper1.name = "Billy"
print(jrDeveloper1.name)
print(jrDeveloper2.name)
// Billy / Billy
// jrDeveloper2는 jrDeveloper1을 '참조'했기에 이후 jrDeveloper1의 값이 바뀌면 jrDeveloper2의 값도 바뀜
// 예시 2
struct BasicInformation {
    let name: String
    var age: Int
}

var JinInfo: BasicInformation = BasicInformation(name: "Jin", age: 99)
JinInfo.age = 100

var friendInfo: BasicInformation = JinInfo
// JinInfo의 값을 복사하여 할당함

print("Jin`s age: \(JinInfo.age)")
      print("friend's age: \(friendInfo.age)")

friendInfo.age = 999

print("Jin's age: \(JinInfo.age)")
print("friend's age: \(friendInfo.age)")
// 999 - friendInfo는 JinInfo의 값을 복사해왔기 때문에 별개의 값을 가짐

class Person {
    var height: Float = 0.0
    var weight: Float = 0.0
}

var Jin: Person = Person()
var friend: Person = Jin

print("Jin's height: \(Jin.height)")
print("friend's height: \(friend.height)")

friend.height = 179
print("Jin's height: \(Jin.height)")
// 179 - friend는 Jin을 참조하기 때문 값이 변동됨

print("friend's height: \(friend.height)")
// 179 - 이를 통해 Jin이 참조하기 곳과 friend가 참조하는 곳 같음을 알 수 있음

func changeBasicInfo(_ info: BasicInformation) {
    var copiedInfo: BasicInformation = info
    copiedInfo.age = 1
}
func changePersonInfo(_ info: Person) {
    info.height = 155.3
}

// changeBaicsInfo(_:)로 전달되는 전달인자는 값이 복사되어 전달됨
// Jin이 참조하는 값들에 변화
changePersonInfo(Jin)
print("Jin's height : \(Jin.height)")    // 155.3


4. 구조체와 클래스 선택해서 사용하기

  • 구조체와 클래스는 새로운 데이터 타입을 정의하고 기능을 추가한다는 점이 같다
    • 하지만 구조체 인스턴스는 항상 값 타입, 클래스 인스턴스는 참조 타입이다.
  • 애플 가이드라인에서 다음 조건 중 하나 이상에 해당한다면 구조체를 사용하는 것을 권장한다.
    • 연관된 간단한 값의 집합을 캡슐화하는 것만이 목적일 때
    • 캡슐화한 값을 참조하는 것보다 복사하는 것이 합당할 때
    • 구조체에 저장된 프로퍼티가 값 타입이며 참보하는 것보다 복사하는 것이 합당할 때
    • 다른 타입으로부터 상속받거나 자신을 상속할 필요가 없을 때
  • 구조체로 사용하기 가장 적합한 예로는 좌표계를 들 수 있다.
  • 대다수 사용자 정의 데이터 타입은 클래스로 구현할 일이 많다.
profile
Let's smile for future 🤩

0개의 댓글