'iOS 프로그래밍/자료구조'에 해당되는 글 2건

Ch2. 스위프트 기본 데이터 구조의 활용

  • 스위프트의 표준 라이브러리는 데이터 타입, 컬렉션 타입, 함수와 메소드 그리고 다양한 목적에 부합하는 다수의 프로토콜 등을 제공
  • 2장 주요내용
    • 스위프트의 표준 라이브러리
    • 서브스크립트 구현 방식
    • immutability 데이터 타입의 이해
    • 스위프트와 Objective-C의 상호 관련성
    • 스위프트의 프로토콜 지향 프로그래밍

스위프트 표준 라이브러리의 활용

  • 스위프트의 표준 라이브러리는 스위프트 프로그래밍 언어와 구분되는 별개의 요소이자, 클래스, 구조체, 열거형, 함수, 프로토콜 등 스위프트 언어를 활용하기 위한 핵심 도구이다.

애플이 구조체를 사용하는 이유

  • 스위프트에 정의된 타입 대부분은 구조체(Value Type)

  • 구조체를 이용하여 클래스를 지원하도록 한 이유는

  • 상속, 초기화 해제객체, 레퍼런스 카운팅등 다양한 기능을 제공하는 클래스에 비해 훨씬 제한된 수의 기능을 제공하는 스위프트가 표준 라이브러리의 구성 요소로서 적합하기 때문

  • 또 구조체는 벨류 타입으로서 단 하나의 소유 객체만을 지니며, 새로운 변수에 할당하거나 함수에 전달할 때는 항상 복사해서 사용한다는 점도 중요한 이유

  • 스위프트의 구조체의 주요기능

    • 자동으로 생성되는 멤버 초기화 함수(memberwise initializer)외에, 커스텀 초기화 함수도 사용 가능
    • 메소드를 지닐 수 있음
    • 프로토콜을 구현할 수 있음
  • 아래 조건중 하나라도 해당된다면 클래스보다는 구조체를 사용하는 편이 낫다고 애플 가이드라인은 설명한다.

    • 특정 타입 생성의 가장 중요한 목적이 간단한 몇 개의 값을 캡슐화 하는것인 경우
    • 캠슐화한 값을 구조체의 인스턴스에 전달하거나 할당할 때 참조가 아닌 복사를 할 경우
    • 구조체에 의해 저장되는 프로퍼티를 참조가 아닌 복사를 위한 벨류 타입인 경우
    • 기존의 타입에서 가져온 프로퍼티나 각종 기능을 상속할 필요가 없는 경우

스위프트에서 배열 선언

  • 스위프트의 배열과 Objective-C의 배열의 몇가지 중요한 차이점

    • 반드시 동일 타입의 값만 저장해야 한다는 것
    • 클래스 타입이 있음(스위프트 배열은 generic type collections)
    • 클래스가 아닌 구조체로서 정의됨
  • 스위프트에는 다음과 같은 세가지 유형의 배열이 있음

    • Array
    • ContiguousArray
    • ArraySlice
  • 모든 Array 클래스는 배열에 포함된 배열 요소를 저장하기 위한 메모리 공간을 유지한다.

  • 배열 요소의 타입이 클래스 또는 @objc 프로토콜 타입이 아닌 경우, 배열의 메모리 영역은 인접 블록에 저장된다.

  • 배열 요소의 타입이 클래스 또는 @objc 프로토콜 타입인 경우, 배열의 메모리 영역은 인접 블록에 NSArray의 인스턴스 또는 NSArray의 서브클래스의 인스턴스로 저장된다.

  • 저장하려는 배열 요소가 클래스 또는 @objc 프로토콜인 경우 ContiguousArray 유형을 사용하면 좀더 효율적인 코드를 작성할 수 있다.

  • ContiguousArray가 Array와 다른 점은 Objective-C와의 브릿징을 지원하지 않는다는 것

  • ArraySlice 도 배열요소를 저장할 때 인접 메모리 공간을 사용하며, Objective-C와의 브릿징을 지원하지 않는다.

  • ArraySlice의 가장 큰 특징은 이미 존재하는 또 다른 배열 타입의 일부 그룹을 대표한다는 것

  • Array, ContiguousArray, ArraySlice의 인스턴스를 생성하면 해당 배열 요소를 저장하기 위한 추가 저장공간이 할당된다.

  • 스위프트의 배열은 기하급수적 증가전략을 따르며, 배열에 요소가 추가될 때마다 소진된 배열 용량을 자동으로 증가시킨다.

  • 배열 요소 추가작업을 여러차례 나눠서 반복적으로 진행할 경우, 각각의 추가 작업에는 일정한 시간이 소요된다.

  • 위 예제의 실행결과를 보면 500개의 용량을 예약 했지만 실제 할당된 공간은 그보다 크다는 사실을 알 수 있다.
  • 스위프트가 실행 성능을 고려해서 실제 요청한 양 이상을 할당한 것으로 볼수 있으며, 최소한 예약한 배열 용량만큼은 확보할 수 있음을 알 수 있다.

