본문 바로가기
Xcode/Swift - PlayGround

PlayGround) Struct 와 Class의 차이 그리고 Protocol

by 후르륵짭짭 2020. 7. 23.
728x90
반응형

안녕하세요! 후르륵짭짭 입니다!

이번에는 Swift에서 큰 차이가 없는 것 처럼 보이는 Struct와 Class에 대해서 설명하도록 하겠습니다!

다른 언어와 다르게 Swift에서는 Struct와 Class 모두 변수와 함수 모두 담을 수가 있습니다.

그래서 저 처럼 초보 개발자는 어느 상황에서 Struct와 Class를 쓰면 좋을지 잘 모를 때가 있습니다!

따라서! 그걸 좀 공부해보자 이 글을 남겨 봤습니다!

 

하나씩 알아가보도록 합시다!

** Protocol 생성 **

일단 Protocol 코드를 만들겠습니다!

protocol InfoRule {
    
    var name : String {get}
    var id : Int {get set}
    var totalID : Int {get set}
    
    mutating func changeInfo(name : String)
    
    func returnInfo() -> String
}

Protocol이란, 하나의 규약을 의미합니다.

name은 get으로 반환만 가능하고

id는 get set으로 반환과 수정이 가능하고

func returnInfo() -> String은 String을 반환하는 함수이고

mutating func changeInfo(name : String)은 name이라는 String 변수를 매개변수로 받아서 스토어프로퍼티를 변경하겠다는 것을 의미합니다! 그래서 mutating이 붙습니다. 자세한것은 뒤에서 설명하도록 하겠습니다!

 

** Struct 생성 **

struct StudentInfo : InfoRule{
    
    var name : String = {
        return "HururuekChapChap"
    }()
    
    var id: Int
    
    var totalID: Int{
        get{
            return 100+self.id
        }
        set{
            self.id = newValue
        }
    }

    mutating func changeInfo(name: String) {
        self.name = name
    }
    
    func returnInfo() -> String{
        return "\(name) : \(id)"
    }

}

이 코드를 보면 구조체 형태은 StudentInfo는 InfoRule의 프로토콜 규약을 상속 받고 있습니다!

따라서 InfoRule이 가지고 있는 모든 것을 가져서 정의 해줘야하죠!

그런데 여기서 제가 항상 헷갈렸던게 있습니다.

도대체 var name : String = {return "이름"} 과  var name : String = {return "이름"}() 

이 둘의 차이는 도대체 멀까???

그래서 열심히 스택오버플로우에서 찾아봤죠!!! ㅋㅋㅋㅋ 

그리고 당연히 저와 같은 생각을 가진 사람이 있었더군요 ㅎㅎㅎㅎㅎ

stackoverflow.com/questions/36749231/function-produces-expected-type-string-did-you-mean-to-call-it-with

 

Function produces expected type "String", did you mean to call it with "()"

let documentUrl: NSURL? = { return NSFileManager.defaultManager().URLsForDirectory(.DocumentDirectory, inDomains: .UserDomainMask).first } var test: String = { return "String" } This is a...

stackoverflow.com

이 글을 보면 나와 있습니다!

저는 처음에 var name : String = {return "이름"}()  이것도 하나의 프로토콜이라 생각 했습니다!

그런데 이것은 프로토콜이 아니라 함수를 지정해주고 결과 값을 name에 넣어주는 거라 하더군요

"You want to call the function and set the variable test to the result. The parentheses make that happen; that is how you call a function."

그럼 var name : String = {return "이름"} 은 멀까요?

이것은 comput property 입니다!!! 둘이 생긴게 비슷하게 생겼습니다!

하지만 get을 생략 해주더군요 ㅎㅎㅎㅎ 그래서 제가 헷갈렸던 겁니다!

 

- 잠시 property 에 대해서 알아 가보도록 하겠습니다 -

프로퍼티에는 stored property / comput property / Type property 가 있습니다.

stored property 란 값을 저장하고 사용할 수 있는 프로퍼티를 의미합니다. 그래서 자기 자신을 {get set}을 가지고 있는 것이라 생각하면 편합니다. 그냥 변수 입니다 ㅎㅎㅎㅎ

comput property는 다른 stored property를 수정해주거나 가공해서 반환해주는 것을 말합니다!

