Swift 문법 심화 - Sizuha/devdog GitHub Wiki

주석

문서화된 주석

/** 혹은 /// 으로 시작되는 주석에서, Markdown 문법을 사용할 수 있다.

파라매터, 리턴값 등의 설명을 달기 위해 몇 가지 예약어가 존재한다.

/// 함수 설명
/// 
/// - Parameters:
///   - arg1: 첫번째 인수에 대한 설명 
///   - arg2: 두번째 인수에 대한 설명
/// - Returns: 리턴값에 대한 설명
func some(arg1: Int, arg2: Int) { .. }

/** 혹은 **/

/// 함수 설명
/// 
/// - Parameter arg1: 첫번째 인수에 대한 설명 
/// - Parameter arg2: 두번째 인수에 대한 설명 
/// - Returns: 리턴값에 대한 설명
func some(arg1: Int, arg2: Int) { .. }

표식

// MARK: *****
// TODO: *****
// FIXME: *****

区切り線表示

// MARK: - *****
// TODO: ***** -
// FIXME: ***** -

let

Xcode에서 return값이 있는 함수를 호출 할 때, return값을 받는 처리가 없으면 경고 마크가 뜬다. 굳이 return값을 받을 필요가 없는 경우, 다음과 같이 처리.

func some() -> Bool { return true }
let _ = some()

let 키워드 조차 생략할 수 있다

_ = some()

컬렉션 타입을 let으로 선언하게 되면 Java의 final 처럼 참조만 상수성을 가지는게 아니라, 컬렉션의 변경 자체가 불가능하게 된다.

let strA: String = "TEXT" // 이후 strA에는 어떤 값도 할당이 안된다
var strB: String = "TEXT" // Mutable String 객체가 된다.

let arrA = [Int]() // 배열 내용을 변경하는게 불가능해 진다.
var arrB = [Int]() // Mutable Array가 된다.

if let 문법으로 대상이 nil이 아닌 경우를 처리할 수 있지만, nil이 아닌것을 전제조건으로 하고자 할 때 guard let 문법을 쓸 수 있다.

// xxx가 반드시 nil이 아니어야 하고, 이후 a로 정의된다.
guard let a = xxx else { return }

enum

Enum iterations

enum TextType: CaseIterable {
    case title
    case subtitle
    case sectionTitle
    case body
    case comment
}

var fonts = [TextType : UIFont]()

for type in TextType.allCases {
    switch type {
    case .title:
        fonts[type] = .preferredFont(forTextStyle: .headline)
    case .subtitle:
        fonts[type] = .preferredFont(forTextStyle: .subheadline)
    case .sectionTitle:
        fonts[type] = .preferredFont(forTextStyle: .title2)
    case .body:
        fonts[type] = .preferredFont(forTextStyle: .body)
    case .comment:
        fonts[type] = .preferredFont(forTextStyle: .footnote)
    }
}

Collection

Dictionary

// Dictionary 타입 정의
// [KeyType : ValueType ]

var dic: [String : Any?] = [:]
// 혹은
var dic = [String : Any?]()

// [:]는 비어있는 컬렉션을 뜻함.
// var로 선언되어 있는 컬렉션만 요소를 추가/삭제 가능.

주의!! nil을 할당하면 key가 삭제된다

dic["someKey"] = nil

이렇게 키에 nil을 할당하면, 「nil」이 값으로 들어가는 것이 아니라, 해당 키가 삭제된다.

문자열 다루기

let과 var의 차이

var로 선언된 문자열 변수는 Java의 StringBuilder, StringBuffer 같은 역할을 한다.

var buffer = ""
buffer.reserveCapacity(100)
buffer.append("some text")

let으로 선언된 문자열 변수는, 문자열 내용이 변화할 때 새로운 문자열을 생성한다.

let text ="abc"
let text2 = text.appending("def") // append()를 사용할 수 없다. (+ 연산자와 같은 기능)

문자열 비교

xxx으로 시작하는지 확인

if url.hasPrefix("http://") { ... }

xxx으로 끝나는지 확인

if filename.hasSuffix(".txt") { ... }

xxx가 포함되어 읽는지 확인

if filename.contains(".txt") { ... }

문자열 치환

func replace(text: String, from: String, to: String) -> String {
    text.replacingOccurrences(of: from, with: to)
}

좌우 공백제거

"   string    ".trimmingCharacters(in: .whitespacesAndNewlines)

문자열 분리

let parts: [String] = "xxx-xxx-xxxx".components(separatedBy: "-")

형변환

Data / UnsafeRawPointer

Data to UnsafeRawPointer

let result = dataObj.withUnsafeBytes { ptr in
    // ...
}

UnsafeRawPointer to Data

let result = dataObj.withUnsafeBytes { ptr in
    Data(bytes: ptr, count: 3)
}