배열 초기화

  • 스위프트 표준 라이브러리는 세 가지 타입의 배열을 구현하기 위해 네 가지 초기화 메소드를 제공 

배열에 요소 추가 및 업데이트

  • 배열에 새로운 요소를 추가할 때는 append(_:) 메소드를 사용한다.
  • 이 메소드는 배열의 맨 마지막 부분에 새로운 요소를 추가한다.
  • 특정한 배열 인덱스 위치에 요소를 추가하려 할 경우 insert(newElement:at:) 메소드를 사용한다.
  • 이 메소드는 newElement를 인덱스 값이 i인 위치에 삽입한다.
  • 특정 인덱스 위치의 배열 요소를 교체하려 할 경우 서브스크립트 문법을 사용할 수 있다 

배열에서 요소 가져오기 및 삭제

  • 배열에서 특정 요소를 가져오기 위한 방법은 여러가지가 있다.
  • 해당 배열의 인덱스를 알고 있거나 인덱스의 범위를 알고 있는 경우 배열 서브스크립트 기법을 사용할 수 있다. 

딕셔너리 가져오기 및 초기화 하기

  • 딕셔너리는 동일한 데이터 타입이 키와 값 쌍으로 묶여 있는 무순위 컬렉션이며(unordered collection) 순위를 별도로 지정할 수 있는 방법은 없다. 각각의 값은 딕셔너리 내에서 해당 값의 이름표(identifier)와 같은 역할을 하는 키와 연결돼 있다.
  • 딕셔너리의 키 타입은 Hashtable 프로토콜에 부합해야만 한다.

딕셔너리 초기화하기

  • 딕셔너리 역시 배열과 마찬가지로 정식 선언 문법과 단축 선언 문법이 있다. 

키/값 쌍 추가, 변경, 삭제

  • 새로운 키/값 쌍을 추가하러가 기존의 쌍을 업데이트 하려고 할 경우, updateValue(_:forkey:) 메소드 또는 서브스크립트 문법을 사용할 수 있다
  • 혹시 키가 존재하지 않는 경우, 새로운 키/값 쌍이 추가되며, 기존의 새로운 값으로 업데이트 된다.
  • 서브스크립트 문법과 달리 updateValue(_:forkey:)메소드는 교체된 값을 반환하거나 새로운 키/값 쌍이 추가된 경우 nil을 반환한다.
  • 키/값 쌍을 삭제하려면 removeValue(forkey:) 메소드에서 삭제하려는 값의 키를 입력한다.
  • 서브스크립트 문법에서는 키에 nil을 전달하면 해당 대상이 삭제된다.
  • 서브스크립트 문법과 달리 removeValue(forkey:)메소드는 삭제된 값을 반환하거나, 키가 존재하지 않을 경우 nil을 반환한다. 

딕셔너리에서 값 가져오기

  • 서브스크립트 문법을 이용해서 딕셔너리에서 특정 키/값 쌍을 가져올 수 있다.

  • 이때 키는 서브스크립트의 대괄호 속에 넣어서 전달하는데, 해당 키가 딕셔너리 상에 존재하지 않을 경우, 서브스크립트 옵셔널을 반환한다.

  • 이때 옵셔널 바인딩 또는 강제 언래핑을 통해 해당 키/값 쌍을 가져오거나, 해당 키/값 쌍이 없다고 결론 지을수도 있다 

  • 특정 값을 가져오는 대신, 딕셔너리를 반복적으로 순회하며 명시적으로 키/값을 분할해서 사용할 수 있는 (key, value)튜플을 반환하도록 할 수도 있다 -딕셔너리의 출력결과는 데이터의 삽입순서와 일치하지 않을 수 있음. 딕셔너리는 무순위 컬렉션이며, 딕셔너리의 순회에 따라 반환되는 결과물은 반드시 삽입된 순서를 따르지 않는다

  • 딕셔너리의 키 또는 값만을 개별적으로 가져오고 싶다면, 딕셔너리의 keys 프로퍼티 또는 values프로퍼티를 사용

  • 이들 프로퍼티는 컬렉션에 대응하는 LazyMapColletion인스턴스를 반환한다.

  • 이렇게 반환된 딕셔너리 요소는 기본 요소에 포함된 변환 클로저 함수의 호출에 의해 정보를 읽을 때마다 지연처리되고, 이때의 키와 값은 각각 .0 멤버와 .1 멤버가 되어 딕셔너리의 키/값 쌍과 동일한 순서대로 나타난다.

 

  • 딕셔너리는 기본적으로 무순위 컬렉션이지만, 가끔은 딕셔너리를 순회하면서 순위 목록으로 정돈해야 할 경우도 있음
  • 이런경우 전역 메소드인 sort(_:)를 이용 

