본문 바로가기
ios 개발 일지

[패스트캠퍼스][나만의 iOS 앱 개발 입문] [ iOS개발 강의] 3주차 학습일지

by IGBR 2022. 1. 11.

벌써 Swift 이론 강의의 마지막 주차이다 앞으로는 지금 까지 배운 Swift 이론을 바탕으로 직접 iOS 개발을 해볼 것이라고 한다. Swift, 어렵긴 하지만 요즘트렌드에 참 잘 맞는다는 생각이 드는 언어이다. 3주차는 구조체와 메소드, 클래스 등을 공부하였다. 쉬운 예를 들어가며 강사분께서 설명해주셨기에 나름 쉽게쉽게 이해하며 넘어갔던것 같다.


구조체

Swift라는 언어에서 주로 구성되어있는 타입은 Struct(구조체) 이다. 

우리가 아는 흔한 Int 또한 구조체로 정의가 되어있다.

struct SturctName {
	// 실행 구문
}

프로퍼티 및 메소드의 구현

struct Sample {
	// 가변 프로퍼티 var로 지정
    var mutableProperty: Int = 100 
    
    // 불변 프로퍼티 let 상수로 지정
    let immutableProperty: Int = 100 
    
    // 타입 프로퍼티
    static var typeProperty: Int = 100 
    
    // 인스턴스 메서드
    func instanceMethod() {
        print("instance method")
    }
    
    // 타입 메서드
    static func typeMethod() {
        print("type method")
    }
}

구조체 사용법

// 가변 인스턴스 생성
var mutable: Sample = Sample()

mutable.mutableProperty = 200

// 불변 프로퍼티는 인스턴스 생성 후 수정할 수 없다
// 컴파일 오류 발생
//mutable.immutableProperty = 200

// 불변 인스턴스
let immutable: Sample = Sample()

// 불변 인스턴스는 아무리 가변 프로퍼티라도 인스턴스 생성 후에 수정할 수 없다
// 컴파일 오류 발생
//immutable.mutableProperty = 200
//immutable.immutableProperty = 200


// 타입 프로퍼티 및 메서드
Sample.typeProperty = 300
Sample.typeMethod() // type method

// 인스턴스에서는 타입 프로퍼티나 타입 메서드를 사용하지 못한다
// 컴파일 오류 발생
//mutable.typeProperty = 400
//mutable.typeMethod()

프로토콜

프로토콜 : Protocol(규약)은 특정 역할을 수행하기 위한 메소드, 프로퍼티, 기타 요구사항 등의 청사진을 정의한다. 구조체, 클래스, 열거형은 프로토콜을 채택(Adopted) 해서 특정 기능을 수행하기 위한 프로토콜의 요구사항을 실제로 구현할 수 있다. 어떤 프로토콜의 요구사항을 모두 따르는 타입은 그 프로토콜을 준수한다(Conform) 고 표현한다. 타입에서 프로토콜의 요구사항을 충족시키려면 프로토콜이 제시하는 청사진의 기능을 모두 구현해야 한다. 즉, 프로토콜은 기능을 정의하고 제시 할 뿐이지 스스로 기능을 구현하지는 않는다.

 

protocol 프로토콜이름 {
 // 프로토콜 정의
}

구조체 , 클래스 등에서 프로토콜을 채택하는 방법

- 타입 이름 뒤에 : 를 쓰고 ,(쉼표)를 구분하여 프로토콜을 나열 

struct SomeStruct: FirstProtocol, AnotherProtocol {
 // 구조체 정의
}
// 상속받는 클래스의 프로토콜 채택
class SubClass: SuperClass, FirstProtocol, AnotherProtocol {
 // 클래스 정의
}

주로 사용되는 프로토콜들

- CustomStrignConvertible

- 자기를 소개하는 문자열을 정의 한다. 

public protocol CustomStringConvertible {
  /// A textual representation of `self`.
  public var description: String { get }
}

- ExpressibleByIntegerLiteral(Int 리터럴)

- ExpressibleByStringLiteral(String 리터럴)

- ExpressibleByArrayLiteral(Array 리터럴)

- ExpressibleByDictionaryLiteral(Dictionary 리터럴)


프로퍼티

- 구조체, 클래스, 열거형에서 소속된 변수 및 속성(Attributes) 등으로 불리는 개념

프로퍼티의 종류는 크게 3가지가 있다.

- Stored Property(저장 프로퍼티)

- Computed Property(연산 프로퍼티)

- Type Property(타입 프로퍼티)

 

Stored Property(저장 프로퍼티)

- 객체의 값(속성)을 저장하고 있는 기본적인 프로퍼티

- 객체가 생성이 되면 자동적으로 초기화가 된다.

- 클래스, 구조체에서 지원, 열거형에선 쓰지 못한다.

- var로 선언하면 변수를 저장

