티스토리 뷰
안녕하세요 :) Zedd입니다.
오늘은...!!! API Design Guidelines을 제대로 보려고 합니다!!!나름...Swift Convention을 잘 지키고 있다고 생각은 하는데.......
제대로 알고 있어야 할 것 같아서 :)
여기에 가시면 Swift에 대한 모든것을 볼 수 있으니 참고해주세요 XD
읽어보시면 알겠지만, 오늘 할 거는 Swift를 어떻게 사용하면 Swift처럼 잘 쓸 수 있는지!!!한마디로 Apple에서 제안하는하는거에요. 정말 한마디로 API Design Guidelines...
■ ●같은 기호들이 나올건데..
● : 최상위
○ : ●의 하위
■ : ○의 하위로 생각해주세요.
그리고 Design Guidelines이니만큼..조금 딱딱하게 번역을 하도록 할게요!
그리고!!! 소스코드가 나올건데, 배경색상에 따라 이게 잘 짠 코드인지, 아닌지..배경이 연두색이면 권장사항대로 잘 짠 코드고, 배경이 빨간색이면 그렇지 않은걸로 보시면 됩니다 :) 그냥 하얀색상 배경 소스코드는 예시라고 보시면 됩니다XD
API Design Guidelines
Fundamentals
● 사용 시점에서의 명확성(Clarity)이 가장 중요한 목표(goal)입니다.
● 명료성(Clarity)은 간결성(brevity)보다 중요합니다.
Swift코드는 압축(compact)될 수 있지만(고차함수에서의 생략?같은 걸 말하는듯..그 외에도 많이 있겠죠?), 가장 적은 수의 문자로 가장 적은 코드를 사용하는것이 목표는 아닙니다. (한마디로 생략하면 코드가 그만큼 "적어"지죠? == 그렇게 막 생략하는것이 좋은것은 아니다..?라는 걸 말하는것 같음)
Swift코드에서 발견되는 간결함은, 강력한 타입 시스템의 부작용(side-effect)이며 자연스럽게 boilerplate(보일러 플레이트)를 줄이는 기능입니다.
( ※ boilerplate(보일러 플레이트) : 최소한의 변경으로 재사용 할 수 있는 것 / 적은 수정만으로 여러 곳에 활용가능한 코드, 문구)
● 모든 선언에 대한 문서 주석(코멘트)을 작성하십시오.
주석 작성으로 얻을 수 있는 통찰력(Insights)은 설계에 큰 영향을 미칠 수 있으므로 이를 미루지 마십시오.
API기능을 간단하게 설명하는데 어려움이 있는 경우, 잘못된 API를 설계했을 수 있습니다.
○ Swift의 dialect of Markdown을 사용하십시오
○ 선언되는 엔티티를 설명하는 summary(요약)부터 시작합니다. API는 종종 선언문과 그 요약에서 완전히 이해 될 수 있습니다.
1 2 3 | /// Returns a "view" of `self` containing the same elements in /// reverse order. func reversed() -> ReverseCollection | cs |
■ 요약에 초점을 맞추십시오. ; 이것은 가장 중요한 부분입니다. 훌륭한 문서 주석은 훌륭한 요약으로 구성되어 있습니다.
■ 가능하다면 마침표로 끝나는 단일 문장 "조각"을 사용하십시오. 완전한 문장을 사용하지 마십시오.
■ 함수 또는 메소드가 수행하는 작업과 리턴하는 작업, null을 생략하는 효과 및 void리턴작업을 설명하십시오.
1 2 3 4 5 6 7 8 9 10 | // Inserts `newHead` at the beginning of `self`. mutating func prepend(_ newHead: Int) /// Returns a `List` containing `head` followed by the elements /// of `self`. func prepending(_ head: Element) -> List /// Removes and returns the first element of `self` if non-empty; /// returns `nil` otherwise. mutating func popFirst() -> Element? | cs |
Note : 드문 경우, 위 코드의 popFirst
같이 요약은 세미콜론으로 구분된 여러 문장으로 구성 됩니다.
■ 서브스크립트(subscript)가 접근(Accesses)하는것에 대해 설명하십시오.
1 2 | /// Accesses the `index`th element. subscript(index: Int) -> Element { get set } | cs |
■ 이니셜라이저가 생성(Creates)하는 작업을 설명하십시오.
1 2 | /// Creates an instance containing `n` repetitions of `x`. init(count n: Int, repeatedElement x: Element) | cs |
■ 다른 모든 선언문의 경우(서브스크립트와 이니셜라이저를 제외한 다른 모든 선언문) 선언된 엔티티가 무엇인지 설명하십시오.
1 2 3 4 5 6 7 8 | /// A collection that supports equally efficient insertion/removal /// at any position. struct List { /// The element at the beginning of `self`, or `nil` if self is /// empty. var first: Element? ... | cs |
(볼드체한 것처럼 정말 "설명"하고있죠?)
○ 선택적으로, 하나 이상의 단락 및 글머리 기호 항목으로 계속합니다. 단락은 빈 줄로 구분되고, 완전한 문장을 사용합니다.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 | /// Writes the textual representation of each ← Summary /// element of `items` to the standard output. /// ← Blank line(빈줄로 구분한다고 그랬죠?) /// The textual representation for each item `x` ← Additional discussion /// is generated by the expression `String(x)`. /// /// - Parameter separator: text to be printed ⎫ /// between items. ⎟ /// - Parameter terminator: text to be printed ⎬ Parameters section /// at the end. ⎟ /// ⎭ /// - Note: To print without a trailing ⎫ /// newline, pass `terminator: ""` ⎟ /// ⎬ Symbol commands /// - SeeAlso: `CustomDebugStringConvertible`, ⎟ /// `CustomStringConvertible`, `debugPrint`. ⎭ public func print( _ items: Any..., separator: String = " ", terminator: String = "\n") | cs |
■ 적절할 때, 인식된 symbol documentation markup 요소를 사용하여 요약 외의 정보를 추가하십시오.
■ symbol command syntax으로 인식된 글 머리 기호 항목을 알고 사용하십시오.
Xcode와 같은 인기있는 개발도구는 다음 키워드로 시작하는 글 머리 기호항목에 특별한 대우를 합니다
Naming
Promote Clear Usage(명확한 사용법 홍보?..)
● 이름이 사용되는 코드를 읽을 때, 모호함을 방지하는 데 필요한 모든 단어를 포함한다.
예를들어, Collection내의 지정 된 위치에서 요소를 제거하는 메소드를 생각해보십시오.
extension List {
public mutating func remove(at position: Index) -> Element
}
employees.remove(at: x)
employees.remove(x) // unclear: are we removing x?
● 불필요한 단어는 생략하십시오. 이름에 나오는 모든 단어는 사용 현장에서 중요한 정보를 전달해야합니다.
의도를 명확하게 하거나, 의미를 명확하게 하기 위해, 더 많은 단어가 필요할 수 있지만, 독자가 이미 소유하고 있는 정보로 중복되는 단어는 생략해야합니다. 특히 타입 정보만 반복하는 단어는 생략하십시오.
public mutating func removeElement(_ member: Element) -> Element?
allViews.removeElement(cancelButton)
public mutating func remove(_ member: Element) -> Element?
allViews.remove(cancelButton) // clearer
이 코드가 나을 것 입니다.
흔히 모호성(
ambiguity)을 피하기 위해 반복되는 타입 정보가 필요하지만, 일반적으로 매개 변수의 타입보다는 매개변수의 역할을 설명하는 단어를 사용하는 것이 좋습니다. ● 타입 제약 조건 보다는 역할에 따라, 변수, 매개 변수 및 관련타입(associated types)을 지정하십시오.
var string = "Hello"
protocol ViewController {
associatedtype ViewType : View
}
class ProductionLine {
func restock(from widgetFactory: WidgetFactory)
}
이런식으로 타입이름을 다른 목적에 맞게 만드는 것은 명확함(clarity)과 표현력(expressivity)을 최적화하지 못합니다. (변수 이름이 string이면........안되겠죠..)
대신, 엔티티의 역할을 나타내는 이름을 선택하려고 노력하십시오.
var greeting = "Hello"
protocol ViewController {
associatedtype ContentView : View
}
class ProductionLine {
func restock(from supplier: WidgetFactory)
}
associated type이 프로토콜 제약 조건에 너무 밀접하게 결합되어 프로토콜 이름이 곧 역할인 경우, associated type 이름에 "Type"을 추가하여 충돌을 방지하십시오.
1 2 3 | protocol Sequence { associatedtype IteratorType : Iterator } | cs |
● 취약한 타입 정보를 보완하여, 파라미터 역할을 명확히 합니다.
특히, 매개변수 타입이 특히 매개 변수 유형이 NSObject, Any, AnyObject 또는 Int 또는 String과 같은 기본 타입 인 경우, 사용시점에서 타입 정보와 컨텍스트가 의도를 완전히 전달하지 못 할 수도 있습니다. 이 예에서는 선언이 명확하지만 사용 현장이 모호합니다.
func add(_ observer: NSObject, for keyPath: String)
grid.add(self, for: graphics) // vague(모호하다)
명료함을 복원하려면, 각 약식 매개변수( weakly typed parameter) 앞에 그 역할을 설명하는 명사를 붙힙니다.
func addObserver(_ observer: NSObject, forKeyPath path: String)
grid.addObserver(self, forKeyPath: graphics) // clear
Strive for Fluent Usage(능수능란한 사용을 위한 노력)
x.insert(y, at: z) “x, insert y at z”
x.subViews(havingColor: y) “x's subviews having color y”
x.capitalizingNouns() “x, capitalizing nouns”
x.insert(y, position: z)
x.subViews(color: y)
x.nounCapitalize()
(번역을 확실하게 못하겠어서...원문을 올립니다.
It is acceptable for fluency to degrade after the first argument or two when those arguments are not central to the call’s meaning:)
(그러니까 호출 지점에서 핵심이 아닐 때, 첫번째 또는 두번째 전달인자 후에 유창성이 저하(degrade)될 수 있다는 말같아요!!)
1 2 3 | AudioUnit.instantiate( with: description, options: [.inProcess], completionHandler: stopProgressBar) | cs |
● "make"를 사용하여 팩토리 메소드의 이름을 시작하십시오. e.g. x.makeIterator()
● 이니셜라이저 및 팩토리 메소드의 호출의 첫번째 파라미터는 기본 이름으로 시작하는 구문을 형성해서는 안됩니다. e.g. x.makeWidget(cogCount: 47)
예를들어, 아래 코드에 대한 첫번째 파라미터는 기본 이름과 동일한 구문의 일부로 읽지 않습니다.
let foreground = Color(red: 32, green: 64, blue: 128)
let newPart = factory.makeWidget(gears: 42, spindles: 14)
let ref = Link(target: destination)
let foreground = Color(havingRGBValuesRed: 32, green: 64, andBlue: 128)
let newPart = factory.makeWidget(havingGearCount: 42, andSpindleCount: 14)
let ref = Link(to: destination)
실제로, 이 지침은 파라미터 레이블에 대한 지침과 함께, 호출이 값 보존 형변환을 수행하지 않는 한 첫번째 파라미터에 레이블이 있음을 의미합니다.
1 | let rgbForeground = RGBColor(cmykForeground) | cs |
● 부작용에 따라 함수 및 메소드 이름 지정
○ 부작용이 없는 것들은 명사구로 읽어야 합니다. e.g. x.distance(to: y)
, i.successor()
.
○ 부작용이 있는 것들은 필수 동사구로 읽어야 합니다. e.g., print(x)
, x.sort()
, x.append(y)
.
○ Mutating/nonmutating메소드 쌍 이름을 일관되게 지정합니다. mutating메소드는 종종 유사한 의미를 같는 nonmutating메소드를 가지지만, 인스턴스를 현재 위치에서 업데이트 하는 대신 새로운 값을 반환합니다.
■ 연산이 동사에 의해 자연스럽게 설명되는 경우에는 mutating메소드에 동사의 명령을 사용하고, "ed"또는 "ing"접미사를 적용하여 해당 nommutating이름을 지정하십시오.
Mutating | Nonmutating |
---|---|
x.sort() | z = x.sorted() |
x.append(y) | z = x.appending(y) |
■ 동사의 과거분사(대개 "ed"추가)를 사용하여 nommutating이름을 지정하는 것이 좋습니다.
1 2 3 4 5 6 7 8 | /// Reverses `self` in-place. mutating func reverse() /// Returns a reversed copy of `self`. func reversed() -> Self ... x.reverse() let y = x.reversed() | cs |
■ 동사에 직접적인 목적어가 있기 때문에, "ed"가 문법적이지 않을 경우, "ing"를 첨부하여 nonmutating이름을 지정하십시오.
1 2 3 4 5 6 7 8 | /// Strips all the newlines from `self` mutating func stripNewlines() /// Returns a copy of `self` with all the newlines stripped. func strippingNewlines() -> String ... s.stripNewlines() let oneLine = t.strippingNewlines() | cs |
Nonmutating | Mutating |
---|---|
x = y.union(z) | y.formUnion(z) |
j = c.successor(i) | c.formSuccessor(&i) |
● Bool메소드 및 프로퍼티의 사용은 수신자에 대한 주장(assertions)으로 해석해야합니다. e.g. x.isEmpty
, line1.intersects(line2)
.
● "무엇"인지를 설명하는 프로토콜은 명사로 읽어야 합니다. (e.g. Collection
).
● 기능을 설명하는 프로토콜은 "able", "
ible", "
ing"접미사를 사용하여 명명해야 합니다.
● 다른 타입, 프로퍼티, 변수 및 상수의 이름은 명사로 읽어야 합니다.
Use Terminology Well
Term of Art : 명사(noun) - 특정 분야 또는 직업 내에서 정확하고 특수한 의미를 지닌 단어 또는 구문.
● 보다 일반적인 단어가 의미를 전달하는 경우, 모호한 용어는 사용하지 마십시오.
"skin(피부)"이 당신의 목적에 부합한다면, "epidermis(표피)"라고 말하지 마십시오. 용어는 필수적인 의사소통 도구이지만, 그렇지 않을 경우 손실될 수 있는 중요한 의미를 포착하는데에만 사용해야 합니다.
● Term of Art(위에서 설명했죠?)를 사용한다면, 확립된 의미에 충실하십시오. (정해진 의미를 고수하십시오.)
보다 일반적인 단어보다, 전문 용어를 사용하는 이유는 그렇지 않으면 모호하거나 불명확 할 수 있는 것을 정확하게 표현한다는 것입니다. 따라서, API는 수용된 의미에 따라 엄격하게 용어를 사용해야 합니다.
○ 전문가를 놀라게 하지 마십시오 : 이미 그 용어에 익숙한 사람은 우리가 새로운 의미를 발명한 것처럼 보이면 놀라게 될 것이고, 분노를 느낄 것입니다.
○ 초보자를 혼동하게 하지 마십시오 : 용어를 배우려는 사람은 누구나 웹검색을 하여 그 전통적인 의미를 발견하기 쉽습니다.
● 약어를 피하십시오. 약자, 특히 비표준 약자는 효과적으로 Term of Art입니다. 왜냐하면 정확하게 그 약어들을 축약하지 않은 형태로 바꾸는 것에 따라 이해가 달라지기 때문입니다. (의역을 조금 했는데, 핵심은 그 축약된 표현을 축약되지 않은 표현으로 "정확하게" 바꿔야만 이해를 할 수 있잖아요? 그러니까 이 축약된 표현을 정확하게 축약되지 않은 표현으로 잘 바꾸냐 못바꾸냐에 따라 이해가 달라질 수 있다는 것 입니다.)
● 선례를 받아들이십시오. 기존 문화를 따른다는 대가를 치르더라도, 초보자를 위한 용어를 최적화하지 마십시오.
뭔소린지 모르겠지만, 설명이 나옵니다.
List
,와 같은 단순화된 용어를 사용하는 것 보다, 연속적인 데이터 구조 Array
의 이름을 지정하는 것이 좋습니다. 초보자가 List
의 의미를 더 쉽게 이해 할 수도 있습니다. 배열은 현대 컴퓨팅의 기본 요소입니다. 따라서 모든 프로그래머느 배열이 무엇인지 곧 알게 될 것입니다. 대부분의 프로그래머가 익숙한 용어를 사용하면, 웹 검색 및 질문에 대한 보상이 제공됩니다.
수학과 같은 특정 프로그래밍 도메인 내에서 sin(x)
와 같이 널리 선행된 용어는 asverticalPositionOnUnitCircleAtOriginOfEndOfRadiusWithAngle(x)
.와 같은 설명문구보다 바람직합니다.
이 경우, 선례가 약어를 피하기 위한 지침을 능가한다는 것에 주목하십시오. 완전한 단어가 sine
,일지라도
sin(x)
는 수십년 동안 프로그래머와 수세기 동안 수학자 사이에서 공통적으로 사용되어 왔습니다.
Conventions
General Conventions
self
가 없을 때1 | min(x, y, z) | cs |
1 | print(x) | cs |
1 | sin(x) | cs |
1 2 3 | var utf8Bytes: [UTF8.CodeUnit] var isRepresentableAsASCII = true var userSMTPServer: SecureSMTPServer | cs |
1 2 | var radarDetector: RadarScanner var enjoysScubaDiving = true | cs |
● 메소드는 동일한 기본 의미를 공유하거나, 고유한 도메인에서 작동 할 때, 이름을 공유 할 수 있습니다.
예를들어, 메소드가 본질적으로 동일한 것을 하기 때문에 다음을 권장합니다.
extension Shape {
/// Returns `true` iff `other` is within the area of `self`.
func contains(_ other: Point) -> Bool { ... }
/// Returns `true` iff `other` is entirely within the area of `self`.
func contains(_ other: Shape) -> Bool { ... }
/// Returns `true` iff `other` is within the area of `self`.
func contains(_ other: LineSegment) -> Bool { ... }
}
extension Collection where Element : Equatable {
/// Returns `true` iff `self` contains an element equal to
/// `sought`.
func contains(_ sought: Element) -> Bool { ... }
}
index
메소드는 다른 의미를 가지며, 다르게 이름을 지정해야합니다.extension Database {
/// Rebuilds the database's search index
func index() { ... }
/// Returns the `n`th row in the given table.
func index(_ n: Int, inTable: TableID) -> TableRow { ... }
}
마지막으로, 타입 유추가 있을 때, 모호함을 초래하기 때문에, "리턴타입에 대한 오버로드"를 피하십시오.
extension Box {
/// Returns the `Int` stored in `self`, if any, and
/// `nil` otherwise.
func value() -> Int? { ... }
/// Returns the `String` stored in `self`, if any, and
/// `nil` otherwise.
func value() -> String? { ... }
}
Parameters
1 | func move(from start: Point, to end: Point) | cs |
● 문서를 제공 할 때, 파라미터 이름을 선택하십시오. 파라미터 이름은, 함수 또는 메소드의 사용 시점에 나타나지 않지만 중요한 설명 역할을 합니다.
문서를 읽기 쉽게 만들려면, 이름을 선택하십시오. 예를들어, 이름을 사용하면 문서를 자연스럽게 읽을 수 있습니다.
/// Return an `Array` containing the elements of `self`
/// that satisfy `predicate`.
func filter(_ predicate: (Element) -> Bool) -> [Generator.Element]
/// Replace the given `subRange` of elements with `newElements`.
mutating func replaceRange(_ subRange: Range, with newElements: [E])
그러나 다음과 같은 것들은 문서를 어색하게 만듭니다.
/// Return an `Array` containing the elements of `self`
/// that satisfy `includedInResult`.
func filter(_ includedInResult: (Element) -> Bool) -> [Generator.Element]
/// Replace the range of elements indicated by `r` with
/// the contents of `with`.
mutating func replaceRange(_ r: Range, with: [E])
● 일반적인 사용을 단순화 할때, 기본 매개변수를 활용하십시오. 공통적으로 사용되는 값이 하나인 매개변수는 기본값의 후보입니다.
기본 전달인자(Argument)는 관련없는 정보를 숨겨 가독성을 향상시킵니다. 예를들어,
let order = lastName.compare(
royalFamilyName, options: [], range: nil, locale: nil)
let order = lastName.compare(royalFamilyName)
기본 전달인자는 일반적으로 메소드 패밀리(method families)의 사용보다 선호됩니다. 왜냐하면 API를 이해하려고 하는 모든 사람에게 더 낮은 인지 부담(lower cognitive burden )을 주기 때문입니다.
extension String {
/// ...description...
public func compare(
_ other: String, options: CompareOptions = [],
range: Range? = nil, locale: Locale? = nil
) -> Ordering
}
위의 내용은 간단하지는 않지만, 다음 코드보다 훨씬 간단합니다.
extension String {
/// ...description 1...
public func compare(_ other: String) -> Ordering
/// ...description 2...
public func compare(_ other: String, options: CompareOptions) -> Ordering
/// ...description 3...
public func compare(
_ other: String, options: CompareOptions, range: Range) -> Ordering
/// ...description 4...
public func compare(
_ other: String, options: StringCompareOptions,
range: Range, locale: Locale) -> Ordering
}
메소드 패밀리의 모든 구성원은 사용자가 별도로 문서화하고 이해해야합니다. 그 중 하나를 결정하기 위해서는 사용자가 모든것을 이해 할 필요가 있습니다. 예를들어, foo(bar: nil)
과 foo()
가 항상 동의어가 아닌 경우가 종종 있습니다. 대부분이 사소한 차이점을 제거하는 지루한 과정입니다. 기본값이 있는 단일 메소드를 사용하면 훨씬 뛰어난 프로그래머 경험을 얻을 수 있습니다.
● 매개변수(Parameter)목록의 끝 부분에, 기본값이 있는 매개변수를 찾으십시오. 기본값이 없는 매개변수는 일반적으로 메소드의 의미에 더 중요하며, 메소드를 호출 할 때, 안정적인 초기 사용 패턴을 제공합니다.
Argument Labels
1 2 | func move(from start: Point, to end: Point) x.move(from: x, to: y) | cs |
● 전달인자를 유용하게 구분 할 수 없는 경우, 모든 레이블을 생략하십시오. e.g.min(number1, number2)
, zip(sequence1, sequence2)
● 값 보존 형 변환을 수행하는 이니셜라이저에서 첫번째 인수 레이블을 생략하십시오. e.g. . Int64(someUInt32)
첫번째 전달인자는 항상 변환의 소스(근원?)이어야 합니다.(다음 코드를 보시면 이해가 갑니다.)
1 2 3 4 5 6 7 8 9 10 | extension String { // Convert `x` into its textual representation in the given radix init(_ x: BigInt, radix: Int = 10) ← Note the initial underscore } text = "The value is: " text += String(veryLargeNumber) text += " and in hexadecimal, it's" text += String(veryLargeNumber, radix: 16) | cs |
(String으로 타입변환을 해줄 때, 항상 첫번째 인자가 String으로 변환할 어떠한 값? 위에서 말하는 Source가 됩니다.)
그러나 "narrowing"타입 변환에서는 narrowing을 나타내는 레이블을 사용하는것이 좋습니다.
1 2 3 4 5 6 7 8 9 10 | extension UInt32 { /// Creates an instance having the specified `value`. init(_ value: Int16) ← Widening, so no label /// Creates an instance having the lowest 32 bits of `source`. init(truncating source: UInt64) /// Creates an instance having the nearest representable /// approximation of `valueToApproximate`. init(saturating valueToApproximate: UInt64) } | cs |
(UInt64를 UInt32같이 이런 변환을 narrowing타입변환이라고 하는 것 같아요. 이럴때는 첫번째 전달인자가 Source임에도 불구하고 레이블을 지정해줬죠.)
참고 : 값 보존형 변환은 단일 형태(monomorphism)의 것으로, 즉 소스 값의 모든 차이에 따라 결과 값이 달라집니다. 예를들어, Int8에서 Int64로의 변환은 모든 고유 Int8값이 고유한 Int64값으로 변환되기 때문에 값보존입니다. 그러나 반대방향으로의 변환은 값을 유지할 수 없습니다. Int64는 Int8에서 표현할 수 있는 것보다 더 많은 값을 가질 수 있습니다.
참고 : 원래 값을 가져오는(retrieve) 기능은 변환이 값 보존인지 여부에 영향을 주지 않습니다.
● 첫번째 인수가 전치사 구의 일부인 경우, 전달인자 레이블을 지정하십시오. 전달인자 레이블은 일반적으로 전치사에서 시작해야 합니다. e.g. x.removeBoxes(havingLength: 12)
처음 두 전달인자가 단일 추상화의 일부를 나타내는 경우, 예외가 발생합니다. (뭐라는거...야...)
a.move(toX: b, y: c)
a.fade(fromRed: b, green: c, blue: d)
이러한 경우, 추상화를 명확히 하기 위해, 전치사 다음에 전달인자 레이블을 붙히십시오.
(위 코드 같은 경우에는, x는 "첫번째"전달인수기 때문에 전치사로 시작했고 다음 전달인수부터는 전치사가 붙지않죠. 이럴때는 다음과 같이 코드를 작성합니다.)
a.moveTo(x: b, y: c)
a.fadeFrom(red: b, green: c, blue: d)
● 그렇지 않고, 첫번째 전달인자가 문법적 구문의 일부를 구성하는경우 해당 레이블을 생략하고, 앞의 단어를 기본이름에 추가합니다.
e.g. x.addSubview(y)
(원래 x.add(Subview : y)라면, add와 Subview가 문법적 구문으로 말이 되니까? 이 Subview라는 레이블을 삭제하고 그냥 기본이름에 추가시키는 거죠)
이 지침은 첫번째 전달인자가 문법적 구문의 일부를 구성하지 않으면 레이블이 있어야만 함을 의미합니다.
view.dismiss(animated: false)
let text = words.split(maxSplits: 12)
let studentsByName = students.sorted(isOrderedBefore: Student.namePrecedes)
(dissmiss와 animated=>문법적 구문 x...이런식으로 판단하는 것 같습니다.)
어구가 올바른 의미를 전달하는 것이 중요합니다.
다음은 문법적이지만, 잘못된 것을 표현합니다.
view.dismiss(false) Don't dismiss? Dismiss a Bool?
words.split(12) Split the number 12?
dissmiss를 안한다는 건가?
12번을 나눈다는건가???
기본값을 가진 전달인자는 생략 할 수 있으며, 이 경우 문법적 구문의 일부가 아니므로 항상 레이블을 가져야합니다.
● 다른 모든 전달인자에 레이블을 붙히십시오.
Special Instructions
● tuple멤버에게 레이블을 지정하고, API에 표시되는 클로져 매개변수 이름을 지정합니다.
이 이름들은 설명력을 가지며, 문서 주석으로 부터 참조될 수 있고, tuple멤버들에 대한 표현적인 접근을 제공합니다.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 | /// Ensure that we hold uniquely-referenced storage for at least /// `requestedCapacity` elements. /// /// If more storage is needed, `allocate` is called with /// `byteCount` equal to the number of maximally-aligned /// bytes to allocate. /// /// - Returns: /// - reallocated: `true` iff a new block of memory /// was allocated. /// - capacityChanged: `true` iff `capacity` was updated. mutating func ensureUniqueStorage( minimumCapacity requestedCapacity: Int, allocate: (_ byteCount: Int) -> UnsafePointer<Void> ) -> (reallocated: Bool, capacityChanged: Bool) | cs |
클로저 매개변수에 사용되는 이름은 최상위 수준 함수의 매개변수 이름과 같이 선택해야합니다.
호출 지점에서 나타나는 클로저 전달인자에 대한 레이블은 지원되지 않습니다.
● overload Set에서 모호함을 방지하기 위해, 제약없는 다형성(polymorphism)에 각별히 주의하십시오.
(예 : Any
,AnyObject
, 그리고 제약없는 generic 매개변수(unconstrained generic parameters))
예를들어 다음 overload Set을 고려하십시오.
struct Array {
/// Inserts `newElement` at `self.endIndex`.
public mutating func append(_ newElement: Element)
/// Inserts the contents of `newElements`, in order, at
/// `self.endIndex`.
public mutating func append(_ newElements: S)
where S.Generator.Element == Element
}
이러한 메소드는 semantic 패밀리를 형성하며, 전달인자 타입은 처음에는 크게 구분됩니다. 그러나 Element
가 Any
,인경우 단일 요소는 일련의 요소와 동일한 타입을 가질 수 있습니다. var values: [Any] = [1, "a"]
values.append([2, 3, 4]) // [1, "a", [2, 3, 4]] or [1, "a", 2, 3, 4]?
(위 코드에서 배열을 집어넣었으면, [1,"a"]라는 배열에 또 배열인 [2,3,4]를 추가한건지 아니면 그냥 2,3,4를 추가한건지 모호하다는거죠.
모호성을 제거하려면 두번째
overload를 보다 명확하게 명명하십시오. struct Array {
/// Inserts `newElement` at `self.endIndex`.
public mutating func append(_ newElement: Element)
/// Inserts the contents of `newElements`, in order, at
/// `self.endIndex`.
public mutating func append(contentsOf newElements: S)
where S.Generator.Element == Element
}
진짜....진짜로 읽기 싫게? 번역을 한 것 같네요ㅠㅠㅠㅠ
위 글을 모두!!!!모두 요약하자면
1. 코드를 짤 때, 무조건!!! 무조건 "명확"하게 짜는 것이 중요. 이 명확함이 간결성(코드 축약같은거)보다 더 중요함ㅇㅇ
2. 그리고 꼭 주석을 달아라!!!!!!!!!!!!!주석은 여기를 참고해서 달도록 해 ㅇㅇ
3. 이름은 일단 코드를 읽었을 때, 모호함을 방지하는데 필요한 모든 단어를 포함하도록 이름을 짓도록해, 근데!!!근데 불필요한 단어는 생략해야해 (항상 코드는 명료해야한다는 걸 명심해)
이 naming부분이 상당히 긴데(Parameter, argument label...)이 부분은 읽어보시는 걸..추천......
제가 어떤 말을 들었는데,
"코드를 읽을 때, 코드를 읽는 것 처럼 느끼도록 하면 안된다. 문서를 읽는 것 처럼 읽혀야 한다"
이게 뭐 사람에 따라? 좀 말도 안되는 말이라고 생각하실 수도 있는데 저는 개인적으로 굉장히 공감해요 :)
이런 Convention들을 잘 익혀놓으면 정말 코드를 "문서화"하는데 도움이 될 것 같아요. 코드에 주석을 다는거랑은 다르게요!!!!
아무튼 도움이 되었으면...좋겠습니다...ㅎㅎ..
'Swift' 카테고리의 다른 글
Swift ) ComparisonResult살펴보기 (0) | 2018.03.24 |
---|---|
Swift4.1 ) flatMap -> compactMap (2) | 2018.03.11 |
Swift ) DateDecoding(Encoding)Strategy (1) | 2018.01.23 |
Swift ) 왕초보를 위한 Codable - CodingKey (5) | 2018.01.23 |
Swift ) NSCountedSet (2) | 2018.01.22 |
- 피아노
- Combine
- swift tutorial
- iOS delegate
- swift 공부
- swift delegate
- WidgetKit
- 회고
- np-hard
- WWDC
- WKWebView
- 제이슨 파싱
- UIBezierPath
- IOS
- Xcode
- FLUTTER
- Accessibility
- np-complete
- swift3
- fastlane
- 스위프트 문법
- ios 13
- SwiftUI
- github
- Swift
- actor
- swift array
- 스위프트
- swift sort
- Git
- Total
- Today
- Yesterday