세트 선언

  • 세트는 서로 중복되지 않고 nil이 포함되지 않은 non-nil 순위를 정의할 수 없는 순위 컬렉션이다.
  • 세트는 형식상 hashable 프로토콜에 부합해야 하며, 스위프트의 모든 기본 타입은 기본적으로 hashable 프로토콜을 따른다. 연관 값을 사영하지 않는 열거형의 case값 역시 기본적으로 hashable 프로토콜을 따른다.
  • 커스텀 타입 저장시에도 해당 타입은 반드시 hashable 프로토콜과 equatable프로토콜에 부합해야만 한다
  • hashable프로토콜은 equatable프로토콜을 상속한 프로토콜이기도 하다.
  • 세트틑 배열에 비해 매우 효율적이며, 데이터 접근 속도 역시 훨씬 빠르다.

세트 초기화

  • 세트의 경우 다른 컬렉션 타입과 달리 그에 포함된 배열 요소의 타입을 스위프트가 추측하지 않으므로 개발자가 직접 Set 타입을 명시적으로 선언해야만 한다. 

세트요소 변경 및 가져오기

  • 세트에 새로운 요소를 추가하려면 insert(_:)메소드를 사용하고
  • 특정요소가 이미 세트에 포함돼 있는지 여부를 확인하려 할 때는 contains(_:)메소드를 사용한다.
  • 삭제하려는 요소의 인스턴스를 알고 있을 때는 remove(_:)메소드를 사용
  • 세트내 특정 요소의 인덱스값을 알고 있다면 remove(at:)메소드를 사용하여 인스턴스를 삭제할수 있다.
  • 세트내 모든 요소를 삭제하려 할 때는 removeAll() 메소드 또는 removeAll(keepCapacity)메소드를 사용할수 있다
  • keepCapacity가 차민 경우 현재의 세트 용량은 감소하지 않는다.

  • 배열이나 딕셔너리와 마찬가지로 for..in 순환문을 이용해서 세트 내 요소를 순회하며 각종 임무를 수행할 수 있다.
  • 스위프트의 세트 타입은 무순위 이지만 sort 메소드를 이용해 정렬가능하다

세트 연산자

  • 세트는 수학에서의 집합 개념을 기반으로 만든 타입
  • 수학의 집합 연산과 같이 두 개 세트의 비교를 위해 다양한 메소드를 제공하고, 두 개 세트의 멤버십 연산과 동등 연산 기법도 제공

세트의 비교연산

  • 스위프트의 세트 타입은 두 개 세트 간의 연산을 위해 합집합, 교집합 연산을 포함 네 개의 연산 메소드를 제공
  • union, formUnion(_:)메소드는 새로운 세트를 만들고, 두개 세트의 합집합으로 원본 세트를 업데이트
  • intersection, formIntersetion(_:) 메소드는 새로운 세트를 만들고 두 개 세트의 교집합으로 원본 세트를 업데이트
  • symmetricDifference, formSymmetricDifference(_:) 메소드는 새로운 세트를 만들고, 두 개 세트의 여집합 요소로 원본 세트를 업데이트
  • subtracting, subtract(_:)메소드는 새로운 세트를 만들고 두 개 세트의 차집합 요소로 원본 세트를 업데이트 

부분 집합 및 동등 연산자

  • 두 개의 세트에 속한 내부 요소가 서로 안전히 같을 경우, 두 세트는 동등하다고 표현한다.
  • 세트는 원래부터 무순위 컬렉션이므로 내부 요소의 순서는 동등 여부 판단에서 별 의미가 없다.
  • 두 개의 세트가 같음을 표현하기 위해 동등 연산자인 == 연산자를 사용
  • 세트에서 다음 메소드도 활용 가능
    • isSubset(of:) : 어떤 세트의 요소가 특정 세트에 모두 포함돼 있는지 확인
    • isStrictSubset(of:) : 어떤 세트의 요소가 특정 세트에 모두 포함돼 있지만, 동등 집합은 아님을 확인
    • isSuperset(of:) : 특정 세트의 모든 요소가 또 다른 세트에 모두 포함돼 있는지 확인
    • isStrictSuperset(of:) : 특정 세트의 모든 요소가 또 다른 세트에 모두 포함돼 있지만, 동등 집합은 아님을 확인
    • isDisjoint(with:) : 두 세트에 공통 요소가 포함돼 있는지 여부를 확인

