티스토리 뷰

iOS

구조체(Struct)/ 클래스 (Class)

Peppo 2022. 2. 16. 21:29
728x90

지난 프로퍼티 시간에 클래스/ 구조체를 잠깐 짚고 넘어갔는데요.

저도 아직 두개의 차이점이 어떤건지 긴가민가 해서 

오늘은 클래스/ 구조체에 대해 공부해보려고해요!

 


구조체와 클래스 (Structures and Classes)

프로그램 코드를 조직화 하기 위해 일반적으로 사용되며, 구조체 및 클래스에 프로퍼티와 메소드를 정의해  기능을 추가할 수 있습니다. 

 

어떻게 보면 같지만서도 서로 다른데 둘의 공통점과 차이점을 보겠습니다 !!

 

공통점

  • 서로 다른 타입들을 하나로 묶을수있다.
    (이런식으로요)
    class VideoMode {
        var resolution = Resolution() 
        var interlaced = false  // Bool
        var frameRate = 0.0     // Double
        var name: String?       // String
    }
  • 묶어놓은 타입들을 새로운 타입처럼 사용 가능하다.
  • 값을 저장하기 위한 프로퍼티 정의
  • 기능을 제공하기 위한 메소드 정의
  • subscripts 문법을 이용해 특정 값에 접근할 수 있는 subscript 정의
  • 초기 상태를 설정 할수 있다. (init)
  • extension이 가능하다.

 

차이점

 

구조체

  • 값 타입은 리소스를 적게 먹는다.
  • 복사 (원본은 '그대로 두고' 복사를 해와 '원본' 지킬수 있다.)

클래스

  • 상속!! : 클래스의 여러 속성을 다른 클래스에 물려줍니다. 
  • 참조 (원본에 바로접근)
  • 여러곳에서 변수값을 변경할때 용이하다.

선언 문법 (Definition Syntax)

 

구조체와 클래스 둘다 비슷한 선언 문법을 갖고 있습니다. 

구조체를 선언할 때는 struct 키워드를 

클래스를 선언할 때는 class 키워드를 사용하시면 됩니다. 

 

struct SomeStructure {
    // structure definition goes here
}

class SomeClass {
    // class definition goes here
}
NOTE

새로운 클래스나 구조체를 선언할 때마다 새로운 타입을 선언하는것이기 때문에,
UpperCamelCase 로 선언 합니다. (SomeStructure, SomeClass 등)
프로퍼티나 메소드 같은 경우엔 lowerCamelCase로 선언합니다. (frameRate, incrementCount등)  

 

아래는 구조체와 클래스의 선언 예 입니다.

struct Resolution {
    var width = 0
    var height = 0
}

class VideoMode {
    var resolution = Resolution() // 위 Resolution 구조체를 값으로 사용.
    var interlaced = false
    var frameRate = 0.0
    var name: String?
}

 

구조체 Resolution의 프로퍼티 width, height는 초기값 0을 할당 했기 때문에, 타입추론에 의해 Int라는 타입을 갖게 됩니다.


구조체와 클래스 인스턴스

 

인스턴스 생성은 클래스/ 구조체 이름 뒤에 '( )' 를 붙이면 됩니다.

let someResolution = Resolution()  // 구조체 인스턴스 생성
let someVideoMode = VideoMode()    // 클래스 인스턴스 생성

 

이러면 someResolution 인스턴스에는 구조체 Resolution의 값이 들어있고,

someVideoMode 인스턴스에는 클래스 VideoMode 값이 들어있습니다.

 

한번 확인해보죠 ! 

프로퍼티을 접근하려면 dot(.) 을 쓴다!!

 

// struct Resolution {
//  var width = 0
//  var height = 0
// }

// class VideoMode {
//   var resolution = Resolution() // 위 Resolution 구조체를 값으로 사용.
//   var interlaced = false
//   var frameRate = 0.0
//   var name: String?
// }