stackoverflow.com/questions/52825022/swift-4-2-setter-getter-all-paths-through-this-function-will-call-itself

 

Swift 4.2 Setter Getter, All paths through this function will call itself

With swift 4.2 I have begun to see a lot of issues, and one of them i'm not really sure how to resolve, since my getter method should be returning the value itself. I imagine what is happening is ...

stackoverflow.com

제가 또 이렇게 모르는게 생겨서 찾아봤습니다 ㅎㅎㅎㅎ

    var totalID: Int{
        get{
            return 100+self.id
        }
        set{
            self.id = newValue
        }
    }

즉, 이것 처럼 스토어드 프로퍼티인 id 를 가지고 가공해서 totalID를 반환해주는 겁니다! ㅎㅎㅎ 어렵지 않죠?

마지막으로 Type property 는 Static으로 사용되어 구조체 자체에서 공용적으로 쓰이는 프로퍼티 입니다., 즉, 구조체에 특징을 나타내는 stored 프로퍼티라고 할 수 있습니다.

 

** Class 생성 **

class WorkerInfo : InfoRule {
    
    
    var name: String
    var id: Int
    
    var totalID: Int{
        get{
            return 100+self.id
        }
        set{
            self.id = newValue
        }
    }
    
    init(_ id : Int){
        self.name = "ChapChap"
        self.id = id
    }
    
    func returnInfo() -> String {
        return "\(name) : \(id)"
    }
    
    
    func changeInfo(name: String) {
        self.name = name
    }
    
        
}

Class도 똑같이 InfoRule을 받도록 합니다.

하지만 Struct와 Class의 차이가 보입니다! 바로 init()이죠 ㅎㅎㅎ

물론 Struct에서도 init()을 생성해줄 수 있습니다. 하지만 변수의 변화가 이러날 수 있는 var 같은 경우는 Struct에서는 실제로 변수로 사용될 수 있도록 생성될 때 ( let one = StudentInfo(id:20) ) 이렇게 생성 될 때, 정의해주면 됩니다!

하지만 Class 같은 경우에는 그렇게 안되고 init()으로 초기화 해주던지 아니면 변수에 값을 직접 넣어줘야합니다!

 

두번째 차이점은 mutating func changeInfo(name : String) 에 있습니다!

mutating이란! 함수를 통해서 stored property 값을 변경 할 경우 필요한 작업 입니다! 

StudentInfo 구조체를 보도록 합시다.

    mutating func changeInfo(name: String) {
        self.name = name
    }

이렇게 스토어 프로퍼티인 name을 StudentInfo의 내부 함수인 changeInfo로 변경하고 있죠? 

이걸 경우에 mutating을 넣어줘야합니다! 

따라서 mutating이 있을 경우에는 스토어프로퍼티를 변경해주는 함수구나 라고 생각 하면 편합니다!

 

반면 클래스 같은 경우는 언제든지 값을 변경 할 수 있도록 가정했기 때문에

    func changeInfo(name: String) {
        self.name = name
    }

이렇게 mutating이 필요 없습니다!!! ㅎㅎㅎ

 

** 값 참조(call by value) 그리고 주소값 참조(call by reference) **

이 사진을 본다면 구조체 같은 경우는 변경 후에 값이 달라 졌지만 

클래스는 변경 후에도 값이 같은 것을 확인 할 수 있습니다!

 

이것이 가능한 이유는 Struct는 복사가 일어나는 시점에서 새롭게 만들어 주게 되어 서로 독립적으로 행동 하게 됩니다!

이것이 call by value 입니다!

반면 Class 같은 경우는 복사가 일어난 시점에 새롭게 값을 만들어 주는 것이 아니라 주소값을 복사 하게 됩니다. 

따라서 서로 영향을 받게 되는 겁니다.

이것이 call by reference 입니다!

 

따라서 Struct 같은 경우는 서로 독립적으로 생성되어 영향을 주지 않을 때 사용하고

Class 같은 경우는 싱글톤이나 객체 복사로 여러곳에서 사용하고자 할 때 사용됩니다!

 

이렇게 Class와 Struct의 차이를 알기 위해서 프로토콜 까지 알아봤습니다 ㅎㅎㅎ

너무 길어서 이해가 될지 잘 모르겠지만

읽어주셔서 감사하고 모두모두 즐코코 하세요~~!!

 

728x90
반응형

댓글