튜플의 특징

  • 튜플은 스위프트에는 있지만 Objective-C에는 없는 고급타입
  • 튜플은 컬렉션 타입은 아니지만, 컬렉션 타입과 매우 비슷한 특징을 지님
  • 튜플에는 하나 이상의 데이터 타입을 함께 담을수 있으며, 내부 요소들이 배열등 다른 컬렉션 타입과 달리 모두 같은 타입일 필요는 없다.
  • 튜플은 컬렉션이 아니기 때문에 SwquenceType프로토콜에 부합할 필요가 없으며, 내부 요소의 순회는 불가능 하다.
  • 튜플은 한 무리의 데이터를 저장하거나 전달하기 위한 목적으로 사용된다
  • 튜플은 구조체를 사용하지 않고도 함수에서 하나의 값으로 여러타입의 데이터를 반환 해야 하는 경우에 특히 유용

무기명 튜플

  • 튜플은 어떤 숫자 또는 어떤 데이터 타입을 조합해서 만들 수 있다.
  • 이때 타입에 대한 정보는 추측해서 판단하며, 튜플을 입력하면 컴파일러가 스스로 타입을 결정한다.
  • 리터럴 타입에 대한 판단을 컴파일러에게 맡기는 것을 원치 않는다면 직접 명시적으로 타입을 선언하면 된다.
  • 튜플의 개별 요소에 접근할 수 있는 방법은 인덱스 값을 쓰는 방법, 그리고 개별요소를 상수 또는 변수로 분할하는 방법 두가지이다. 

기명 튜플

  • 기명 튜플은 개별 요소에 이름을 붙일 수 있는 튜플이다.

  • 코드를 더 이해하기 쉽게 해주며, 메소드를 통해 튜플을 반환할 때 특정 인덱스 위치에 어떤 값이 있는지 알기 쉽게 도와준다.

  • 기명 튜플 역시 튜플 타입을 명시적으로 선언할 수 있다 

  • 튜플은 메소드를 통해 구조화된 값을 임시로 전달할 때 특히 유용한 타입이다.

  • 메소드에 의해 튜플을 반환할 때는 클래스를 정의할 때 필요한 정보 혹은 여러 타입의 값을 지니고 있는 딕셔너리를 이용할 때 필요한 정보와 같은 것을 추가적으로 전달할 수 있다. 

서브스크립팅 구현

  • 서브스크립트 기법으로도 클래스, 구조체, 그리고 열거형을 모두 정의할 수 있다.
  • 서브스크립트는 컬렉션, 리스트, 시퀀스 타입의 개별요소에 접근할 수 있는 지름길을 제공하며 문법은 좀 더 간소해질 수 있다.
  • 서브스크립트에서는 특정 인덱스를 지정하기 위해 set과 get요소를 사용한다.

서브스크립트 문법

  • 서브스크립트는 하나 혹은 그 이상의 입력 파라미터를 받을 수 있으며, 이때의 파라미터는 서로 다른 타입이어도 무방하고, 반환하는 값 역시 어떤 타입이든 가능하다.
  • 서브스크립트 정의에는 subscript키워드를 사용하며, 읽기전용 속성으로 정의하거나 특정요소에 접근하기 위한 getter 또는 setter를 설정할 수 있다. 

서브스크립트 옵션

  • 클래스와 구조체는 필요한 만큼 많은 서브스크립트를 반환할 수 있으먀, 이와 같은 다중 서브스크립트 지원방식을 서브스크립트 오버로딩이라 부른다.

수정 가능 속성과 수정 불가 속성의 이해

  • 스위프트에서는 타입을 정할 때 수정 가능, 수정 불가능 속성을 별도로 정할 필요가 없다.
  • var 키워드를 사용해서 배열, 세트, 딕셔너리 변수를 만들면, 이들 변수는 기본적으로 수정 가능 객체가 된다.
  • let 키워드를 사용해서 배열, 세트, 딕셔너리 등을 만든다면 이들은 상수인 객체가 된다.

컬렉션의 수정가능 속성

  • 컬렉션 타입으로 작업하려 할 때는 스위프트가 구조체와 클래스에서 수정 가능 속성을 어떻게 처리하는지 미리 알고 있어야 한다.
  • 구조체 인스턴스를 만들거나 이를 상수에 할당할 때는 이들이 변수로 선언됐다 하더라도 해당 인스턴스의 프로퍼티는 수정할 수 없다.
  • 클래스 인스턴스를 생성하고 이를 상수에 할당하면, 해당 인스턴스를 또 다른 변수에는 할당할 수 없지만, 해당 인스턴스의 프로퍼티는 수정 가능하다
  • 컬렉션을 변경할 필요가 없는 경우, 애플은 모든 컬렉션을 생성할 때 수정불가 속성으로 만들것을 권장, 이렇게 하면 컴파일러가 켈렉션과 관련된 코드를 처리할때 성능을 최적화 할 수 있다고 설명함

스위프트와 Objective-C의 상호 관련성

초기화 방식

  • 스위프트에서 Objective-C 프레임 워크를 사용하려면, 다음과 같이 코드 상단에 해당 프레임워크의 이름을 써서 임포트 하면 된다.
  • import Foundation