- let으로 선언하면 상수를 저장, 선언 이후 변경 불가

 

Stored Property(저장 프로퍼티)의 구조체(Value Type) 예제 

- 여기서 'firstValue' , 'length'는 StoredProperty(저장 프로퍼티)이다.

struct FixedLengthRange {
    var firstValue: Int
    let length: Int
}

var rangeOfThreeItems = FixedLengthRange(firstValue: 0, length: 3)

rangeOfThreeItems.firstValue = 6
rangeOfThreeItems.length = 10//상수로 지정되어 있는 저장 프로퍼티의 값은 변경할수 없다.

 

var 로 선언한 'rangeOfThreeItems'를 let으로 선언한다면?

struct FixedLengthRange {

    var firstValue: Int
    let length: Int

}

let rangeOfThreeItems = FixedLengthRange(firstValue: 0, length: 3)

rangeOfThreeItems.firstValue = 6//error!
rangeOfThreeItems.length = 10//error!

/*
구조체는 Value타입으로 인스턴스가 Var로 선언 되어있다면 변수 프로퍼티(let 제외) 수정가능
하지만 let으로 인스턴스를 선언한다면 모든 프로퍼티는 var이던 let이던 수정불가
*/

 

Stored Property(저장 프로퍼티)의 Class(Reference Type) 예제

클래스로 구현시에 저장 프로퍼티에 초기값이 선언 되어 있지 않다면, 반드시 init을 구현해준다.  

class FixedLengthRange {

    var firstValue: Int
    let length: Int

    init(firstValue : Int, length:Int) {
        self.firstValue = firstValue
        self.length = length
    }
}

var rangeOfThreeItems = FixedLengthRange(firstValue: 0, length: 3)

rangeOfThreeItems.firstValue = 3
rangeOfThreeItems.length = 10//error!

/*
구조체의 경우와 같게 let으로 선언된 저장프로퍼티인 'length'는 수정 불가
*/

 

rangeOfThreeItems 인스턴스를 let으로 선언한다면?

class FixedLengthRange {

    var firstValue: Int
    let length: Int

    init(firstValue : Int, length:Int) {
        self.firstValue = firstValue
        self.length = length
    }
}

let rangeOfThreeItems = FixedLengthRange(firstValue: 0, length: 3)

rangeOfThreeItems.firstValue = 3
rangeOfThreeItems.length = 10//error!

/*
구조체의 경우와는 다르게 let으로 선언된 인스턴스에서도 firstValue의 값을 수정할 수 있다. 
클래스의 경우 Reference Type 이기에 원본에 바로 접근이 가능하다.
따라서 인스턴스가 let으로 선언 되어있다 하더라도 원본의 저장프로퍼티가 var인지 let인지에 따라 
수정이 가능 불가능이 갈린다.
*/

추가 ) Lazy Stored Property (게으른 저장 프로퍼티)

- 값이 사용되기 전까지는 값이 계산 되지 않는 프로퍼티

- var 인 저장프로퍼티만 사용이 가능하다 사용은 lazy 키워드를 사용한다.  ex) lazy var 저장프로퍼티이름

- 초기값이 인스턴스의 초기화가 될 때까지 값을 모르는 외부 요소에 의존하는 경우에 유용하다.

- 초기값이 복잡하거나 계산비용이 많이 드는 설정을 필요로 할 때도 유용하다.

- 성능향상과 공간 낭비를 줄이는 이점이 있다. 

 

Computed Property (연산 프로퍼티)

- 특정 연산을 통해 필요할 때 연산을 통해 값을 리턴

- 클래스, 구조체, 열거형에서 모두 사용가능

- 값이 변하는 var로 선언되어야함

- 실제 값을 저장하고 있는게 아님

- 반드시 연산 프로퍼티를 위한 저장프로퍼티 하나가 있어야 함

- get , set 동시에 구현 가능하며, get만 구현하는 것도 가능

- set에서 파라미터를 생략할 수 있으며, 인자 기본이름인 'newValue' 를 사용

 

연산 프로퍼티 예제

- var x는 연산 프로퍼티로 저장의 역할을 하지 못한다. 따라서 그 역할을 해줄 저장 프로퍼티인 tempX 선언 및 초기화

- x는 값을 읽을 때 tempX의 값을 가져온다.

- 새로 지정되는 값은 newValue 에 가져오고 set부분에서 연산뒤에 저장프로퍼티인 tempX에 저장한다.

- 밑 코드의 실행 결과 tempX 에는 24가 저장이 된다. 

class Point {
    var tempX : Int = 1
    var x: Int {
    
        get {
            return tempX
        }
        
        set(newValue) {
            tempX = newValue * 2
        }
    }
}

var p: Point = Point()
p.x = 12

 

이때 set(newValue) 부분의 newValue는 임의로 커스텀이 가능하다. 