UnsafeRawPointer to [UInt8]

let data: UnsafeRawPointer
// ...
let converted: [UInt8] = data.load(as: [UInt8].self)

String

C형식 문자열을 Swift 문자열로. (단, UTF-8 문자열인 경우)

let swiftStr = String(cString: sqlite3_column_name(stmt, col))

Data to String

let text1 = String(data: dataObj, encoding: .utf8)
let text2 = String(bytes: dataObj, encoding: .utf8)

String to Data

if let data = string.data(using: .utf8) {
    // ...
}

to String

let text = "\(대상변수)"

String to Int

// 10진수로 변환
if let result = Int("100") {
    // ...
}

// 2진수로 변환
if let result = Int("100", radix: 2) {
    // ...
}

String to Double, Float ...

if let result = Double("123.0") { 
    // ...
}

if let result = Float("123.0") {
    // ...
}

Assertion

Swift Optimisation Levels

  • Onone (default for debug builds) : 디버그 빌드
  • O (default for release builds) : 릴리즈 빌드
  • Ounchecked : 권장안함

Assert

assert()

assert(조건식, "메세지")

디버그 빌드에서만 동작. 조건식이 실패하면 에러를 발생시키고 '메세지'를 표시한다.

assertionFailure()

디버그 빌드에서만 동작하고, assertion 실패 메세지를 표시한다.

func printAge(_ age: Int) {
    guard age >= 0 else {
        assertionFailure("Age can't be a negative value")
        return
    }
    print("Age is: ", age)
}

precondition(), preconditionFailure()

assert(), assertionFailure()와 같은 동작이지만, 디버그와 릴리즈 빌드에서 모두 동작한다. 단, Ounchecked 레벨에서는 동작하지 않는다.

Fatal Error

fatalError()는 assertionFailure(), preconditionFailure()와 비슷하게 동작하지만, 모든 빌드에 적용된다.

func printAge(_ age: Int) {
    guard age >= 0 else {
        fatalError("Age can't be a negative value")
    }
    print("Age is: ", age)
}

연산자 오버로딩

2項演算子

/* 分数の足し算 */
func + (left: Fraction, right: Fraction) -> Fraction {
    let numerator = left.numerator * right.denominator + right.numerator * left.denominator
    let denomitor = left.denominator * right.denominator
    return Fraction(numerator: numerator, denominator: denomitor)
}

let f1 = Fraction(numerator: 2, denominator: 3)
let f2 = Fraction(numerator: 4, denominator: 5)
let f3 = f1 + f2
print(f3.asString)    // -> 22 / 15

単項演算子

prefix(前に置く), infix(間に置く), postfix(後に置く)

/* 符号の反転 */
prefix func - (fraction: Fraction) -> Fraction {
    return Fraction(numerator: -fraction.numerator, denominator: fraction.denominator)
}

let f1 = Fraction(numerator: 4, denominator: 10)
print(f1.asString)    // -> 2 / 5
let f2 = -f1
print(f2.asString)    // -> - 2 / 5
let f3 = -f2
print(f3.asString)    // -> 2 / 5

複合代入演算子

/* 分数の加算 */
func += (inout left: Fraction, right: Fraction) {
    left = left + right
}

var f1 = Fraction(numerator: 2, denominator: 5)
let f2 = Fraction(numerator: 3, denominator: 4)
f1 += f2
println(f1.asString)    // -> 23 / 20

等価演算子

/* 等価判定 */
func == (left: Fraction, right: Fraction) -> Bool {
    return left.asString == right.asString
}
func != (left: Fraction, right: Fraction) -> Bool {
    return !(left == right)
}

let f1 = Fraction(numerator: 2, denominator: 3)
let f2 = Fraction(numerator: 3, denominator: 5)
let f3 = Fraction(numerator: 4, denominator: 6)

println(f1 == f2)   // false
println(f1 == f3)   // true
println(f1 != f2)   // true

独自の演算子

예를들어 ?= 라는 연산자를 새로 정의하고 싶을 때, 사용할 연산자를 먼저 선언해 둔다.

prefix operator ?=

다음에 연산자의 동작을 정의해 준다.

public extension String {
    static prefix func ?= (pattern: String) -> NSRegularExpression? {
        return try? NSRegularExpression(pattern: pattern, options: [])
    }
    func isMatch(_ regex: NSRegularExpression?) -> Bool {
        return regex?.numberOfMatches(in: self, options: [], range: getNSRange()) ?? 0 > 0
    }
}

println("abcd1234".isMatch(?="^[0-9a-zA-Z]+$")) // true

IO

TextFile 읽기 (한번에)

do {
    let content = try String(contentsOf: url, encoding: .utf8)
}
catch { ... }