클래스 인스턴스를 메소드에 전달하는 방법

  • 스위프트 등에서 메소드라는 표현이 일반적이지만 Objective-C에서는 메시지 라는 개념을 사용한다.
  • Objective-C의 명령 실행 체계인 메시지는 리시버, 셀렉터, 파라미터 등의 요소로 구성된다.
  • 그 중 리시버는 메소드가 실행 결과를 받게 될 대상 객체이고 셀렉터는 메소드의 이름이며, 파라미터는 메소드에 전달되 실행될 객체를 의미한다.
  • Objective-C 메시지로 모델을 전달할 때는 보통의 컴파일 시점의 바인딩이 아닌 동적 바인딩 기법을 사용한다. 이렇게 하면 메시지를 미리 구현해 놓지 않고, 런타임에 해당 메시지를 구현 및 실행하는 것이 가능해진다.
  • 런타임에서 특정 객체가 즉각 메시지에 반응할 수 없는 경우에도 상속 연쇄는 해당 객체를 찾을 때까지 기다렸다가 메시지를 전달한다.
  • 하지만 결국 해당 객체를 찾을 수 없을 때는 nil을 반환하며, 이는 컴파일러 설정에서 변경할 수 있다.

스위프트 타입의 호환성

  • 스위프트 1.1부터 사용자가 직접 init메소드를 정의할 수 있는 실패가능 초기화(failable initialzation)기능을 추가
  • 이 패턴을 통해 이전보다 일관성 높은 구조 선언 문법을 제공
  • 초기화 객체 작성과 팩토리 메소드 생성과정의 혼돈과 불필요한 복제 가능성을 크게 줄일 수 있게 됐다.
  • 다음은 NSURLComponents클래스를 이용한 실패 가능 초기화 예제코드

  • 실패가능 초기화 기능은 Objective-C와 관련 프레임 워크를 이용해서 작업할 때 특히 유용
  • 다음은 스위프트에서 만든 타입에 실패 가능 초기화 기능을 사용해서는 안되는 이유를 보여주는 코드

  • 구조체와 클래스를 개발하고자 한다면 다음의 SOLID원칙을 준수해야함

    • 단일 책임 원칙 : 하나의 클래스는 오직 단 하나의 책임만 부담해야 한다.
    • 개방과 폐쇄의 원칙 : 소프트웨어는 확장이라는 측면에서는 개방되어 있어야 하고, 수정이라는 측면에서는 폐쇄되어 있어야 한다.
    • 리스코프 대체 원칙 : 특정 클래스에서 분화돼 나온 클래스는 원본 클래스로 대체 가능해야 한다.
    • 인터페이스 세분화 원칙 : 개별적인 목적에 대응할 수 있는 여러개의 인터페이스가 일반적인 목적에 대응할 수 있는 하나의 인터페이스보다 낫다.
    • 의존성 도치의 원칙 : 구체화가 아닌 추상화를 중시한다.
  • 접근자 함수를 이용해서 이미지 생성 및 NSImage와 관련된 오류를 구조체가 처리할 수 있도록 구현한 초기화 객체코드

컬렉션 클래스 브릿징

  • 스위프트는 파운데이션 컬렉션 타입인 NSArray, NSSet, NSDictionary를 스위프트의 배열, 세트, 딕셔너리 타입으로 브릿징 할 수 있도록 지원한다.

NSArray를 Array로 브릿징

  • NSArray를 파라미터화된 타입으로 브릿징하면 [ObjectType] 형식의 배열이 만들어지고, 별도의 파라미터화된 타입을 지정하지 않으면 [AnyObject]형식의 배열이 만들어진다.
  • [AnyObject]타입으로 브릿징된 NSArray를 활용해야 하는경우, 배열에 다른 타입이 포함되어 있을 때는 이들 인스턴스를 별도로 관리해야한다.
  • 스위프트는 이러한 것을 처리하기 위한 도구를 제공하는데 강제 언래핑과 타입캐스팅 연산자다
  • 만일 [AnyObject]배열에 서로 다른 타입이 포함되어 있는지 알지 못한다면 nil을 반환할 수 있는 타입 캐스팅 연산자를 사용해서 다른 타입의 인스턴스를 안전하게 관리할 수 있다.

NSSet을 set로 브릿징

  • 파라미터화된 타입으로 NSset을 브릿징하면 Set 타입의 세트가 만들어지고, 별도의 파라미터 타입을 지정하지 않고 브릿징 하면 Set타입의 세트가 만들어 진다.
  • 강제언래핑, 타입캐스팅연산자를 이용하여 서로 다른 타입이 포함된 세트를 안전하게 관리할 수 있다.