set(kaka) {
    tempX = kaka * 2
}

 

set은 축약이 가능하다 이것을 Shorthand Setter Declaration 이라고 한다. 

- 주의할점은 반드시 newValue 라는 지정된 인자 이름을 사용하여야 한다.

set {
	tempX = newValue * 2
}

 

Property Observers (프로퍼티 감시자)

- 프로퍼티 값의 변경을 모니터링 할수 있다. 

- 자신이 정의한 '저장 프로퍼티'에 추가 할 수 있다. 

- superClass(부모클래스)를 상속한 프로퍼티에도 추가 할 수 있다. 

 

두가지의 프로퍼티 옵저버

- willSet : 값이 저장되기 직전에 호출된다.

- didSet : 새로운 값이 저장된 직후에 호출된다.


메소드

특정 타입의 클래스, 구조체, 열거형과 관련된 함수를 메소드라 한다. 특정 타입의 인스턴스에서 실행할 수 있는 메소드를 인스턴스 메소드, 특정 형과 관련된 메소드를 타입 메소드라 한다. 타입 메소드는 Objective-C에서 클래스 메소드와 유사하다. Swift에서 메소드가 C나 Objective-C 메소드 간의 가장 큰 차이는 바로 Objective-C에서는 클래스 타입에서만 메소드를 선언할 수 있다는 것 이다. 반면 Swift에서는 클래스 타입 뿐만아니라 구조체, 열거형에서도 메소드를 선언해 사용할 수 있다.

 

class Counter {
    var count = 0
    func increment() {
        count += 1
    }
    func increment(by amount: Int) {
        count += amount
    }
    func reset() {
        count = 0
    }
}

 

프로퍼티 vs 메소드 고르는 법


클래스

클래스와 구조체의 공통점과 차이

 

공통점

- 값을 저장할 프로퍼티를 선언할 수 있다.

- 함수적 기능을 하는 메소드를 선언 할 수 있다.

- 내부값에 .을 사용 하여 사용 할 수 있다.

- 생성자를 사용해 초기 상태를 설정할 수 있다.

- extension을 사용하여 기능을 확장 할 수 있다.

- protocol을 채택하여 기능을 설정할 수 있다.

 

차이점

 

클래스(Class)

- 참조 타입이다.

- ARC로 메모리로 관리한다.

- 같은 클래스 인스턴스를 여러 개의 변수에 할당한 뒤 값을 변경시키면 할당한 모든 변수에 영향을 준다. (메모리만 복사)

- 상속이 가능하다.

- 타입 캐스팅을 통해 런타임에서 클래스 인스턴스의 타입을 확인할 수 있다.

- deinit을 사용하여 클래스 인스턴스의 메모리 할당을 해제할 수 있다.

 

구조체(Struct)

- 값 타입이다.

- 구조체 변수를 새로운 변수에 할당할 때마다 새로운 구조체가 할당된다.

- 같은 구조체를 여러 개의 변수에 할당한 뒤 값을 변경시키더라도 다른 변수에 영향을 주지 않는다. (값 자체를 복사)

 

 

클래스와 구조체 중 무엇을 언제 쓸지 ?
클래스와 구조체 모두 프로그램의 코드를 조직화 하고 특정 타입을 선언하는데 사용된다. 클래스 인스턴스가 인자로 사용될 때는 참조가 넘어가고 구조체는 값이 넘어간다. 그럼 언제 클래스를 사용하고 언제 구조체를 사용해야 할까?

일반적으로 다음의 조건 중 1개 이상을 만족하면 구조체를 사용하는 것을 고려해 볼 수 있다.
  • 구조체의 주 목적이 관계된 간단한 값을 캡슐화(encapsulate) 하기 위한 것인 경우
  • 구조체의 인스턴스가 참조되기 보다 복사되기를 기대하는 경우
  • 구조체에 의해 저장된 어떠한 프로퍼티가 참조되기 보다 복사되기를 기대하는 경우
  • 구조체가 프로퍼티나 메소드 등을 상속할 필요가 없는 경우

클래스 상속

상속의 규칙

- 자식은 한개의 superClass만 상속 받음

- 부모는 여러 자식들을 가질 수 있음

- 상속의 깊이는 상관이 없음

 

상속을 고려할 만한 상황 5가지

 

- SIngle Resoponsibility(단일책임)

- Type Safety(타입이 분명해야 할 때)

- Shared Base Classes(다자녀가 있다)

- Extensibility(확장성이 필요한 경우)

- Identify(정체를 파악하기 위해)

 


이렇게 3주차도 끝이났다. 다음부턴 앱을 실제 개발하면서 진행이 되는데 수월한 진행을 위해 내가 헷갈렸던 부분이나 어려웠던 부분을 다시한번 짚고 넘어가야겠다. 화이팅!

 

 

댓글