궁금한 내용을 검색해보세요!
이 포스팅은 쿠팡 파트너스 활동의 일환으로, 이에 따른 일정액의 수수료를 제공받습니다.
서근 개발노트
티스토리에 팔로잉
SWIFT/Grammar

Swift : 기초문법 [클래스 - Class]

서근
QUOTE THE DAY

-
Written by SeogunSEOGUN

반응형

Class

  • swift에서는 객체라는 용어 대신에 인스턴스 라는 용어를 사용합니다. 한마디로 클래스 타입의 인스턴스를 객체라고 칭하지 않습니다. 
  • 단일 상속만 가능합니다.
  • (인스턴스/타입) 메서드, (인스턴스/타입) 프로퍼티 (Struct와 같음)
  • 참조타입(=reference type) (리퍼런스reference 라고 부름)
  • iOS 프레임워크의 대부분이 클래스로 구성되어있습니다.
  • SwiftUI에서는 대부분잉 Struct로 구성되어있습니다.

class 는 프로퍼티와 메서드를 사용하여 새로운 유형을 생성할 수 있다는 점에서 구조체와 유사하지만, 중요한 차이점이 있으며 각 차이점을 하나씩 살펴보겠습니다.

 

클래스 정의

키워드 class

class 클래스 이름 {
  // 프로퍼티와 메서드
}

sturct 구조와 비슷하죠? 차이점은 클래스는 상속받을 수 있기 때문에 상속 받을 때는 이름 뒤에 콜론( : ) 하고 부모클래스 이름을 명시해야 해요. 

class 클래스 이름: 부모클래스 이름 {
   // 프로퍼티와 메서드
}
class Person {
 var height: Double = 0.0
 var weight: Double = 0.0
}

여기서 정의된 클래스는 Double 타입인 heightweight 저장 프로퍼티가 있는 Person 클래스 이겠네요 :)

 

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

클래스를 정의하고 초기화 할때는 따로 초깃값을 주지 않고 이니셜라이저를 사용하면 됩니다.

 

인스턴스가 생성되고 초기화 즉, 이니셜라이즈된 후에  구조체와 마찬가지로 마침표를 사용하면 되는데, 차이점은 struct는 변수일때만 수정이 가능했지만, classvar 이건 let이건 내부 프로퍼티 값을 변경할 수 있습니다,

class Character {
    var name: String = "포뇨"
    var isfavorite: Bool = false
}

let Mycharacter: Character = Character()
Mycharacter.name = "소피아"
Mycharacter.isfavorite = false

if Mycharacter.isfavorite {
    print("내가 좋아하는 인형은 \(Mycharacter.name) 입니다")
} else {
    print("나는 이 캐릭터를 좋아하지 않는다.")
} // 나는 이 캐릭터를 좋아하지 않는다.



var myCharacter: Character = Character()
myCharacter.name = "캘시퍼"
myCharacter.isfavorite = true

if myCharacter.isfavorite {
    print("내가 좋아하는 인형은 \(myCharacter.name) 입니다")
} else {
    print("나는 이 캐릭터를 좋아하지 않는다.")
} // 내가 좋아하는 인형은 캘시퍼 입니다

class와 struct의 차이점

구조체와 클래스는 서로 비슷하거나 같은 점이 많습니다. 먼저 같은점 부터 고고 

같은 점

  • 값을 저장하기 위해 프로퍼티를 정의할 수 있음
  • 기능 실행을 위해 메서드를 정의할 수 있음
  • 서브스크립트 문법을 통해 구조체 또는 클래스가 갖는 값에 접근하도록 서브스크립트를 정의할 수 있음
  • 이니셜라이저를 정의할 수 있음
  • 익스텐션을 통해 확장할 수 있음
  • 특정 프로토콜을 준수할 수 있음

다른 점

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

이 둘은 정의하는 방법, 인스턴스 하는방법, 프로퍼티와 메서드를 갖는다는 것 모두 비슷하지만, 이 두 타입을 구분 짓는 가장 큰 차이점은 클래스는 참조타입 이고 구조체는 값 타입! 그렇기에 참조 횟수 계산은 클래스에만 적용됩니다.

클래스에는 memberwise 이니셜 라이저가 함께 제공되지 않습니다. 즉, 클래스에 프로퍼티가 있는 경우 항상 고유 한 이니셜 라이저를 만들어야 합니다.

예를 들면 다음과 같습니다.

class Dog {
    var name: String
    var breed: String

    init(name: String, breed: String) {
        self.name = name
        self.breed = breed
    }
}

해당 클래스의 인스턴스를 만드는 것은 마치 구조체인 것처럼 보이죠?

let poppy = Dog(name: "Poppy", breed: "Poodle")

기존 클래스를 기반으로 클래스를 만들 수 있다는 것입니다. 원래 클래스의 모든 프로퍼티와 메서드를 상속하고 자체 클래스를 맨 위에 추가할 수 있습니다.

 

이를 클래스 상속(class inheritance)  또는 서브클래싱(subclassing) 이라고하며 상속된 클래스를 "parent” 또는 “super" 클래스라고 하며 새 클래스를 "child" 클래스라고 합니다.

class Dog {
    var name: String
    var breed: String

    init(name: String, breed: String) {
        self.name = name
        self.breed = breed
    }
}

우리는  Poddle이라는 class을 바탕으로 새로운 class를 만들 수 있습니다. 기본적으로 Dog와 동일한 프로퍼티 및 이니셜 라이저를 상속합니다.