NSDictionary를 dictionary로 브릿징

  • 파라미터화된 타입으로 NSDictionary를 브릿징하면[ObjectType] 타입의 딕셔너리가 만들어지고, 별도의 파라미터 타입을 지정하지 않고 브릿징하면 [NSObject:AnyObject]딕셔너리가 만들어진다.

스위프트 프로토콜 지향 프로그래밍

명령 전달을 위한 디스패치 기법

  • 스위프트의 프로토콜은 Objective-C 프로토콜의 슈퍼세트이다.
  • Objective-C 에서 모든 메소드는 런타임시 메시지를 이용해서 다이나믹 디스패치 또는 동적 명령 전달 기법을 사용
  • 스위프트에서 메소드는 명령 전달을 위한 다양한 기법을 제공한다.
  • 스위프트는 기본적으로는 특정 클래스에서 사용 가능한 메소드 목록을 담은 vtable기법을 사용하는데 컴파일시 생성된 vtable은 인덱스 값으로 접근할 수 있는 함수 포인터를 포함한다.
  • 애플의 세번째 명령 전달 기법은 정적 디스패치이다.
  • 처리하려는 메소드와 관련된 정보가 충분하지 않을 경우, 컴파일러는 정적으로 명령을 전달하게 된다. 이 방식은 메소드 전체를 직접 호출하거나 해당 메소드를 아예 학제한다.

프로토콜 작성 문법

  • 프로토콜 선언 문법은 클래스, 구조체 선언과 매우 유사하다.

protocol Particle {
    var name: String { get }
    func particalAsImage() -> NSImage
}
  • 프로토콜을 선언할 때는 protocol 키워드 뒤에 프로토콜의 이름을 입력한다. 프로토콜 프로퍼티를 정의할 때는 get 속성인지, set속성인지, 혹은 둘 다인지 구체적으로 밝혀야 한다. 그리고 메소드를 정의할 때는 이름, 파라미터, 반환타입을 명시한다.
  • 메소드에서 구조체의 멤버변수가 변경될 수 있으면 메소드 선언시 mutating 키워드를 추가해야 한다.
  • 프로토콜은 하나 혹은 그 이상의 다른 프로토콜을 상속할 수 있다.
  • 또 여러개의 프로토콜을 프로토콜 컴포지션으로 묶어서 하나처럼 사용할 수 있다. 프로토콜 컴포지션을 만들 때는 여러개의 프로토콜 이름을 & 기호로 연결하면 된다. 하지만 이는 새로운 프로토콜을 정의한 것이 아니며, 한데 묶인 프로토콜의 모든 규칙과 요구사항을 임시로 적용하기 위한 방법일 뿐이다.

타입으로서의 프로토콜

  • 프로토콜을 이용해서 뭔가를 직접 구현하지 않더라도, 타입이 필요한 곳에 프로토콜을 하나의 타입으로서 사용할 수 있다. 대략적인 용도는 다음과 같다.
    • 함수, 메소드, 초기화 객체에서 반환타입 또는 파라미터 타입
    • 배열, 세트, 딕셔너리에서 개별 아이템 타입
    • 변수, 상수, 프로퍼티의 타입

프로토콜 익스텐션

  • 프로토콜 익스텐션을 이용하면 소스 코드 작성자가 아니더라도 기존 프로토콜의 기능을 확장할 수 있다.
  • 익스텐션을 통해 기존 프로토콜에 새로운 메소드, 프로퍼티, 서브스크립트를 추가할 수 있다.

컬렉션에서 활용하기 위한 프로코콜의 검증

Array 리터럴 문법

  • ExpressibleByArrayLiteral 프로토콜은 배열 형식의 문법을 이용해서 구조체, 클래스, 열거형을 초기화할 수 있도록한다.

열거형 배열 만들기

  • Sequence프로토콜과 IteratorProtocol 은 for...in 문법을 이용해서 컬렉션을 순환하는 것과 같은 기능을 제공한다.
  • 이때 사용되는 데이터 타입은 컬렉션의 순회 기능을 사용하기 위해 열거형을 지원해야 한다.

Sequence/IteratorProtocol

  • 이들 두개의 컬렉션 프로토콜은 서로 뗄 수 없는 사이
  • 시퀀스는 일련의 연속된 값을 나타내고, 반복기 Iterator는 시퀀스에서 이들 값을 한번에 하나씩, 순서대로 사용할 수 있는 방법을 제공
  • Sequence 프로토콜에는 단 하나의 규칙이 있는데 모든 시퀀스는 makeIterator() 메소드로 만들 반복기만 지원해야 한다는 것


블로그 이미지

百見 이 不如一打 요 , 百打 가 不如一作 이다.

,

Ch.1 플레이그라운드 살펴보기

데이터 구조의 중요성

  • 데이터 구조는 프로그래밍에 있어 효율적이며, 확장가능하고 유지 보수성 높은 시스템을 만들기 위한 주요요소중 하나로서, 시스템에서 데이터의 공유, 유지, 정렬, 검색등 데이터 활용을 위한 데이터의 체계화 방법이다.