let someResolution = Resolution()
let someVideoMode = VideoMode()

print("The width of someResolution is \(someResolution.height)")
// The width of someResolution is 0

print("someVideoMode is  \(someVideoMode.resolution.width)")
// someVideoMode is 0

someVideoMode.resolution.width = 1280
print("The width of someVideoMode is now \(someVideoMode.resolution.width)")
// The width of someVideoMode is now 1280

 

첫번째 print를 먼저 봅시다. 접근되는 방식은 아래와 같아요.

1. someResolution에 Resolution 구조체의 값 대입 (인스턴스화)

2.someResolution.height로 Resoloution 내 height값에 접근

3. height 값은 0 

 

두번째도 첫번째와 같은 케이스이니 넘어갈게요 !

 

세번째  print의 과정은

1. someVideoMode에 VideoMode클래스의 값 대입

2. VideoMode 내 resolution 구조체 인스턴스 내 width로 접근

3. width값 0 -> 1280 으로 변경

 

접근과 값이 변경되는 과정을 보았습니다. 

 


구조체형의 멤버 초기화 (Memberwise Initializer) 

 

여기서 !! 플레이그라운드에 따라해 보셨다면 인스턴스 생성과정중에 이런걸 발견 하셨을거에요.

괄호 하나만 열었을때 !? 