class Poodle: Dog {

}

하지만 이 클래스에 Poodle만의 이니셜 라이저도 줄 수 있습니다. 우리는 그것이 항상 "푸들"이라는 품종을 가지고 있다는 것을 알고 있기 때문에 name 프로퍼티만 필요한 새로운 이니셜 라이저를 만들 수 있습니다.

 

더 좋은 점은 Poodle 이니셜 라이저가 Dog 이니셜 라이저를 직접 호출하도록 하여 동일한 설정이 가능하도록 할 수 있다는 점입니다.

class Poodle: Dog {
    init(name: String) {
        super.init(name: name, breed: "Poodle")
    }
}

Swift는 항상 안전을 추구하기 때문에  자식 클래스의 super.init() 를 호출합니다. 왜냐하면 부모 클래스가 생성될 때 중요한 작업을 수행할 경우를 대비해서입니다.

 

예시

class Shape {
	var sides: Int
	init(sides: Int) {
		self.sides = sides
	}
}
class Rectangle: Shape {
	var color: String
	init(color: String) {
		self.color = color
		super.init(sides: 4)
	}
}
  • 클래스에는 합성된 memberwise 이니셜 라이저가 제공되지 않습니다.
  • 한 클래스는 다른 클래스 위에 빌드("상속")하여 프로퍼티와 메서드를 얻을 수 있습니다.
  • 구조체의 복사본은 항상 고유 한 반면 클래스 복사본은 실제로 동일한 공유 데이터를 가리 킵니다.
  • 클래스에는 클래스의 인스턴스가 소멸될 때 호출되는 메서드 인 deinitializer 가 있지만 구조체는 그렇지 않습니다.
  • 상수 클래스의 변수 속성은 자유롭게 수정할 수 있지만 상수 구조체의 변수 속성은 수정할 수 없습니다.

이러한 차이점을 곧 자세히 설명할 것이지만 요점은 이들이 존재하고 중요 하다는 것입니다.

 

대부분의 Swift 개발자는 가능하면 클래스보다 구조체를 사용하는 것을 선호합니다.

 

즉, 구조체 대신 클래스를 선택할 때 위의 동작 중 하나를 원하기 때문에 그렇게 하는 것입니다.

 

값 타입 참조 타입

구조체 = 값 타입 / 클래스 = 참조 타입. 

 

값 타임 과 참조 타입의 가장 큰 차이점은 ' 무엇이 전달되느냐 ' 입니다.

 

만약 어떠한 함수의 전달인자로 값 타입의 값을 넘기면 전달인자 값이 복사되어 전달 되겠죠? 

 

근데, 참조 타입이 전달인자로 전달 될땐 값을 복사하지 않고 참조가 전달 될 뿐!

struct information {
    var name: String
    var age: Int
}

var SeogunInfo: information = information(name: "서근", age: 26)
SeogunInfo.age = 27

var MijinInfo: information = SeogunInfo

//MijinInfo가 SeogunInfo의 값을 복사하여 할당했음
print("Seogun age : \(SeogunInfo.age)")
print("Mijin age : \(MijinInfo.age)")

/*
 Seogun age : 27
 Mijin age : 27
*/

// 만약 MijinInfo의 이름을 변경한다면?
MijinInfo.name = "미진"

print("Seogun name: \(SeogunInfo.name)") //Seogun 이름값은 변동이 없음
print("Mijin name: \(MijinInfo.name)") // 복사해서 가져왔기 때문에 별개의 값을 갖음.

/*
 Seogun name: 서근
 Mijin name: 미진
*/


/* ================== */

class Person {
    var name: String = "서근"
    var height: Double = 0.0
}

let Seogun: Person = Person()
let Mijin: Person = Seogun // 미진은 서근의 참조를 할당했음

print("Seogun의 height: \(Seogun.height)")
print("Mijin의 height: \(Mijin.height)")

/*
 Seogun의 height: 0.0
 Mijin의 height: 0.0
*/

Mijin.height = 170

// Seogun은 Mijin을 참조하기 때문에 값이 같이 변동됨
// 즉, Seogun과 Mijin은 참조하는 곳이 같음을 알수 있음.
print("Seogun의 height: \(Seogun.height)")
print("Mijin의 height: \(Mijin.height)")

/*
 Seogun의 height: 170.0
 Mijin의 height: 170.0
*/

값 타입(구조체) : 값 타입 데이터 -> 함수의 전달인자로 전달 -> 인스턴스 생성됨 -> 복사되어 들어감

 

참조 타입(클래스) : 참조타입 데이터 -> 기존 인스턴스의 참조를 전달 

 

식별 연산자

클래스의 인스턴스끼리 참조가 같은지 확인 할때는 식별 연산자 를 사용!

class Person {
    var name: String = "서근"
    var age: Int = 26
}

let Seogun: Person = Person()
let Mijin: Person = Seogun // Seogun의 참조를 할당
let anotherFrnd: Person = Person() // 새로운 인스턴스 생성
 
print(Seogun === Mijin) // true
print(Mijin === anotherFrnd) // false
print(Mijin !== anotherFrnd) // true

 

TEST :  문제를 풀려면 이곳을 클릭해주세요.

 

 

읽어주셔서 감사합니다🤟

 

 


잘못된 내용이 있으면 언제든 피드백 부탁드립니다.


서근


위처럼 이미지 와 함께 댓글을 작성할 수 있습니다.