데이터 구조 + 알고리즘 = 프로그램

  • 데이터의 추상화(abstraction)기법은 데이터가 지닌 복잡성을 관리하기 위한 기술이다.
  • 데이터 구조를 디자인할 때 데이터 추상화 기법을 사용하는데, 이는 개발자가 애플리케이션을 만들려고 할 때 내부의 상세한 구현 방식을 몰라도 되도록 하기 위함이다.
  • 내부의 복잡한 구현 방식을 드러내지 않음으로써 개발자는 알고리즘이 제공하는 인터페이스의 활용에 더욱 집중할 수 있으며, 이 때문에 데이터 구조는 프로그램 내부에서 구현하게 된다.

기본적인 데이터 구조

  • 데이터 구조의 가장 근원적인 형태는 사실상 배열과 포인터 두가지 타입이며, 다른 데이터 구조는 여기서 파생된다고 할 수 있다.
    • 인접 데이터 구조(Contiguous Data Structures) : 데이터를 메모리 영역 중 인접한 부분에 저장한다
      • Arrays, Heaps, Matrices, Hash Tables 등이 있다.
    • 연결 데이터 구조(Linked Data Structures) : 서로 명확히 구분되는 메모리 영역을 차지하되, 포인터라는 주소 체계로 연결, 관리되는 구조이다
      • Lists, Trees, Graphs 등이 있다.

인접 데이터 구조

[1] 배열(Array)

  • 일차원배열(선형배열)
    • swift에서 배열의 인덱스 값은 0부터 시작하며, 데이터 간에 순서가 있으먀, 임의로 특정 요소에 접근할 수 있는 데이터 집합의 성질을 지닌다.
  • 다차원배열
    • 행렬은 다차원 배열의 대표적인 형식

[2] 배열 선언

  • var myIntArray: Array = [1,3,5,7,9] / 기본형
  • var myIntArray: [Int] = [1,3,5,7,9] / 축약형
  • var myIntArray = [1,3,5,7,9] / 타입추측
  • var my2Darray =[[Int]] = [[1,2],[10,11],[20,30]] / 다중 배열

[3] 배열 요소 가져오기

6> var myIntArray: [Int] = [1,3,5,7,9]
myIntArray: [Int] = 5 values {
  [0] = 1
  [1] = 3
  [2] = 5
  [3] = 7
  [4] = 9
}
  7> var someNumber = myIntArray[2]
someNumber: Int = 5
  8> for elemebt in myIntArray {
  9.     print(elemebt)
 10. }
1
3
5
7
9
 11> var someSubset = myIntArray[2...4]
someSubset: ArraySlice<Int> = 3 values {
  [2] = 5
  [3] = 7
  [4] = 9
}

12> var my2dArray: [[Int]] = [[1,2],[10,11],[20,30]]
my2dArray: [[Int]] = 3 values {
  [0] = 2 values {
    [0] = 1
    [1] = 2
  }
  [1] = 2 values {
    [0] = 10
    [1] = 11
  }
  [2] = 2 values {
    [0] = 20
    [1] = 30
  }
}
 13> var element = my2dArray[0][0]
element: Int = 1
 14> element = my2dArray[1][1]
 15> print(element)
11

[4] 배열 요소 추가

  • 기존 배열의 맨 끝 부분에 요소를 추가하는 방법
16> var myIntArray = [1,3,5,7,9]
myIntArray: [Int] = 5 values {
  [0] = 1
  [1] = 3
  [2] = 5
  [3] = 7
  [4] = 9
}
 17> myIntArray.append(10)
 18> print(myIntArray)
[1, 3, 5, 7, 9, 10]
  • 기존 배열의 특정 인덱스 위치에 요소를 삽입하는 방법
19> var myIntArray: [Int] = [1,3,5,7,9]
myIntArray: [Int] = 5 values {
  [0] = 1
  [1] = 3
  [2] = 5
  [3] = 7
  [4] = 9
}
 20> myIntArray.insert(4, at:2)
 21> print(myIntArray)
[1, 3, 4, 5, 7, 9]

[5] 배열 요소 삭제

  • 배열의 맨 끝 부분에 있는 요소를 삭제 하는 방법
22> var myIntArray: [Int] = [1,3]
myIntArray: [Int] = 2 values {
  [0] = 1
  [1] = 3
}
 23> myIntArray.removeLast()
$R2: Int = 3
 24> print(myIntArray)
[1]
  • 특정 인덱스 위치의 요소를 삭제하는 방법
25> var myIntArray: [Int] = [1,3,5,7,9]
myIntArray: [Int] = 5 values {
  [0] = 1
  [1] = 3
  [2] = 5
  [3] = 7
  [4] = 9
}
 26> myIntArray.remove(at: 3)
