티스토리 뷰

Swift

[Swift] Struct와 Class의 차이

XXIN-dev 2021. 2. 19. 15:22
Class  Struct에 대한 궁금증이 생겼다. 두 가지를 무턱대고 사용했고 당연히 ViewController를 만들 때는 Class, Test Model를 만들 때는 Struct로 Model를 만들었다. 이유는 크게 없었고 그냥 그렇게 배웠고 그렇게 만들어 왔기 때문에. 어떤 언어를 배우던지 Class, Struct는 큰 차이가 없다고 말하는데 그 미묘한 차이는 뭔지 왜 말을 안해주는지.. 그래서 검색을 해보았다.

🙏 공통점

  1. 프로퍼티 메서드 구조화해서 묶어둔 형식
  2. 하나의 사용자 지정 타입을 만드는 것
  3. 이니셜라이저 init 를 정의해서 초기 상태를 지정할 수 있음
  4. 새로운 기능 추가를 위해서 Extension 사용 가능
  5. Protocol 사용 가능
  6. 서브 스크립트 정의 가능
  7. 프로퍼티값에 접근하고 싶다면 . 를 사용

🤲 차이점

1. Struct

  • 값(Value) Type
    - Value 이기에 multi-thread 환경에서 공유 변수로 인한 문제를 일으킬 확률이 적다.
  • Stack memory 사용
    - 속도가 빠르다.👍
    - Complier의 메모리 할당/해제가 정확하다.
  • let 으로 초기화 이후에 값 변경이 불가능

2. Class

  • 참조(Reference) Type
  • 상속 가능
    - 상속이 가능하기에 타입캐스팅도 허용된다.
  • deinitalize도 구현 가능
  • Heap memory 사용
    - 속도가 느리다.😅
  • let 으로 초기화 이후 값 변경 가능
  • class 인스턴스에 하나 이상 참조 가능
  • 식별 연산자(===) 사용 가능
    - 상수와 변수가 같은 인스턴스를 참조하고 있는지 비교하기 위해 식별 연산자를 사용

 

검색하면서 내가 궁금해했던 것보다 차이점이 미미하다고 생각했다. 그래서 파고들어간 것이initalization 부분이다.

📨 initalization(초기화)

init은 Struct, class선언과 동시에 프로퍼티에 초기값을 지정해준다. 명확한 initalizer이고 타입 추론이 가능하게 하며 사용 시에 코드를 짧고 간결하게 만들 수 있다. 모든 프로퍼티에 할당하지 않으면 에러가 발생한다.
Objective-C와 차이점이 있다면 Swift의 init은 값을 반환하지 않는다고 한다.

 

struct Model { 
	var name: String 
    var age: Int 
    init(age: Int){ 
    	name = "test" 
        self.age = age 
    } 
} 

let firstModel = Model(age: 10)

1. Struct

1. 자동 생성

  • 기본 이니셜라이저(Default Initalizers)
    - stored property의 초기값을 사용
    - stored property를 모두 초기화한 경우에만 사용 가능(옵셔널 아님)
struct Model { 
	var name: String = "Tom" 
    var age: Int = 10 
} 

let model = Model()

해당 코드처럼 property를 모두 초기화시킨 경우만 기본 이니셜라이저 사용 가능하다.

 

  • 멤버와이즈 이니셜라이저(Memberwise Initalizers)
    - 자동으로 stored property가 가진 값을 사용
    - stored property가 하나라도 초기화되지 않은 경우 사용
    - 사용자 지정 이니셜라이저를 정의하지 않았다면 멤버와이즈 이니셜라이저를 제공
struct Model { 
	var name: String 
    var age: Int 
} 

let model = Model(name: "Tom", age: 10)

 

2. 사용자 지정(Customizing Initalizers)

사용자 지정 이니셜라이저는 자동 생성 이니셜라이저와 함께 사용할 수 없다.
만약, 두 가지를 모두 사용하고 싶다면 extension를 이용하면 가능하다.

struct Model { // optional 사용해서 실패할 경우 nil반환 
	var name: String? 
    var age: Int? 
    init(name: String) { 
    	self.name = name 
        age = 10 
    } 
} 

let model = Model(name: "Tom") // name: "Tom", age: 10

사용자가 직접 init 매개변수를 지정해서 초기화 함수를 만들 수 있다. 내부에 있는 매개변수의 조합만 서로 다르다면 init를 여러개로 만들 수도 있다. 또한, 내부 내용 사용자가 직접 지정 가능하다.

 

  • Delegate Initalizers (<-> class와 동작 방식에 차이 존재)
    - init 안에서 self.init() call
    - 다른 init에 초기화를 위임해서 코드 중복을 예방
