Swift API Design Guidelines - Special Instructions

고라니·2023년 10월 16일
0

TIL

목록 보기
43/67

이번에는 Swift의 공식 API Design Guide의 Special Instructions파트에 대해 알아보자

Special Instructions(특별 지침)

API에 튜플과 클로저가 드러나는 곳에서, 튜플의 멤버들에 라벨을, 클로저의 매개변수들에 이름을 지어 주자

튜플과 클로저 매개변수의 이름은 코드의 의미를 명확하게 전달하며, 문서화 시에도 참조가 가능하다.

/// 최소한 `requestedCapacity` 요소에 대해 고유하게 참조된 저장소를 보유
///
/// 더 많은 저장소가 필요한 경우, `allocate`는 할당할 최대 정렬된
/// 바이트의 수와 같은 `byteCount`로 호출
///
/// - 반환값:
///   - reallocated: 새로운 메모리 블록이 할당되었다면 `true`;
///     그렇지 않다면 `false`.
///   - capacityChanged: `capacity`가 업데이트되었다면 `true`;
///     그렇지 않다면 `false`.
mutating func ensureUniqueStorage(
  minimumCapacity requestedCapacity: Int,
  allocate: (_ byteCount: Int) -> UnsafePointer<Void>
) -> (reallocated: Bool, capacityChanged: Bool)

예시 코드 분석

  • 이 함수는 두 개의 매개변수를 가진다.
    : requestedCapcity, allocate클로저
  • 이 함수는 튜플을 반환한다.
    : (reallocated: Bool, capacityChanged: Bool)
    reacllocated: 새로운 메모리 블록이 할당되면 true 반환
    capacityChanged: capacity가 업데이트되면 true 반환
  • 이렇게 튜플의 각 요소에 이름을 부여하여 반환값을 사용할 때 이해하기 더쉬워진다. 함수 호출 후 반환된 튜플에서 .reallocated 를 사용하여 해당 값 접근 가능

클로저 매개변수의 이름 선택

  • 클로저는 일급 객체로서, 함수의 매개변수로 전달될 수 있음. 그러므로 클로저 내의 매개변수에도 이름을 부여하는 것이 중요
  • 이 이름은 클로저의 동작을 설명하는 데 도움을 줄 수 있음. 위의 코드에서 allocate 클로저의 매개변수로 byteCount라는 이름을 제공.
  • 그러나 Swift에서는 클로저의 인자 라벨이 호출 위치에서 지원되지 않기 때문에, 클로저 호출시 인자 라벨 사용 불가.

결론적으로 튜플의 각 요소나 클로저의 매개변수에 이름을 부여하여 코드의 명확성을 향상시키는것을 권장함.

overload 집합들에서 애매함을 피하기 위해, 제한사항이 없는 다형성을 특별히 신경쓰자

ex) Any, AnyObject, 제약 없는 제네릭 매개변수들

아래의 overload 집합들을 고려해보자

struct Array {
      // `self.endIndex`에 newElement를 넣는다
      public mutating func append(_ newElement: Element)

      // `self.endIndex`에 newElement의 구성요소를 순서대로 넣는다
      public mutating func append(_ newElements: S)
          where S.Generator.Element == Element
  }

첫 번째는 단일 요소를 후가하고, 두 번째는 여러 요소(시퀀스)를 추가한다. 이 두 메서드는 의미적으로는 동일하거나 관련이 있지만, Element 타입이 Any인 경우 문제가 발생한다. 왜냐하면 단일 요소와 요소의 시퀀스가 같은 타입을 가질 수 있기 때문

var values: [Any] = [1, "a"]
values.append([2, 3, 4]) // [1, "a", [2, 3, 4]] or [1, "a", 2, 3, 4]?

이렇게 Any 타입의 요소를 추가하는 경우 모호성이 발생한다.
이부분도 조금 이해가 안됐었다. 내가 이해한 바로는 위의 예시처럼 두 메서드가 같은 의미(배열에 요소를 추가) 때문에 동일한 형태를 가지고 있지만 인자 타입이 Any인 경우 둘 중 어떤 함수가 실행되어야 하는지 모호해진다.

이 모호성을 해결하기 위해 두 번째 append 오버로드의 이름을 더 명확하게 변경한다.

struct Array {
      /// Inserts `newElement` at `self.endIndex`.
      // `self.endIndex`에 newElement를 넣는다
      public mutating func append(_ newElement: Element)

      /// Inserts the contents of `newElements`, in order, at
      /// `self.endIndex`.
      // `self.endIndex`에 newElement의 구성요소를 순서대로 넣는다
      public mutating func append(contentsOf newElements: S)
          where S.Generator.Element == Element
  }

즉 함수 오버로드를 사용할 때 특히 무제한 다형성을 다룰 때는 모호성을 피하도록 명확한 API 네이밍이 필요할 수 있다. 주의하자

마치면서

iOS개발자로서의 기본은 아마 Swift 언어의 규칙과 원칙을 잘 알고 지키는 것이지 않을까라고 느끼게 되었다. 이번에 Swift API Design Guidelines를 살펴보면서 이미 알고 있던 내용은 물론, 처음 접하는 내용들도 많았다. 새로 배운 내용들을 통해 지금까지 잘못 작성해온 코드들이 생각났다.
한 번에 모든 가이드라인을 완벽하게 적용하기는 어려울 것 같다. 하지만 조금씩 내 코드에 반영해보려고 한다.

참고: Swift API Guidelines, yagom-academy

profile
🍎 무럭무럭

0개의 댓글