$R3: Int = 7
 27> print(myIntArray)
[1, 3, 5, 9]

연결 데이터 구조

  • 연결 데이터 구조는 데이터 타입과 이를 다른 데이터와 묶어주는 포인터로 구성된다. 여기서 포인터(pointer)란 메모리상의 위치 주소를 의미한다.
  • 스위프트는 직접적으로 포인터에 접근하지 않으먀, 포인터를 활용할 수 있는 별도의 추상 체계를 제공한다.
  • 연결 리스트(linked list)
    • 일련의 노드로 구성되며, 이들 노드는 링크 필드를 통해 서로 연결되어 있다.
    • 가장 간단한 형태의 연결 리스트는 데이터와 다음 노드에 연결할 수 있는 레퍼런스(또는 링크)정보를 포함한다.
    • 좀 더 복잡한 형태의 경우, 추가 링크 정보를 통해 연결된 데이터에서 앞 또는 뒤로 이동할 수 있다.
    • 연결 리스트에서 추가 노드를 삽입하거나 삭제하는 일은 매우 간단하다.

단일 연결 리스트

28> class LinkedList<T> {
 29.     var item: T?
 30.     var next: LinkedList<T>?
 31. }
  • 자세한 연결 리스트 구현은 3장으로..

데이터 구조의 종류와 단점

알고리즘 개요

  • 대부분의 데이터 구조에서 다음과 같은 내용은 필수적으로 파악하고 있어야 한다.

    • 새로운 데이터 아이템을 삽입하는 방법
    • 데이터 아이템을 삭제하는 방법
    • 특정 데이터 아이템을 찾는 방법
    • 모든 데이터 아이템을 순회하는 방법
    • 데이터 아이템을 정렬하는 방법

스위프트에서의 데이터 타입

벨류 타입과 레퍼런스 타입

  • 스위프트의 기본 데이터 타입은 벨류 타입과 레퍼런스 타입 두가지이다.
  • 벨류 타입은 오직 하나의 소유 객체만을 지니며, 해당 타입의 데이터가 변수 또는 상수에 할당됐을 때 혹은 함수에 전달됐을 때, 지니고 있던 값을 복사한다
  • 벨류 타입에는 다시 구조체와 열거형, 두 가지 유형이 있으며, 스위프트의 모든 데이터 타입은 기본적으로 구조체이다.
  • 반면, 레퍼런스 타입은 벨류 타입과 달리 값을 복사하지 않고 공유한다.
  • 즉, 레퍼런스 타입은 변수에 할당하거나 함수에 전달할 때 값을 복사해서 제공하는 대신 동일한 인스턴스를 참조값으로 활용한다.
  • 레퍼런스 타입은 여러 개의 소유 객체가 참조라는 방식으로 공유할 수 있다.

기명 타입과 복합 타입

  • 스위프트의 또 다른 데이터 타입 분류 체계는 기명 타입(named type) 과 복합 타입(compound type)이다.
  • 기명 타입은 사용자가 정의할 수 있는 데이터 타입이자, 해당 타입이 정의될 당시 특정한 이름을 부여할 수 있는 타입이다.
  • 기명 타입에는 클래스, 구조체, 열거형, 프로토타입이 있다
  • 사용자 정의 기명 타입 외에 스위프트 라이브러리에는 배열, 딕셔너리, 세트, 옵셔널 값을 나타낼 수 있는 기명 타입이 별도로 마련되어 있다.
  • 또한 기명 타입은 익스텐션 선언을 통해 동작 범위를 확장할 수 있다.
  • 복합 타입은 별도의 이름이 붙여지지 않은 타입이며, 스위프트에서는 function 타입과 tuple 타입 두개의 복합 타입이 정의 되어 있다.

타입 에일리어스

  • 타입 에일리어스는 기존의 타입을 또 다른 이름으로 부를 수 있는 방법을 제공한다
32> typealias TCPPacket = UInt16
 33> var maxTCPPacketSize = TCPPacket.max
maxTCPPacketSize: UInt16 = 65535

스위프트 표준 라이브러리 컬렌션 타입

  • 스위프트는 배열, 딕셔너리, 세트 등 세가지의 컬렉션 타입을 제공한다.
  • 이외에도 정식 컬렉션 타입은 아니지만, 복합적인 값을 한꺼번에 묶어서 사용할 수 있는 튜플도 있다.

점근적 분석

데이터 크기 분석 방법

func insertionSort(alist: inout [Int]){
    for i in 1..<alist.count {
        let tmp = alist[i]
        var j = i-1
        while (j >= 0 && alist[j] > tmp) {
            alist[j+1] = alist[j]
            j = j-1
        }
        alist[j+1] = tmp
    }
}

정리


블로그 이미지

百見 이 不如一打 요 , 百打 가 不如一作 이다.

,