let someResolution = Resolution(

 

구조체형의 멤버 초기화!

 

모든 구조체는 초기화시 '( )' 해당 구조체 내에 있는 프로퍼티의 초기자를 자동으로 생성 합니다. 

 

 

그럼 클래스는요 ?

 

응 안돼~

 네 안돼요. 구조체만 가능합니다.

 

확인해볼까요?

class VideoMode를 struct로 바꿔보죠!

 

 

구조체는 프로퍼티 초기자를 자동생성 한다.

 


구조체와 열거형은 값 타입 (값 복사)

 

구조체와 열거형은 값 타입으로, 함수에서 상수나 변수에 전달될 때 값이 복사되어 전달됩니다. 

 

먼저 구조체 예제를 보시죠.

struct Resolution {
    var width = 0
    var height = 0
}

let hd = Resolution(width: 1920, height: 1080)
var cinema = hd

cinema.width = 2048
print("hd:",hd)
print("cinema:",cinema)

 

hd 라는 상수에 width: 1920, height: 1080 값을 넣고,

hd를 변수 cinema에 할당 했습니다. 

거기에 cinema.width 값을 2048로 바꾸면 cinema와 hd의 width값은 아래와 같아져요.

 

 

원본(hd)의 width 값은 그대로 1920이죠. 

hd와 cinema 두 인스턴스는 서로 다른 공간에 저장되어 사용된다는걸 알 수 있어요 !

 

열거형도 구조체와 같아요!!

예제를 볼까요?

 

enum CompassPoint {
    case north, south, east, west
}
var currentDirection = CompassPoint.west
let rememberedDirection = currentDirection
// rememberedDirection값은 CompassPoint.west

currentDirection = .east
// currentDirection의 값은 CompassPoint.east

if rememberedDirection == .west {
    print("The remembered direction is still .west")
}
// "The remembered direction is still .west"

위 구조체 내용에서 봤듯이, 지정해서 값을 변경하는게 아닌이상 원본을 유지합니다.

rememberedDirection의 값이 CompassPoint.west로 유지되는것 처럼요.

 

구조체, 열거형을 한줄로 정리하자면 ? 

 

원본은 그대로 두고 값을 복사한다.

클래스 (참조 타입)

클래스는 변수나 상수에 값을 할당 하거나 함수에 인자로 전달할 때, 값이 복사 되지 않고 참조 됩니다. 

여기서 참조란 그 값을 갖고 있는 메모리를 바라보고 있다는 뜻입니다.

 

 

역시 문서는 딱딱 하고 어렵죠.. 예제로 볼게요!

 

struct Resolution {
    var width = 0
    var height = 0
}

class VideoMode {
    var resolution: Resolution = Resolution() // 위 Resolution 구조체를 값으로 사용.
    var interlaced = false
    var frameRate = 0.0
    var name: String?
}

let hd = Resolution(width: 1920, height: 1080)

// tenEighty에 VideoMode 클래스 인스턴스 생성
let tenEighty = VideoMode()    

// 각 프로퍼티에 값을 할당
tenEighty.resolution = hd
tenEighty.interlaced = true
tenEighty.frameRate = 25.0
tenEighty.name = "1080i"

// alsoTenEighty라는 상수에 tenEighty 클래스 인스턴스 할당
let alsoTenEighty = tenEighty
alsoTenEighty.frameRate = 30.0

자! 여기서 구조체와 클래스의 차이점이 나와요!!

 

tenEighty.frameRate가 25.0이죠. 

아래쪽에 alsoTenEighty.frameRate 값을 30.0으로 바꿔줬어요. 

 

그럼 tenEighty.frameRate는 몇일까요 ??

 

.

.

.

.

.

 

tenEighty
alsoTenEighty

 

 

 

 

 

뭐여 왜 둘다 바껴 !!

 

네 둘다 바뀌어요.

클래스는 값을 복사하는게 아니라 참조 하기 때문이죠.

 

계속 참조 참조 하는데 저도 참조라는 말이 와닿지 않아서

'해당 값을 그대로 따라한다 라고 이해를 했어요. '

 

문서에서는 메모리 주소를 동일하게 바라보고 참조한다.

이 말보다는 따라한다는 말이 더 와닿더라구요. 

 

그리고 하나 의심가는점이 있지않나요?

let alsoTenEighty = tenEighty
alsoTenEighty.frameRate = 30.0

바로 이 코드

let으로 선언을 했는데 어떻게 값이 바뀌지?

 

위에 크게 적어 놓은거처럼 생각해보시면 돼요. 

해당 값을 그대로 따라해서요!

 


식별 연산자

클래스는 상수와 변수가 같은 인스턴스를 참조하고 있는지 비교하기 위해 식별 연산자를 사용합니다.

 

 

식별 연산자는 이렇게 생겼어요. 

두 상수, 변수가 같은 인스턴스를 참조 하고 있는경우 ( ===

두 상수, 변수가 다른 인스턴스를 참조 하고 있는경우 ( !== )

우리 ( == ) 비교 연산자 공부했었죠 ? 

 

비슷해보이지만 둘은 이런 차이가 있어요.

 

식별 연산자 ( === )는 참조를 비교

비교 연산자 ( == ) 는 을 비교

 

if tenEighty === alsoTenEighty {
print("tenEighty and alsoTenEighty refer to the same VideoMode instance.")
}
// print ==> "tenEighty and alsoTenEighty refer to the same VideoMode instance."

어떨때 클래스를 쓰고 구조체를 쓰나요?

애플은 이럴때 구조체를 사용하라고 권장해요.

  • 연관된 간단한 값 (프로퍼티) 의 집합을 캡슐화 할 때 
  • 캡슐화한 값을 복사하는것이 합당할 때 
  • 다른 타입으로부터 상속받거나 상속할 필요가 없을 때 

 

이외에는 클래스를 사용하는게 좋아요 !!! 

왠만한 경우에 클래스를 사용한다고 보시면 될것 같네요.

 


안그래도 구조체, 클래스에 대해 많이 헷갈렸었는데 조금이나마 정리되는것 같네요. 

블로그 하나 정리하고 코드를 볼때 마다 

내가 모르는걸 정리하고 봐서 그런지 어떤형태인지 조금씩 눈에 보이게 되는점이 너무 좋은것 같습니다.

특히 눈으로만 공식문서를 읽었는데 플레이그라운드에 따라하면서 하니 도움이 많이 되는것 같아요. 

아직 배워야할게 많지만 하나하나 같이 공부해 나가보겠습니다!!

 

 

728x90