클래스와 구조체 Class, Struct 만들고 사용하기, init, deinit/ Xcode SwiftUI Playground 공부
클래스와 구조체
코드를 추상화 하기 위해서 사용
구조체와 클래스는 프로퍼티, 메소드(함수)를 사용해서 구조화된 데이터의 기능을 사용할 수 있음
새 사용자 정의 데이터 타입을 만드는 것
// 클래스
class 클래스이름 {
프로퍼티, 메서드
}
// 구조체
struct 구조체이름 {
프로퍼티, 메서드
}
클래스 구조체 공통점
1. 프로퍼티 선언 할 수 있다 (값을 저장하는 프로퍼티 선언)
2. 함수적 기능 메서드 선언 할 수 있다.
3. 내부 값에 .을 사용히여 접근할 수 있다.
4. 생성자를 사용해 초기 상태를 설정할 수 있다.
5. extension을 사용하여 기능을 확장할 수 있다.
6. 프로토콜(Protocol)을 채택하여 기능을 설정할 수 있다.
- 프로퍼티, 메소드를 사용해 구조화된 데이터 기능 가짐
- 새로운 사용자 정의 타입 만들기
- 초기화(기본값)을 정의해 초기 상태 설정 가능
- 확장 가능
- 프로토콜 사용 가능 (?)
- 특정 값에 접근할 수 있는 서브스크립트 문법 사용
- 프로퍼티 값에 접근하고자하면 structName.valuename / className.valuename( 로 사용하요 접근가능)
클래스 구조체 차이점
클래스(값을 참조함) | 구조체(값을 복사함) |
- 참조타입 - ARC로 메모리를 관리 - 상속이 가능 - 타입 캐스팅을 통해 런타임에서 ㄴ클래스 인스턴스의 타입을 확인할 수 있음 - deinit을 사용해 클래스 인스턴스 메모리 할당을해제할 수 있음 - 같은 클래스 인스턴스를 여러 개의 변수에 할당한 뒤 값을 변경 시키면 모든 변수에 영향을 줌 (메모리에 복사 됨) |
- 값 타입 - 구조체 변수를 새로운 변수에 할당할 떄마다 새로운 구조체가 할당된다 - 즉, 같은 구조체를 여러 개의 변수에 할당한 뒤 값을 변경시키더라도 다른 변수에 영향을 주지 않음 (값 자체를 복사) |
// 클래스와 구조체의 공통점, 차이점
// 차이점
class SomeClass {
var count: Int = 0
}
//같은 클래스 인스턴스를 여러 개의 변수에 할당한 뒤 값을 변경 시키면 모든 변수에 영향을 줌 (메모리에 복사 됨)
var class1 = SomeClass()
var class2 = class1
var class3 = class1
class3.count = 2
class1.count // 2
struct SomeStruct {
var count: Int = 0
}
// 같은 구조체를 여러 개의 변수에 할당한 뒤 값을 변경시키더라도 다른 변수에 영향을 주지 않음 (값 자체를 복사)
var struct1 = SomeStruct()
var struct2 = struct1
var struct3 = struct1
struct2.count = 3
struct3.count = 5
struct1.count // 0
struct2.count // 3
struct3.count // 5
클래스 (값을 참조함)
- 참조 타입(Referece Type) 참조타입은 값을 할당하거나 전달할 때 그 값을 복사하는게 아니라 참조하는 의미
--> 변수나 상수에 값을 할당하거나 함수에 인자로 전달될 때
- call by reference : 할당, 전달 시 객체를 가리키는 메모리 주소 값 복사됨
- 속도가 느림 (Heap Memory영역에 할당)
- 런타임 시에 직접 할당(allocate)을 함, 참조계수(referece counting)를 통해 할당값을 해제(deallocate) 해야함
- 상속가능
- 런타임에 타입캐스팅으로 클래스 인스턴스에 따라 여러 동작
- 초기화 기능 (deinitializer)
구조체 (값을 복사함)
- 값 타입 : 값 타입은 함수에서 상수나 변수로 전달될 떄 그 값이 복사되어 전달된다.
- call by value : 할당 또는 파라미터 전달할 때 값을 복사함 (value copy)
- 멀티 스레드 환경에서 공유 변수로 인한 문제가 발생할 가능성이 적음
- Stack Memory 메모리에 쌓기 때문에 속도가 빠름
- Scope Based lifetime (수명주기) : 메모리 할당 해제 시기를 정확히 알고있음
- 상속 불가능
- AnyObject 타입 캐스팅 불가능
구조체 사용하는 것이 유리한 경우
- 연관 값을 캡슐화(하나로 묶는)하는 것이 목적인 경우
- 캡슐화 한 값을 참조보다 복사 할 때
- 다른 타입으로 상속하거나 상속받지 않아도 될때 (상속이 필요없을때)
- JSON 필드와 1:1 Mapping 되는 간단한 모델이 필요할때
구조체 생성
// 구조체 만들기
struct User {
var nicname: String
var age: Int
}
구조체 인스턴스화
// 인스턴스화 하기
var user = User(nicname: "영희", age: 17)
user.nicname
user.nicname = "철수"
user.nicname
구조체 선언 / 사용하기
// 구조체 안에 함수(메서드) 선언하고 사용하기
struct User2 {
var nicname2: String
var age2: Int
func information() {
print("\(nicname2), \(age2) 함수출력")
}
}
var user2 = User2(nicname2: "영희2", age2: 17-2)
user2.information()
클래스 생성
// 강아지 정보 구성 클래스
class Dog {
var dogName: String = ""
var dogAge: Int = 0
// 생성자 정의 생성자를 통해 초기화도 가능
init(){
}
//메서드 정의
func introduce() {
print("name \(dogName) age \(dogAge)")
}
}
클래스 인스턴스화, 프로퍼티 값 설정, 사용하기
// dog 클래스의 인스턴스 생성 프로퍼티 값 설정
var dog = Dog()
dog.dogName = "아롱이"
dog.dogAge = 11
dog.dogName
dog.dogAge
// doc 클래스 안에 메서드 불러오기
dog.introduce()
init 초기화 구문 사용하기
초기화 구문을 사용하여 초기화하면 인스턴스 생성 후 바로 값 저장 가능
- 클래스, 구조체, 열거형 인스턴스 사용을 위한 준비작업
- 프로퍼티의 초기 값 설정
- init() 형태로 초기화 값 사용
- deinit() : 인스턴스의 할당이 해제될 때 메모리 관리 작업을 수행하기 위해 사용
init(매개변수:타입, ...){
// 프로퍼티 초기화
// 인스턴스 생성시 필요한 설정을 해주는 코드 작성
}
// 초기화 구문을 사용하여 초기화하면 인스턴스 생성 후 바로 값 저장 가능
클래스 User 생성해서 init 이용 초기화, 초기화 값 저장 사용
// 클래스 User 생성 -> init 이용 초기화
class User {
var nickname: String
var age: Int
// 기본사용
init(nickname: String, age: Int) {
self.nickname = nickname
self.age = age
}
}
// 인스턴스 생성, 프로퍼티 값 저장
var user = User(nickname: "JSON", age: 17)
// 프로퍼티 값 사용하기
user.nickname
user.age
사용자 정의 생성자를 이용한 생성자 정의하기
매개변수 전달 없이 프로퍼티 값에 대입하여 초기화 하기
// 클래스 User 생성 -> init 이용 초기화
class User {
var nickname: String
var age: Int
// 사용자 정의 생성자를 이용한 생성자 정의하기
// 매개변수로 전달받지 않고 프로퍼티 값에 대입하여 초기화 하기
init(age:Int) {
self.nickname = "ablert"
self.age = age
}
}
// 사용자 정의 생성자를 이용한 생성자 정의하기
var user2 = User(age: 27)
user2.nickname
user2.age
deinit 사용하기
초기화와 반대의 개념
스위프트는 인스턴스가 더 이상 필요가 없다고 판단되면 자동으로 소멸 시킨다.
deinit은 인스턴스의 할당이 해제되면 메모리 관리 작업을 수행하기 위해 'deinit()'함수를 제공한다.
// 클래스 User 생성 -> init 이용 초기화
class User {
var nickname: String
var age: Int
// deinit
deinit {
print("deinit user")
}
}
// deinit 확인햅호기
// 옵셔널타입으로 타입 선언 -> nill : nill을
// 스위프트는 인스턴스가 더이상 필요가 없으면 자동으로 소멸시킴
var user3 : User? = User(age: 23)
user3 = nil
프로퍼티
구조체와 클래스 프로퍼티에는
저장 프로퍼티, 지연프로퍼티, 계산된 프로퍼티가 있다.
저장 프로퍼티
구조체
// 구조체안에 프로퍼티는 변수,상수 의미 그대로 적용됨 (구조체 : 값타입 함수로 함수에서 상수나 변수로 전달될 때 그 값이 복사되서 전달됨)
// 변경을 하면 안될 때 사용
struct Dog {
var name: String
let number: String
}
var dog = Dog(name: "아롱이", number: "0112")
print(dog)
// dog.name = "돼지"
// dog.number = 1113
// 구조체 struct의 dog.number 은 상수로 값이 변경될 수 없음 (name은 변경되지만 뒤에 number 때문에 같이 오류가 남)
클래스
// 클래스안에 프로퍼티는 변수,상수 의미가 참조되어 전달되기 때문에 인스턴스 생성 후 변수나 상수를 만들어 선언하면 참조되어 변경이 가능하다.
// 변경이 가능할 때 사용
class Cat {
var name: String
let number: String
init(name: String, number: String){
self.name = name
self.number = number
}
}
let cat = Cat(name: "아롱이", number: "1113")
cat.name = "돼지"
print(cat.name, cat.number)
계산(연산) 프로퍼티
// 계산 (연산) 프로퍼티
struct Stock {
// 평균가격
var averagePrice: Int
// 수량
var quantity: Int
// 계산형 프로퍼티
// 매입가격
var purchasePrice: Int {
get {
return averagePrice * quantity
}
// newPrice = 변경된 총 매입가에 저장
set (newPrice) {
averagePrice = newPrice / quantity
}
}
}
var stock = Stock(averagePrice: 2300, quantity: 3 )
print(stock)
// get 실행
stock.purchasePrice
// set 실행
// purchasePrice 값을 받게 되면서 다시 변경된 값의 변경이 되면서 set이 실행이 됨
stock.purchasePrice = 3000
stock.averagePrice
- 읽기 전용 프로퍼티
연산 프로퍼티에서 get 만 사용하여 읽기전용 프로퍼티를 사용해서 값을 변경할 수 없게도 사용할 수 있음
- set 사용 시 매개변수를 적지 않고 newValue 로 사용가능
set을 사용할때 매개변수의 값(여기서 newPrice)를 선언하지 않으면 newValue로 사용할 수 있다.
// newPrice = 변경된 총 매입가에 저장
set {
averagePrice = newValue / quantity
}
class를 이용한 연산 프로퍼티 사용법
// 클래스로 계산된 프로퍼티 사용하기
// 넓이와 길이 계산
class Sample2 {
var no1 = 0.0, no2 = 0.0
var length = 300.0, breadth = 150.0
// 계산된 프로퍼티
var middle:(Double, Double) {
get{
return(length / 2, breadth / 2)
}
// class 에서도 매개변수이름 없이 newValue 사용 가능
/*
set{
no1 = newValue.0 - (length / 2)
no2 = newValue.1 - (breadth / 2)
}
*/
set(axis){
no1 = axis.0 - (length / 2)
no2 = axis.1 - (breadth / 2)
}
}
}
var sample2 = Sample2()
print(sample2.middle) // (150.0, 75.0)
sample2.middle = (0.0, 10.0)
print(sample2.no1) // -150.0
print(sample2.no2) // -65.0
프로퍼티 옵저버
(저장 프로퍼티, 오버라이딩이 된 저장 계산된 프로퍼티에서만 사용가능)
● 프로퍼티에는 새로운 값이 설정될 때마다 이러한 이벤트를 감지할 수 있는 프로퍼티 옵저버를 제공합니다.
● 프로퍼티 옵저버는 새 값이 이전 값과 동일하더라도 항상 호출될 수 있습니다.
● 프로퍼티 옵저버는 지연 저장 프로퍼티에는 사용할 수 없습니다.
● 계산된프로퍼티는setter에서값의변화를감지할수있기때문에옵저버를따로정의할필요
가 없습니다.
● 프로퍼티 옵저버는 두 가지 종류가 있습니다.
// 저장 프로퍼티의 사용 확인
// 잔액을 표시하는 저장형 프로퍼티
class Account {
var credit: Int = 0 {
// 소괄호 이름 지정
//willSet:값을저장하기전에호출됩니다. 새로운값의파라미터이름을따로지정하지않으면,기본값 으로 “newValue” 라는 이름을 사용할 수 있습니다.
willSet{
print("잔액이 \(credit)원에서 \(newValue)원으로 변경될 예정입니다.")
}
//didSet:새로운값이저장되고난이후에호출됩니다. 바뀌기전값의파라미터이름을따로지정하지 않으면, 기본 값으로 “oldValue” 라는 이름을 사용할 수 있습니다.
didSet{
print("잔액이 \(oldValue)원에서 \(credit)원으로 변경되었습니다.")
}
}
} // Acoount end
var account = Account()
account.credit = 1000
타입 프로퍼티
(저장 프로퍼티, 연산프로퍼티에서만 사용가능)
(인스턴스 생성없이 객체 내에 프로퍼티에 접근을 가능하도록 하는 것 = 프로퍼티 타입 자체와 연결하는 것을 말함)
● 타입 프로퍼티는 특정 인스턴스에 속한 프로퍼티를 말합니다.
● 값 유형에 대한 타입 프로퍼티를 선언하기 위해서는 ‘static’ 키워드를 사용합니다.
● 클래스 유형에 대한 타입 프로퍼티를 선언하기 위해서는 ‘static’와 ‘class’ 키워드를 사용할 수 있
습니다.
● 타입 프로퍼티도 ‘.’을 이용해 프로퍼티의 값을 가져오고 할당할 수 있습니다.
struct SomeStructuer {
// 스토어
static var storedTypePeroperty = "Some value."
// 컴퓨디드
static var computedTypeProperty : Int {
return 1
}
}
SomeStructuer.computedTypeProperty
SomeStructuer.storedTypePeroperty
SomeStructuer.storedTypePeroperty = "hello"
SomeStructuer.storedTypePeroperty
// 인스턴스 생성 없이도 static 을 사용하면 그냥 변경도 가능하다.
// 인스턴스 생성 없이도 static 을 사용하면 그냥 변경도 가능하다.