struct Model { 
	var name: String?
    var age: Int? 
    
    init(name: String, age: Int) { 
    	self.name = name 
        self.age = age 
    } 
    
    init(name: String) { 
    	self.init(name, 10) 
    } 
} 

let model = Model(name: "Tom") // name: "Tom", age: 10

 

2. Class

모든 클래스의 stored property와 superclass로부터 상속받은 모든 property는 초기화 단계에서 반드시 초기값이 할당되어야 한다.

1. 지정 이니셜라이저(Designated Initalizers)

  • 모든 stored property 초기화
  • 반드시 한 개 이상 Designated Initalizers 필요
  • superclass Initalizer call -> 연쇄로 superclass call
    - 자신이 superclass인 경우에는 super.init()하지 않아도 된다.

 

2. 컨비니언스 이니셜라이저(Convenience Initalizers)

  • 있어도 되고 없어도 되는 이니셜라이저
  • 매개변수를 지정 이니셜라이저와 다르게 받아서 지정 이니셜라이저를 호출해서 초기화
    - 무조건! self.init() 사용
    - 무조건! 편의 이니셜라이저는 지정 이니셜라이저를 호출
  • super.init() , superclass func 초기화하기위해 호출 불가능
  • 시간 단축  class Initalizers의 의도를 명확하게 해줌

 

class Model { 
	var name: String? 
    
    // 지정 이니셜라이저 
    init(name: String){ 
    	self.name = name 
    } 
} 

class SubModel: Model { 
	var age: Int 
    
    // superclass를 상속받은 지정 이니셜라이저 
    init(name: String, age: Int) { 
    	self.age = age 
        super.init(name: name) 
    } 
    
    // 편의 이니셜라이저 
    convenience init(age: Int) { 
    	self.init(name: "Tom", age: age) 
    } 
}

Designated Initalizers는 항상 superclass를 Delegate
convenience Initalizers는 항상 같은 class를 Delegate

잘못 초기화하는 상황을 막기 위해서 자식 클래스가 부모 클래스의 init를 꼭 상속하게 하지 않는다.
but, 특정 조건을 만족하면 부모 클래스의 init를 상속해야 함.

  1. 자식이 지정 Initalizers를 정의하지 않았을 경우 -> 부모 클래스 지정 init 상속
  2. 자식이 부모의 지정 Initalizers를 모두 구현했을 경우 -> 부모 클래스 편의 init 상속

 

3. 필수 이니셜라이저(Required Initalizers)

  • superclass에서 선언하면 자식 클래스는 무조건 필수 이니셜라이저를 구현
  • 따로 override하지 않아도 됨
struct Model { 
	required init() { 
    	... 
    } 
} 

struct SubModel: Model { 
	required init() { 
    	... 
    } 
}

 

4. 클로져 이니셜라이저(Closure Initalizers)

  • struct도 사용 가능
  • 다소 복잡한 값을 할당할 때 사용하기 좋음
  • 한 번만 호출되며, 변수var일 경우에는 값 변경도 가능
  • stored property에 사용자 정의나 설정이 필요하다면, 사용자 정의 기본 값을 제공하기 위한 클로저나 전역 함수를 사용
struct Model { 
	let age: [Int] = { 
    	var myAge: [Int] = [] 
        for i in 1...10 { 
        	myAge.append(i) 
        } 
        return myAge 
    }
}

 


🤔 Struct? Class?

  1. 간단한 값의 집합을 캡슐화 시킬 수 있다. -> struct
  2. 캡슐화한 값에 reference보다 copy가 더 합당할 때 -> struct
  3. property가 value type이여야 할 때 -> struct
  4. 상속받거나 상속할 필요가 있을 때 -> class
  5. deinit 사용 -> class

 

☑️ Plus

Value type :

Struct, Enum, Tuple
struct String, struct Array( Set, Dictionary)

Reference type :

Class,Function




잘못된 점과 피드백은 댓글로 주시면 감사하겠습니다.

참고:
https://medium.com/@jgj455/%EC%98%A4%EB%8A%98%EC%9D%98-swift-%EC%83%81%EC%8B%9D-struct-class-60fa5fd2218d
https://jusung.gitbook.io/the-swift-language-guide/language-guide/14-initialization
https://stackoverflow.com/questions/24217586/structure-vs-class-in-swift-language

'Swift' 카테고리의 다른 글

[Swift] Property  (0) 2021.02.21
[Swift] Enum  (0) 2021.02.20
링크
최근에 올라온 글
최근에 달린 댓글