어제 아파서 수업에 온전하게 참여를 못했다보니, 흐름이 끊어진 부분에 대하여 아쉬움이 많이 남는다. 다행히 저녁쯤에 정신을 차리고 기초스터디를 통해서 어느정도 복습을 하였지만 100%가 아니기 때문에 다음 진도를 나가는 것에 있어 약간의 걱정이 앞선다.
<9/28일(수) 앞서 배운 것>
- 서브클래싱
- 상속
- 익스텐션의 개념
- 메소드 상속 가능
- 저장 프로퍼티 상속 불가능
<9/29일(목) 오늘 배울 것>
- 구조체와 클래스에 대한 차이점
- 구조체의 선언
- 값타입(call-by-value)과 참조타입(call-by-reference)에 대한 개념
- 오늘의 개념을 잘 잡아야 type을 만들 때 구조체로 할지 클래스로 할지 올바르게 정할 수 있다.
1. 오전일정
<구조체>
1. 구조체의 개요
- 클래스와 같이 구조체도 객체지향 프로그래밍의 기초를 형성하며 데이터와 기능을 재사용할 수 이는 객체로 캡슐화하는 방법을 제공한다.
- 구조체 선언은 클래스와 비슷하지만, class 키워드 대신에 strunct 키워드를 사용한다.
struct SampleStruct{
var name: String
init(name: String){
self.name = name
}
}
let myStruct = SampleStruct(name:"Lee")
//클래스 선언과 같은
class SampleClass{
var name: String
init(name: String){
self.name = name
}
}
let myClass = SampleClass(name:"Lee")
- 클래스와 마찬가지로 구조체도 확장가능하며, 프로토콜을 채택하거나 초기화를 가질 수 있다.
- 클래스와 구조체의 공통점이 많기 때문에 어떻게 다른지를 이해하는 것이 중요하다.
2. 값타입(구조체)
- 겉으로 보기에는 구조체와 클래스가 비슷하지만, 구조체의 인스턴스와 클래스의 1)인스턴스가 복사되거나 2) 메서드 또는 함수에 인자가 전달될 때 발생하는 동작에 큰 차이가 있다.
➡ 구조체의 인스턴스 타입은 값타입, 클래스의 인스턴스 타입은 참조타입 - 구조체 인스턴스가 복사되거나 메서드에 전달될 때 인스턴스의 실제 복사본이 생성되면서 원본 객체가 가지고 있던 모든 데이터를 그대로 복사해서 가지게 된다.
➡ 즉 복사본은 원본 구조체 인스턴스와는 별개인 자신만의 데이터를 가진다는 의미다. - 실제로 실행중인 앱 내의 구조체 인스턴스에 대한 복사본이 여러 개 존재할 수 있으며, 각각의 복사본은 자신만의 데이터를 가질 수 있다.
➡ 어떤 하나의 인스턴스를 변경해도 다른 복사본들에 영향을 미치지 않는다.
struct SampleStruct{
var name: String
init(name: String){
self.name = name
}
}
let myStruct = SampleStruct(name:"Lee")
var myStruct2 = myStruct1
myStruct2.name = "David"
print(myStruc1.name)
print(myStruc2.name)
// 출력값 : Lee
// 출력값 : David
3. 참조타입(클래스)
- 클래스는 인스턴스가 복사되거나 인자로 전달되면 해당 클래스 인스턴스가 있는 메모리의 위치에 대한 참조체가 만들어지거나 전달된다.
var account: BankAccount = BankAccount()
var myAccount = account
var yourAccount = account
myAccount.accountNumber = "123345556"
print("\(yourAccount.accountNumber)")
//결과값: 123345556
4. 구조체의 특징
- 구조체는 클래스에 있던 상속이나 하위클래스를 지원하지 않는다.
- 구조체는 소멸자 메서드(deinit)을 포함할 수 없다.
- 런타임에서 클래스 인스턴스의 유형을 식별할 수 있지만 구조체는 그렇지 않다.
* 클래스 : 상속을 우선적으로 생각하고, protocol은 옵션적으로 생각하는 경향이 있다.
* 구조체 : 상속은 불가능하며, 어떤 protocol을 구현하고 선언해서 쓸 것인지를 생각해야 한다.
- 일반적으로 구조체가 클래스보다 효율적이고 멀티스레드 코드를 사용하는데 더 안정적이기 때문에 가능하다면 구조체를 권장한다.
- 하지만 상속이 필요하거나 데이터가 캡슐화된 하나의 인스턴스가 필요할 때는 클래스를 사용해야 한다.
- 인스턴스가 소멸될 때 리소스를 확보(deinit())하기 위한 작업이 필요할 때도 클래스를 사용해야 한다.
2. 오후일정
<Swift 프로퍼티 래퍼>
1. 프로퍼티 래퍼(property warapper) 형태
- 클래스나 구조체 인스턴스에 있는 프로퍼티에 값을 할당하거나 접근할 때 값을 저장하거나 읽어내기 전에 변환하거나 유효성을 검사해야 할 경우 연산 프로퍼티를 만들어서 구현하였다.
- 하지만 여러 클래스나 구조체에 생성한 연산 프로퍼티들이 유사한 패턴을 갖는 경우가 빈번하게 발생한다.
- Swift 5.1 이전에는 연산 프로퍼티 로직을 공유하는 유링한 방법이 코드 복사였기에 매우 비효율적일 뿐만 아니라 계산 방법이 수정되는 일이 생기면 각각 클래스나 구조체에 복사해둔 연산 프로퍼티를 일일이 찾아 직접 수정해야 했고 이러한 단점을 해결하기 위해 나온 개념이다.
- 프로퍼티 래퍼는 기본적으로 연산 프로퍼티 기능을 개별 클래스와 구조체와 불리할 수 있게 하며, 앱 코드에서 재사용할 수 있게 한다.
//기존 구조체
struct Address{
private var cityname: String //밖에서는 city로만 접근, 안에서는 cityname으로 사용
var city:String {
get { cityname }
set { cityname = newValue.uppercased() }
}
}
//프로퍼티 레퍼
//연산프로퍼티 대신에 이 로직을 프로퍼티 래퍼로 구현 가능
@propertyWrapper //클래스나 구조체 안에 구현됨
struct FixCase {
private(set) var city:String = ""
var WarappedValue: String{ //모든 프로퍼티 래퍼는 값을 변경하거나 유효성을 검사하는 게터,세터 코드가 포함된 wrappedValue 프로퍼티를 가져와야 한다.
get {value}
set {value = newValue.uppercased()}
}
init(wrappedValue initialValue: String){
self.wrappedValue = initialValue
}
}
//프로퍼티 래퍼의 사용법
struct Contact {
@FixCase var name: String
@FixCase var city: String
@FixCase var country: String
}
2. 여러변수와 타입 지원하기
- 앞에 예제의 프로퍼티 래퍼는 할당되는 값의 형태로 단 하나의 값을 받았다.
- 여러 값을 받도록 좀 더 복잡한 프로퍼티 래퍼를 구현할 수 있다.
- 추가되는 값들을 프로퍼티 래퍼 이름 다음의 괄호 안에 둔다.
struct Demo {
@MinMalVal(min:100, max:200) var value: Int = 100 //value는 최소 10, 최대 100의 범위 안에 있는지 확인
}
var demo = Demo()
demo.value = 150
print(demo.value) //출력값 : 150
demo.value = 250
print(demo.value) //출력값 : 200
- 프로퍼티 래퍼의 목적은 비교 작업을 하는 것이므로, Foundation 프레임워크에 포함된 Comparable프로토콜을 따르는 것으로 작성하면 모든 타입의 비교가 가능하다.
struct Demo {
@MinMaxVal(min: "Apple", max: "Orange") var value: String = ""
}
var demo = Demo()
demo.value = "Banana"
print(demo.value)
var demo = Demo()
demo.value = "Orange"
print(demo.value)
struct DateDemo {
@MinMaxVal(min: Date(), max: Calendar.current.date(byAdding:.month, value:1, to:Date()!) var value: Date = Date())
}
3. 프로퍼티 래퍼 실습
import Foundation
@propertyWrapper //1. propertyWrapper 만들기
struct LowerWrapper {
var value: String = ""
var wrappedValue: String {
get {value}
set {value=newValue.lowercased()}
}
init(wrappedValue data: String ) {
self.wrappedValue = data
}
}
struct MyComputer {
@LowerWrapper var kind: String //2. propertyWrapper 사용이 필요한 프로퍼티 앞에 @로 선언해주기만 하면 사용 가능
var dateOfPurchase: String
var price: Int
init(kind:String, date:String, price:Int){
self.kind = kind
dateOfPurchase = date
self.price = price
}
func displayComputer() -> String{
return "kind = \(kind) , dateOfPurchase = \(dateOfPurchase) , price = \(price)"
}
}
var myMac: MyComputer = MyComputer(kind:"MacBook", date:"2022-09-29", price:100)
print(myMac.kind)
https://zeddios.tistory.com/1221
Property Wrapper
안녕하세요 :) Zedd입니다. 오늘은 Property Wrapper에 대해서 공부해보겠습미당 # Property Wrapper Swift 5.1에서 추가된 기능입니다. 직역하면 프로퍼티를 감싸는 그런 느낌인데..프로퍼티를 감싼다는게
zeddios.tistory.com
질문 : 각 init에 프린트문을 넣어봤는데 프로퍼티래퍼 init에 프린트부분이 먼저 나오는데 이게 맞을까요?
답변 : 구조체의 init 안에서 매개변수값을 프로퍼티래퍼가 적용된 프로퍼티에 할당하는 코드가 걸리는 순간, 그때가 프로퍼티래퍼의 init이 가동되는 타이밍이에요. 물론 래퍼 자체를 선언문으로 만들어줄때 init이 걸릴 수는 있습니다. 거기에 초기화 코드가 함께 들어가버리면요. @래퍼 var 프로퍼티이름: 타입 = 타입() <- 이러면 프로퍼티래퍼의 init이 먼저 가동되겠죠.
<Swift 컬렉션(배열, 딕셔너리, 세트) 작업>
- Swift의 배열과 딕셔너리는 다른 객체들의 집합을 담을 수 있는 객체이다.
1. 가변형 컬렉션과 불변형 컬렉션
- Swift에서 컬렉션은 가변형(mutable)과 불변형(immutable)이 있다.
- 불변형 컬렉션 인스턴스에 속한 것은 객체가 초기화된 이후에 변경될 수 없다.
- 불변형 컬렉션을 만들고 싶다면 컬렉션을 생성할 때 상수에 할당, 가변형은 변수에 할당
2. 배열
- 배열은 하나의 순서가 있는 컬렉션(=다른 컬렉션과 차이점)에 여러 값을 담기 위하여 특별하게 설계된 데이터 타입니다.
- Swift의 배열은 기본적으로 동일한 타입의 값들만 저장할 수 있지만, 여러 타입이 혼합된 배열을 생성할 수도 있다.
- 배열의 타입은 탕입 어노테이션(type annotation)을 이용하여 구체적으로 지정할 수도 있고, 타입추론(type inference)를 이용하여 컴파일러가 식별하게 할 수도 있다.
array.isEmpty : Bool타입 / 프로퍼티 / 배열이 비었는지 확인 및 반환
array.count : 배열의 원소 수를 반환하는 메서드
array[0] : 인덱스 첨자(index subscripting)라 불리는 기술을 이용하여 배열 인덱스의 항목 위치를 참조하여 배열의 특정 항목에 접근하거나 수정할 수 있다.
3. 배열 항목 섞기와 무작위로 가져오기
array.shuffled() : 배열 객체의 shuffled() 메서드를 호출하면 항목의 순서가 무작위로 섞인 새로운 버전의 배열이 반환
array.randomElement() : 배열 객체의 한가지 값을 무작위로 추출
4. 배열 값 추가
<맨 뒤에 삽입>
array.append("name")
array += ["name"]
array += ["name", "date", "inforamtion"]
<맨 앞에 삽입>
array.insert("name", at: 0)
array = ["name"] + array
<특정 위치 값 삽입>
array.insert("name", at: 2)
5. 배열 값 삭제
<특정 위치 값 삭제>
array.remove(at: 1)
<맨 뒤에 삭제>
array.removeLast()
* 타입이 혼합된 배열 생성은 지양해야 한다. 피하자!
6. 딕셔너리
- key-value 쌍의 형태로 데이터를 저장하고 관리할 수 있게 해준다.
- 배열과 비슷하지만 딕셔너리에 저장된 각 항목은 연관된 값을 참조하고 접근하는 데 사용되는 유일한 키(키는 특정 딕셔너리 객체에서 유일하다.)와 연결되어 있다는 점이 다르다.
- 현재는 String, Int, Double, Bool 타입만 Swift 딕셔너리에 키로 사용할 수 있다.
- 딕셔너리 순서가 없는 단일 컬렉션에 여러 값을 담기 위해 설계된 특별한 타입이다.
7. 딕셔너리 값 삭제
dict["key"] = nil : 특정 key에 nil을 할당하여 값을 삭제
dict.removeValue(forKey: "key") : removeValue(forKey:) 메서드를 사용하여 특정 키에 해당하는 값 삭제
3. 오늘의 리뷰
:)
'멋쟁이사자처럼 앱스쿨 1기' 카테고리의 다른 글
[멋쟁이사자처럼] 앱스쿨 1기 - 클로저&이스케이프 클로저 (14일차 22.10.05) (4) | 2022.10.06 |
---|---|
[멋쟁이사자처럼] 앱스쿨 1기 - Swift 에러 핸들링&열거형(Enum)&제너릭(Generics) (13일차 22.10.04) (2) | 2022.10.04 |
[멋쟁이사자처럼] 앱스쿨 1기 - 클래스&프로토콜 (10일차 22.09.27) (1) | 2022.09.27 |
[멋쟁이사자처럼] 앱스쿨 1기 - Swift 함수&클로저 표현식(closure expression) (9일차 22.09.26) (3) | 2022.09.26 |
[멋쟁이사자처럼] 앱스쿨 1기 - Playgorund 문제풀이&Swift 제어흐름 (8일차 22.09.23) (0) | 2022.09.23 |