검색결과 리스트
로그의 노트에 해당되는 글 212건
- 2015.08.14 [swift2] Xcode7 beta5 Swift 언어 변경사항
- 2015.08.06 [swift1.2] Swift 프로토콜 지향 프로그래밍
- 2015.08.01 [swift] 문자열과 문자 -1 (Strings and Characters)
- 2015.07.30 [swift2] Xcode7 beta4 Swift 언어 변경사항
- 2015.07.16 [swift] 성능향상 시키기 - 다이나믹 디스패치를 최소화
- 2015.07.11 [swift2] Xcode7 beta3 Swift 언어 변경사항
- 2015.07.10 [swift2] Swift에서 에러 처리 (Error Handling)
- 2015.07.01 [swift2] 타입의 텍스트적인 표현 커스터마이징하기
- 2015.06.24 [swift2] Xcode7 beta2 Swift 언어 변경사항
- 2015.06.23 [swift] 문자열 Indexing과 Slicing
글
[swift2] Xcode7 beta5 Swift 언어 변경사항
Linker
- bitcode를 활성화하면 이제 weak frameworks와 weak libraries가 지원됩니다. linker는 더이상 -weak_framework, -weak-l 또는 -weak_librarys가 -bitcode_bundle 과 함께 사용될수 없다고 경고하지 않습니다.
Swift Language Changes
1. 구조체, 클래스도 이제 ErrorType 프로토콜을 구현할수 있습니다.
2. 실패가능한 initializer에 위임하거나 체이닝할 때 !를 사용하여 force-unwrap 할 수 있습니다.
- self.init(...) 또는 super.init(...)가 실패가능한 경우 아래코드와 같이 force-unwrap 할 수 있습니다.
extension UIImage {
enum AssetIdentifier: String {
case Isabella
case William
case Olivia
}
convenience init(assetIdentifier: AssetIdentifier) {
self.init(named: assetIdentifier.rawValue)!
}
}
- 위와같이 실패가능한 initializer에 체이닝한 non-optional initializer 정의가 가능해집니다.
3. 성능향샹을 위한 -Onone 빌드 옵션
- -Onone 옵션 (debug) 빌드를 사용하면 표준라이브러리 제네릭 타입에 특화된 인스턴스를 사용하여 빌드성능을 향상시킬 수 있습니다.
- 많은 경우에 컴파일 타임에 영향없이 debug 빌드에서 상당히 빠른 실행파일을 생성합니다.
4. Objective-C 클래스의 제네릭 서브클래스와 이것을 상속하는 non-제네릭 클래스들은 runtime metadata 인스턴스화가 요구됩니다.
- Objective-C 코드에서 직접적으로 네이밍을 지정할수 없습니다.
- Beta4에 Objective-C 클래스들의 제네릭 서브클래스 정의가 가능해졌지만, 생성된 Objective-C 브리징 헤더에는 그러한 클래스들을 에러항목으로 리스팅합니다. 이로인해 잘못된 런타임 행동과 컴파일 타임에러를 발생시키는 오류를 수정하였습니다.
- 클래스에 대한 @objc 속성의 행동이 좀더 명확해졌습니다.
- 브리징헤더에 나타날 수 없는 클래스에 @objc를 적용하면 이제 에러입니다.
- 클래스의 어떤 수퍼클래스라도 @objc 클래스라면, 그 클래스의 멤버들은 암묵적으로@objc 이며, 모든 @objc 클래스들은 NSObject 를 상속해야함. 따라서 유효한 코드에 대한 어떤 변경을 발생시키지는 않습니다.
5. MacTypes.h 에 정의된 Boolean은 Swift와 Objective-C 타입 브리징 상황에서 Bool로 임포트됩니다.
6. NSManaged 속성을 CoreData가 자동생성한 키밸류코딩 호환 to-many 접근자에 접근하기 위한 메서드와 프로퍼티에 사용할 수 있습니다.
@NSManaged var employees: NSSet
@NSManaged func addEmployeesObject(employee: Employee)
@NSManaged func removeEmployeesObject(employee: Employee)
@NSManaged func addEmployees(employees: NSSet)
@NSManaged func removeEmployees(employees: NSSet)
- 이것들은 NSManagedObject 서브클래스에 선언되어야 합니다.
7. 타입 이름들과 enum cases들은 이제 디폴트로 자격없이(프로토콜 구현없이) print하고 String으로 변환할수 있습니다. debugPrint와 String(refelcting:)은 fully-qualified 이름을 얻기 위해 사용할 수 있습니다.
enum Fruit { case Apple, Banana, Strawberry }
print(Fruit.Apple) // "Apple"
debugPrint(Fruit.Apple) // "Fruit.Apple"
8. print()와 Mirrors의한 reflection은 current case와 멀티 payload 타입을 갖는 모든 enums payload를 report 할수 있습니다.
- 유일하게 reflection이 지원되지 않고 남아있는 enum 타입들은 @objc enums와 C에서 임포트된 enums 입니다.
Swift Standard Library Enhancements and Changes
1. SequenceType 프로토콜 구현체에 .forEach 구문을 사용할 수 있습니다.
(0..<10).forEach {
print($0)
}
위 구문은 아래구문과 유사합니다.
for x in 0..<10 {
print(x)
}
하지만 다음과 같은 차이점이 있습니다.
for-in 루프문과 다르게 break, continute를 사용하여 현재 클로져 몸체 호출을 종료하거나 일련의 호출을 건너뛸수
없습니다.클로져 몸체에 return문을 사용하면 외부 스콥이 아닌 현재 호출만 종료할수 있으며, 이어지는 일련의 호출을 건너뛸수 없습니다.
이러한 제약때문에 forEach 멤버는 함수형 알고리즘에 일련의 체이닝을 적용하고, 몸체가 적을때만 사용하도록 권장합니다.
foo.map {...}.filter {...}.forEach {...}
- 다른 경우에는 for..in 문 사용을 권장합니다.
3. SIMD: simd 모듈의 정수형 벡터타입은 &+, &-, & 연산자를 사용하여 감싸진 unchecked 산술식만 지원합니다. +,-, 연산자들은 정수형 벡터에는 사용 불가능하고 Xcode가 자동으로 wrapping 연산자로 교체하라고 제안할 것입니다.
4. SIMD: smid 모듈의 정수형 벡터타입 코드생성은 벡터하드웨어를 더 잘 사용할 수 있도록 개선되어 대부분의 경우에 극적으로 성능이 향상되었습니다.
5. Dictionary의 removeAtIndex(_:) 메서드는 key,value 쌍을 제거하여 두 엘리먼트를 튜플로 반환합니다.
- 유사하게 Set의 removeAtIndex(_:)은 삭제된 엘리멘트를 반환합니다.
6. Word와 UWord 타입은 표준라이브러리에서 삭제되었습니다. 대신 UInt를 사용하세요.
'Swift' 카테고리의 다른 글
| [swift2] Swift2에서의 문자열(strings) (0) | 2015.09.02 |
|---|---|
| [swift2] Xcode7 beta6 Swift 언어 변경사항 (0) | 2015.08.27 |
| [swift2] Xcode7 beta5 Swift 언어 변경사항 (0) | 2015.08.14 |
| [swift1.2] Swift 프로토콜 지향 프로그래밍 (0) | 2015.08.06 |
| [swift] 문자열과 문자 -1 (Strings and Characters) (0) | 2015.08.01 |
| [swift2] Xcode7 beta4 Swift 언어 변경사항 (0) | 2015.07.30 |
설정
트랙백
댓글
글
[swift1.2] Swift 프로토콜 지향 프로그래밍
WWDC 2015 세션중 "Protocol-Oriented Programming in Swift" 라는 흥미로운 세션이 있습니다. 애플에서 Standard Library 그룹의 리더를 맡고 있는 Dave Abrahams 라는 분이 발표한 세션입니다. 세션에서 제공하는 슬라이드와 InfoQ에서 이 세션을 정리한 아티클을 바탕으로 내용을 요약했습니다.
클래스는 멋집니다!
다음과 같은 멋진 특징들을 제공하지요.
- 캡슐화 (Encapsulation)
- 접근제어 (Access Cotrol)
- 추상화 (Abstraction)
- 네임스페이스 (Namespace)
- 풍부한 문법 (Epressive Syntax)
- 확장성 (Extensibility)
그런데...
구조체로도 다 할수 있습니다.
클래스는 훌륭합니다.타입은 훌륭합니다!
사실 위 특징들은 모든 타입들의 특징이며 클래스는 그것을 구현하는 하나의 방법에 불과합니다. 그렇다면 클래스 사용으로 인한 단점은 무엇이 있을까요?
1. 암묵적인 참조(Reference) 공유
- 두 객체가 동시에 어떤 객체를 참조하는 경우, 서로 그 사실을 모른채 그 객체를 변경할 수 있습니다.
- 참조 공유를 방지하기 위해 참조하는 객체를 복사할 수 있지만, 그렇게 하면 효율성이 나빠집니다.
- 참조를 공유하는 객체에 Race Conditions 이 발생할 수도 있습니다.
- Lock을 사용하여 레이스 컨디션을 방지할수 있지만 효율성은 더 나빠집니다.
- Lock을 잘못 사용하면 Deadlock 상태에 빠질 수 있습니다.
- 잦은 Lock 사용은 코드를 복잡하게 합니다.
- 복잡성은 더 많은 버그를 유발합니다.
Values Don't Share. (That's a good thing)
2. 상속 (비지니스에 관한 모든 것을 가지고 있습니다.)
- 슈퍼 클래스를 하나 밖에 가질수 없기 때문에, 초기에 잘 선택해야합니다.
- 슈퍼 클래스를 나중에 변경하는 것은 매우 어렵습니다.
- 슈퍼클래스가 갖는 모든 저장속성은 서브클래스에도 (강제적으로) 상속됩니다.
- 초기화 처리가 복잡해집니다.
- 수퍼클래스의 불변성을 깨뜨리지 않을수 없게됩니다.
- 언제 무엇을, 어떻게 재정의해야 하는지 (수퍼 클래스에 대해서)알아야 합니다.
More and more, we promote delegation.
3. 타입관계를 잃어버립니다.(Lost Type Relationships.)
- 추상 수퍼클래스와 서브클래스의 구현코드가 함께 존재하게 됩니다.
서브클래스 메소드에 접근하기 위해 수퍼클래스를 서브클래스로 다운캐스팅하면서 타입관계를 잃게됩니다.
이진탐색 클래스 모델 Ordered 정의
class Ordered {
func precedes(other: Ordered) -> Bool { fatalError("메소드를 구현해주세요.") }
}
class Number:Ordered {
var value:Double = 0
override func precedes(other: Ordered) -> Bool {
return value < (other as! Number).value //
}
}
func binarySearch(sortedKeys: [Ordered], forkey k: Ordered) -> Int {
var lo = 0
var hi = sortedKeys.count
while hi > lo {
let mid = lo + (hi - lo) / 2
if sortedKeys[mid].precedes(k) { lo = mid + 1 }
else { hi = mid }
}
return lo
}
as! ASubclass 는 타입 관계를 잃어버렸다는 신호
대부분 추상화를 위해 클래스를 사용하기 때문이다.
4. 좋은 추상화 메커니즘이란?
- Value type을 지원합니다.(클래스 외에도)
- 정적 타입관계를 지원합니다. (동적 디스패치 외에도)
- 큰 덩어리로 뭉치지 않아야합니다.
- Retroactive 모델링을 지원합니다.
- 모델에 인스턴스 데이터를 강요하지 않아야 합니다.
- 모델에 초기화의 부담이 없어야 합니다.
- 무엇을 구현해야 하는지 명확해야 합니다.
Swift는 프로토콜 지향 프로그래밍 언어!
5. 프로토콜 지향 프로그래밍
- Swift에서 새롭게 추상화를 생각할 때 첫 번째 포인트는 프로토콜!
이진탐색 프로토콜 모델 Ordered 변경해보겠습니다.
protocol Ordered {
func precedes(other:Self) -> Bool
}
struct Number : Ordered {
var value:Double = 0
func precedes(other:Number) -> Bool {
return self.value < other.value
}
}
이진탐색 함수를 정의합니다.
func binarySearch<T:Ordered>(sortedKeys:[T], forKey k: T) -> Int {
var lo = 0
var hi = sortedKeys.count
while hi > lo {
let mid = lo + (hi-lo)/2
if sortedKeys[mid].precedes(k) { lo = mid + 1 }
else { hi = mid }
}
return lo
}
Int 배열에 이진탐색 함수를 적용해 보겠습니다.
- 먼저 Int가 프로토콜 Ordered를 구현해야합니다.
// Int형 Ordered 프로토콜 구현하기
extension Int:Ordered {
func precedes(other: Int) -> Bool {
return self < other
}
}
- Int 배열에 이진탐색을 적용합니다.
// 1. 키 정렬
var keys = [10, 30, 1, 4, 5, 6, 7, 8, 55, 20]
var sortedKeys = keys.sorted { $0 < $1 }
// sortedKeys: [1, 4, 5, 6, 7, 8, 10, 20, 30, 55]
// 2. 찾을 키
var searchKey = 30
// 키 30의 index는 8.
// 3. 이진탐색
var searchIndex = binarySearch(sortedKeys, forkey: searchKey)
// searchIndex: 8
Number 배열에도 이진탐색을 적용해보겠습니다.
// 1. 정렬된 Number 배열 생성
var numbers = sortedKeys.map { return Number(value: Double($0)) }
// numbers: [1.0, 4.0, 5.0, 6.0, 7.0, 8.0, 10.0, 20.0, 30.0, 55.0]
// 2. 찾을 키
var keyToSearch = 5.0
// 3. 이진탐색
var index = binarySearch(numbers, forkey: Number(value: keyToSearch))
// index: 2
프로토콜을 사용하면, 커스텀 Number 구조체 뿐만 아니라 기존 타입에도 이진탐색을 쉽게 적용할 수 있음을 알 수 있습니다.
'Swift' 카테고리의 다른 글
| [swift2] Xcode7 beta6 Swift 언어 변경사항 (0) | 2015.08.27 |
|---|---|
| [swift2] Xcode7 beta5 Swift 언어 변경사항 (0) | 2015.08.14 |
| [swift1.2] Swift 프로토콜 지향 프로그래밍 (0) | 2015.08.06 |
| [swift] 문자열과 문자 -1 (Strings and Characters) (0) | 2015.08.01 |
| [swift2] Xcode7 beta4 Swift 언어 변경사항 (0) | 2015.07.30 |
| [swift] 성능향상 시키기 - 다이나믹 디스패치를 최소화 (0) | 2015.07.16 |
설정
트랙백
댓글
글
[swift] 문자열과 문자 -1 (Strings and Characters)
프래그램밍에서 기본은 문자열 다루기! Swift에서는 문자열과 문자를 어떻게 다룰까요?
문자열은 "hello" 같은 문자들의 순서집합
Swift 문자열은 String 타입으로 표현되며, String 타입은 Character타입 값들의 집합으로 표현됨.
String과 Character 타입은 코드에서 텍스트를 다룰 때 빠르고 유니코드 호환되는 방식을 제공.
문자열 생성과 조작 문법은 간단하고 가독성이 좋으며, C와 유사한 문자열 리터럴 문법을 가짐.
+ 연산자를 사용하여 두 문자열을 쉽게 연결가능.
문자열의 변경가능 유무는 Swift의 다른 값들처럼 var,let 에 따라 관리.
문법이 단순함에 더해 Swift의 문자열 타입은 빠르고 현대적은 구현을 갖음.
모든 문자열은 인코딩에 독립적인 유니코드 문자로 구성.
다양한 유니코드 표현방식으로 문자들 접근을 지원.
문자열 보간을 사용하여 문자열에 상수, 변수, 리터럴, 표현식 삽입가능.
1. 문자열 리터럴
문자열 리터럴은 쌍따옴표 쌍으로 감싸진 고정된 순서의 문자들입니다. 상수나 변수에 초기값으로 문자열 리터럴을 사용할수 있습니다.
let someString = "Some string literal value"
Swift는 someString 상수가 문자열 리터럴로 초기화 되었기 때문에 String 타입임을 추론합니다.
2. 빈 문자열 생성
var emptyString = "" // 문자열 리터럴 사용
var anotherEmptyString = String() // 문자열 생성자 사용
// 두 문자열은 비어있으면 두 표현은 동일함
isEmpty 속성을 사용해서 String 값이 비어있는지 검사할 수 있습니다.
if emptyString.isEmpty {
print("Nothing to see here")
}
3. 문자열의 수정가능 유무(Mutability)
문자열을 변수(var) 또는 상수(let)에 할당하느냐에 따라 String의 수정가능 유무를 가리킬수 있습니다.
var variableString = "Horse"
variableString += " and carriage"
// variableString은 "Horse and carriage"
let constantString = "Highlander"
costantString += " and another Highlander"
// 문자열 상수는 변경할수 없다는 컴파일 에러가 발생
문자열의 수정가능 유무를 NSString 클래스와 NSMutableString 클래스로 결정하는 Objective-C 와는 접근방식이 다릅니다.
4. 문자열은 Value타입!
Swift의 String타입은 value타입입니다. 새 문자열을 생성하면 문자열은 함수나 메소드에 전달되거나, 변수 또는 상수에 할당될 때마다 복사됩니다. 각 경우에 기존 문자열의 복사본이 생성되고 이 복사본이 전달되거나 할당됩니다.
Swift의 이런 특성(copy-by-default)은 함수나 메소드가 문자열을 전달받으면 어디에서 왔는지에 상관없이 완전히 문자열을 소유한다는 것을 보장해줍니다. 즉 스스로 문자열을 변경하지 않으면 전달받은 문자열은 결코 수정되지 않을 것이라 확실할 수 있습니다.
사실 Swift 컴파일러가 반드시 필요한 경우에만 복사가 일어나도록 문자열 사용을 최적화합니다. value 타입으로 문자열을 사용하면 항상 좋은 성능을 얻을 수 있습니다.
5. 문자 다루기
for-in 루프에서 character 속성을 사용해서 문자열의 개별 문자들에 접근할 수 있습니다.
for character in "Dog!".character {
print(character)
}
// D
// o
// g
// !
한문자만을 갖는 문자열 리터럴을 사용해서 Character 상수를 생성할수 있습니다.
let exclamationMar:Character = "!"
문자열은 Character의 배열을 생성자의 인자로 전달하여 생성할 수도 있습니다.
let catCharacters:[Character] = ["C", "a", "t", "!"]
let catString = String(catCharacters)
print(catString)
// Cat!
6. 문자열과 문자 접합
- 연산자를 사용하여 두 문자열을 접합한 새로운 문자열을 생성할 수 있습니다.
let string1 = "hello"
let string2 = " there"
var welcome = string1 + string2
// welcome은 "hello there"
문자열 변수에 += 연산자를 사용하여 문자열을 추가할 수도 있습니다.
var instruction = "look over"
instruction += string2
// instruction은 "look over there"
문자열 변수에 append() 메소드를 사용하여 문자를 추가할 수 있습니다.
let exclamationMark:Character = "!"
welcome.append(exclamationMark)
// welcome은 "hello there!"
Character 변수는 하나의 문자만 포함하기 때문에 다른 문자열이나 문자를 추가할 수 없습니다.
7. 문자열 보간(Interpolation)
문자열 보간은 문자열 리터럴 안에 상수, 변수, 리터럴, 표현식을 혼합하여 새로운 문자열을 생성하는 방식입니다. 각 항목은 백슬러시와 소괄호 안에 감싸여진 문자열 리터럴에 삽입됩니다.
let multiplier = 3
let message = "\(multiplier) times 2.5 is \(Double(multiplier) * 2.5)"
위 예에서 multipiler값은 문자열 리터럴 (multiplier)에 삽입됩니다. 새 문자열을 생성하기 위해 문자열 보간이 평가될때 placeholder는 multiplier의 실제값으로 대치됩니다.
표현식 Double(multiplier) * 2.5는 계산된 결과 7.5가 문자열에 삽입됩니다.
'Swift' 카테고리의 다른 글
| [swift2] Xcode7 beta5 Swift 언어 변경사항 (0) | 2015.08.14 |
|---|---|
| [swift1.2] Swift 프로토콜 지향 프로그래밍 (0) | 2015.08.06 |
| [swift] 문자열과 문자 -1 (Strings and Characters) (0) | 2015.08.01 |
| [swift2] Xcode7 beta4 Swift 언어 변경사항 (0) | 2015.07.30 |
| [swift] 성능향상 시키기 - 다이나믹 디스패치를 최소화 (0) | 2015.07.16 |
| [swift2] Xcode7 beta3 Swift 언어 변경사항 (0) | 2015.07.11 |
설정
트랙백
댓글
글
[swift2] Xcode7 beta4 Swift 언어 변경사항
드디어 Xcode7 beta4가 공개되었네요! beta4 에서 Swift 언어 변경사항에 대해 알아볼까요?
Objective-C Language Changes
1. 더블 언더스코어 nullability 한정자들 이름이 변경되었습니다.
- as is: __nullable, __nonnull, __null_unspecified
- to be: _Nullable, _Nonnull, _Null_unspecified
- 소스 호환성을 위해 컴파일러가더블 언더스코어 이름을 새로운 이름으로 맵핑하는 매크로를 정의해줍니다.(predefine)
Swift Language Changes
1. Enum과 Case에 indirect 키워드를 사용할 수 있습니다.
- indirect 키워드를 사용해서 재귀적인 데이터 구조를 정의할 수 있음.
- 재귀적인 데이터 구조 정의시 enum의 연관값은 간접적으로 저장됨
enum List<T> {
case Nil
indirect case Cons(head: T, tail: List<T>)
}
indirect enum Tree<T> {
case Leaf<T>
case Branch(left: Tree<T>, right: Tree<T>)
}
2. class 객체를 참조하는 AnyObject, NSObject 변수를 class Object 타입으로 복원이 가능합니다.
let x:AnyObject = NSObject.self // NSObject 클래스 객체를 AnyObject로 캐스팅
let y = x as! NSObject.Type // AnyObject를 NSObject 클래스 객체로 복원
3. class 객체를 포함하는 Array,Dictionary,Set 을 NSArray,NSDictionary,NSSet로 브리징할 수 있습니다.
- [NSURLSessionConfiguration protocolClasses]과 같이 NSArray * 객체를 반환하는 Objective-C API도 이제 Swift에서 정상동작합니다.
4. 클래스에 적용한 @objc 속성은 Objective-C 헤더에 생성된 클래스의 컴파일타임 이름 뿐만 아니라 런타임 이름도 변경합니다.
- 프로토콜에도 적용됨
// Swift
@objc(MyAppDelegate)
class AppDelegate : NSObject, UIApplicationDelegate {
//...
}
// Objective-C
@interface MyAppDelegate : NSObject <UIApplcationDelegate>
// ...
@end
5. 스칼라값의 0 엘리먼트를 참조하는 기능은 제거되었습니다.
6. 타입검사 제약시스템이 생성한 에러 진단메시지가 beta4에서 개선되었습니다. 이후 베타버전에서 더 많은 부분들이 개선될 예정입니다.
7. SinkType 프로토콜과 SinkOf 구조체가 표준 라이브러리에서 제거되고 대신 (T) -> () 클로져를 사용합니다.
8. ExtensibleCollectionType 부모프로토콜이 서브 프로토콜 RangeReplaceableCollectionType로 통함되었습니다.
- RangeReplaceableCollectionType 은 subRange를 다른 컬렉션의 엘리먼트들로 교체를 지원하는 컬렉션 타입.
- 기본구현을 위한 메소드들이 추가됨.
- 이 프로토콜들과 연관된 (free) Swift 모듈 함수들 대신 이 메서드들을 사용할 수 있습니다.
9. Unmanaged를 사용한 속성과 메서드들도 Objective-C에 노출가능합니다.
- Unmanaged는 unmananged 객체 레퍼런스를 전달하기 위한 타입.
- 이 타입을 사용하면 객체가 살아있도록 유지할 책임이 있음.
10. performSelector 류의 API 들 Swift 코드에서 사용가능합니다.
11. __unsafe_unretained로 마크된 C 구조체 필드는 Swift에 Unmanaged 로 표시됩니다.
- Swift 컴파일러는 이 참조들이 실제로 strong(+1) 또는 unretained(+0)로 의도되었는지는 알수 없음.
12. Swift 문서 주석들은 최상위 목록아이템을 인식합니다.
- Throws: ... 구문은 어떤 에러가 왜 던져질수 있는지 문서화하기 위해 사용합니다.
- Xcode QuickHelp의 파라미터 및 반환값 설명 옆에 표시됩니다.
13. 타입은 그 타입보다 더 낮은 버전에서 가용한 프로토콜을 구현할 수 있습니다.
@available (iOS 9.0, *)
protocol P { ... }
@available (iOS 7.0, *)
class MyController : UIViewController, P {
...
}
'Swift' 카테고리의 다른 글
| [swift1.2] Swift 프로토콜 지향 프로그래밍 (0) | 2015.08.06 |
|---|---|
| [swift] 문자열과 문자 -1 (Strings and Characters) (0) | 2015.08.01 |
| [swift2] Xcode7 beta4 Swift 언어 변경사항 (0) | 2015.07.30 |
| [swift] 성능향상 시키기 - 다이나믹 디스패치를 최소화 (0) | 2015.07.16 |
| [swift2] Xcode7 beta3 Swift 언어 변경사항 (0) | 2015.07.11 |
| [swift2] Swift에서 에러 처리 (Error Handling) (0) | 2015.07.10 |
설정
트랙백
댓글
글
[swift] 성능향상 시키기 - 다이나믹 디스패치를 최소화
애플 Swift 블로그의 [Increasing Performance by Reducing Dynamic Dispatch] 를 번역한 글입니다.
다른 언어들처럼 Swift도 수퍼클래스에 선언된 메서드와 속성을 오버라이드 할수 있습니다.
이말은 곧 프로그램이 런타임에 어떤 메서드와 프로퍼티를 참조할 것인지 결정해야하고, 간접호출 또는 간접접근을
수행해야 한다는 것을 의미합니다. 다이나믹 디스패치(dynamic dispatch)라고 불리는 이 기술은,
간접 호출/접근 횟수마다 일정량의 런타임 오버헤드 비용을 증가시킵니다. 성능에 민감한 코드에서는 그와같은 오버헤드
종종 바람직하지 않습니다. 이 포스트는 다이나믹함을 제거함으로써 성능을 향상시키는 3가지 방법에 대해 알려드립니다.
: final, private, 모듈 최적화.
다음 예를 살펴보겠습니다.
class ParticleModel {
var point = ( 0.0, 0.0 )
var velocity = 100.0
func updatePoint(newPoint: (Double, Double), newVelocity: Double) {
point = newPoint
velocity = newVelocity
}
func update(newP: (Double, Double), newV: Double) {
updatePoint(newP, newVelocity: newV)
}
}
var p = ParticleModel()
for i in stride(from: 0.0, through: 360, by: 1.0) {
p.update( (i * sin(i), i), newV: i*1000 )
}
작성된데로, 컴파일러는 동적으로 호출을 dispatch 할 것입니다.
1. p의 upodate 호출
2. p의 uodatePoint 호출
3. p의 point 튜플 속성에 접근한다
4. p의 velocity 속성에 접근한다
이것은 코드를 보면서 기대했던 것이 아닐수 있습니다. 동적 호출은 ParticleModel의 서브클래스들이 point 또는 velocity 를
계산속성이나 새롭게 구현된 updatePoint() 또는 update()로 오버라이딩 할수 있기 때문에 필수적입니다.
Swift에서 동적 디스패치콜은 메서드 테이블에서 함수를 찾고 간적호출을 수행하는 방식으로 구현되어집니다. 이것은 직접호출로
수행하는 것보다 더 느립니다. 게다가 간접호출은 많은 컴파일러 최적화를 막아 간접호출이 훨씬 더 많은 비용이 들도록 합니다.
성능 크리티컬한 코드에서 성능을 향샹시킬 필요가 있을때 다이나믹 행동을 제한하기 위해 사용할 수 있는 기술들이 있습니다.
1. 오버라이딩 할 필요가 없는 속성 선언에는 final을 사용하세요.
final은 클래스, 메서드, 속성이 오버라이드 할 수 없도록 제약하는 키워드입니다. 이렇게 하면 컴파일러가 다이나믹 디스패치의
간접호출/접근을 생략할 수 있게 합니다. 아래 예제의 point, velocity는 객체의 저장속성으로부터 직접적으로 액세스되고, updatePoint()는 direct 함수콜을 통해 호출됩니다. 반면 udpate()는 여전히 동적 디스패치를 통해 호출되며 서브클래스가 update()를 오버라이 할 수 있습니다.
class ParticleModel {
final var point = ( x:0.0, y:0.0 )
final var velocity = 100.0
final func updatePoint(newPoint: (Double, Double), newVelocity: Double) {
point = newPoint
velocity = newVelocity
}
func update(newP: (Double, Double), newV: Double) {
updatePoint(newP, newVelocity: newV)
}
}
var p = ParticleModel()
for i in stride(from: 0.0, through: 360, by: 1.0) {
p.update( (i * sin(i), i), newV: i*1000 )
}
final를 사용하여 전체 클래스 자체를 final로 만들수 있습니다. 이렇게 하면 서브클래싱이 불가능해지며 클래스의 모든 함수와 속성들이 final이 됩니다.
final class ParticleModel {
var point = ( x: 0.0, y: 0.0 )
var velocity = 100.0
// ...
}
2. private 키워드를 사용하면 한 파일에서만 참조되는 final 선언임을 추론할수 있습니다.
private 키워드를 선언부에 적용하면 현재파일에만 선언이 노출되도록 가시성을 제한합니다. 이것은 컴파일러가 모든 잠재적인 오버라이딩 선언을
찾을 수 있도록 합니다. 오버라이딩 선언이 없으면 컴파일러는 자동으로 final 키워드를 추론하고 메서드와 속성 접근에 대해 간접호출
(indirect calls)들을 제거합니다.
현재 파일에 ParticleModel 클래스에 대한 오버라이딩이 없다고 판단되면, 컴파일러는 private으로 선언된 모든 dynamic dispatch call을
직접호출로 대체합니다.
class ParticleModel {
private var point = ( x:0.0, y:0.0 )
private var velocity = 100.0
private func updatePoint(newPoint: (Double, Double), newVelocity: Double) {
point = newPoint
velocity = newVelocity
}
func update(newP: (Double, Double), newV: Double) {
updatePoint(newP, newVelocity: newV)
}
}
var p = ParticleModel()
for i in stride(from: 0.0, through: 360, by: 1.0) {
p.update( (i * sin(i), i), newV: i*1000 )
}
이전 예제에서처럼, point, velocity는 직접접근되며 updatePoint()는 직접호출 됩니다. 하지만 update()는 private이 아니기
때문에 간접적으로 호출됩니다.
final과 마찬가지로 클래스 선언부에 private을 사용하여 클래스 자체를 private로 만들고 그에 따라 모든 속성과 메서드를 private하게 만들
있습니다.
private class ParticleModel {
var point = ( x: 0.0, y: 0.0 )
var velocity = 100.0
}
3. internal 선언을 final로 추론할수 있는 전체 모듈 최적화를 사용하세요.
internal 접근자를 갖는 선언(디플트값)은 그들이 선언된 모듈 내에서만 볼수 접급될 수 있습니다. swift는 일반적으로 모듈을 구성하는
파일들을 개별적으로 컴파일하기 때문에 컴파일러는 internal 선언이 다른 파일에서 오버라이딩 됐는지 알수 없습니다. 그러나 전체 모듈최적
옵션을 활성화하면 모든 모듈이 동시에 한번에 컴파일 됩니다. 이렇게 하면 전체 모듈에 대해 컴파일러가 추론할수 있게되어 오버라이딩 되지
않은 internal 선언을 final로 추론할 수 있게됩니다.
원래 코드로 되돌아가서 이번에는 ParticleModel에 몇몇 public 키워드를 추가해보겠습니다.
public class ParticleModel {
var point = ( x:0.0, y:0.0 )
var velocity = 100.0
func updatePoint(newPoint: (Double, Double), newVelocity: Double) {
point = newPoint
velocity = newVelocity
}
public func update(newP: (Double, Double), newV: Double) {
updatePoint(newP, newVelocity: newV)
}
}
var p = ParticleModel()
for i in stride(from: 0.0, through: 360, by: 1.0) {
p.update( (i * sin(i), i), newV: i*1000 )
}
모듈 최적화(Module Optimization) 옵션을 활성화하고 이 코드를 컴파일하면, 컴파일러는 point, velocity,
updatePoint() 함수를 final로 추론할수 있습니다. 반면 udpate()는 public 접근자를 갖기 때문에 final로 추론
될 수 없습니다.
'Swift' 카테고리의 다른 글
| [swift] 문자열과 문자 -1 (Strings and Characters) (0) | 2015.08.01 |
|---|---|
| [swift2] Xcode7 beta4 Swift 언어 변경사항 (0) | 2015.07.30 |
| [swift] 성능향상 시키기 - 다이나믹 디스패치를 최소화 (0) | 2015.07.16 |
| [swift2] Xcode7 beta3 Swift 언어 변경사항 (0) | 2015.07.11 |
| [swift2] Swift에서 에러 처리 (Error Handling) (0) | 2015.07.10 |
| [swift2] 타입의 텍스트적인 표현 커스터마이징하기 (0) | 2015.07.01 |
설정
트랙백
댓글
글
[swift2] Xcode7 beta3 Swift 언어 변경사항
Swift Language Changes
1. 표현식 결과 포맷팅 방식 변경
po 또는 expr -O를 사용할 때 Swift 표현식 결과를 포맷팅하는 방식이 beta3에서 상당히 변화되었습니다.
첫 번째 beta 버전에서 소개되었던 Customization이 다음과 같은 방식으로 개선되었습니다.
CustomDebugStringConvertible 또는 CustomStringConvertible 프로토콜을 구현하는 타입들은
상대적으로 debugDescription, description 메소드를 사용하여 포맷팅 결과를 제공합니다. 둘다 구현하지 않은경우
타입이름이 출력되고, 레퍼런스 타입들은 Objective-C 클래스처럼 참조되는 주소를 보여줄 것입니다.레퍼런스 타입들과는 다르게 enums, tuples, struct 같은 value 타입들은 디폴트로 summary 아래에 모든 멤버들이
들여쓰기 되어 보여질것입니다. CustomReflectable 구현함으로써 이러한 동작들을 커스터마이징 할수 있습니다.
출력 커스터마이징은 모든 필드와 값들의 리스트를 제공하기 위해 p 또는 expr을 -O옵션없이 사용해서 건너뛸 수 있습니다.
2. String 타입 Enum의 기본값
string raw타입으로 선언된 enum 엘리먼트가 명시적으로 raw value를 갖지 않으면, enum의 이름을 디폴트 텍스트로
갖습니다.
예를 들어 다음 선언은
enum WorldLayer : String {
case Ground, BelowCharacter, Character
}
아래와 동일합니다.
enum WorldLayer : String {
case Ground = "Ground"
case BelowCharacter = "BelowCharacter"
case Character = "Character"
}
3. 이름 없는 파라미터
이름없는 파라미터는 명시적으로 이름이 없음을 의미하는 _:를 사용합니다**. 예를 들어 다음은 이제 에러입니다.
func f(Int) {} // 에러
다음과 같이 작성되야합니다.
func f(_:Int) {}
이것이 인자의 레이블 모델을 단순화시킵니다.
그리고 func f((a: Int, b:Int)) 같은 경우에 a 그리고 b라는 이름의 파라미터를 가지지 않는지 명료하게 해줍니다.
4. array에 tuple 추가가 가능합니다.
5. Objective-C 클래스의 제네릭 서브클래싱이 지원됩니다.
6. 문법이 조정되어 .으로 시작하는 라인들은 항상 메서드 또는 프로퍼티 룩업으로 파싱되어 다음과 같은 코드 포맷팅이 가능합니다.
foo
.bar
.bas = 68000
문맥에 따른 static member looup 으로 시작하는 라인은 더이상 사용 불가능합니다.
.staticVar = MyType()
7. try!
try! 표현식에서 발생한 에러, 최상위 또는 playground에서 처리되지 않은 에러들은 trap 메시지에 에러 값들이 표시됩니다.
8. NS_REFINDED_FOR_SWIFT 매크로
오리지널 구현을 사용가능하면서도, Swift의 동일 API에서는 더 좋은 버전을 제공하기 위해 NS_REFINED_FOR_SWIFT
매크로는를 Objective-C 선언부에 사용할수 있습니다. 예를 들어 클래스를 인자로 받는 Objective-C API는 Swift에서는
파라미터 타입을 더 정밀하게 제공할수 있습니다.
NS_REFINDED_FOR_SWIFT 매크로는 경우에 따라 다르게 동작합니다.
- init 메소드는 첫 번째 외부 파라미터 이름에 __이 붙는 initializer가 임포트 됩니다.
- (instancetype)initWithClassName:(NSString*)name NS_REFINED_FOR_SWIFT
=> init(__className:String)
- 다른 메소드들은 기본이름에 __이 붙어 임포트 됩니다.
- (NSString*)displayNameForMode:(DisplayMode)mode NS_REFINED_FOR_SWIFT;
=> func __displayNameForMode(mode:DisplayMode) -> String
subscript 메소드는 다른 메서드들처럼 취급되며 subcript로 임포트 되지 않습니다.
다른 선언들은 이름 앞에 __이 붙습니다.
@property DisplayMode mode NS_REFINED_FOR_SWIFT;
var __mode:DisplayMode { get set }
9. 타입 제네릭 파라미터
Swift 표준 라이브러리에서 제공하는 타입 제네릭 파라미터들은 API에서 타입의 역할을 반영하도록 이름이 변경되었습니다.
예를 들어 Array
'Swift' 카테고리의 다른 글
| [swift2] Xcode7 beta4 Swift 언어 변경사항 (0) | 2015.07.30 |
|---|---|
| [swift] 성능향상 시키기 - 다이나믹 디스패치를 최소화 (0) | 2015.07.16 |
| [swift2] Xcode7 beta3 Swift 언어 변경사항 (0) | 2015.07.11 |
| [swift2] Swift에서 에러 처리 (Error Handling) (0) | 2015.07.10 |
| [swift2] 타입의 텍스트적인 표현 커스터마이징하기 (0) | 2015.07.01 |
| [swift2] Xcode7 beta2 Swift 언어 변경사항 (0) | 2015.06.24 |
설정
트랙백
댓글
글
[swift2] Swift에서 에러 처리 (Error Handling)
(본 포스트는 The Swift Programming Language - Swift 2 Prerelease. 의 Error Handling 장을 번역한 문서입니다.)
에러처리는 프로그램에서 에러조건에 대응하고 회복하는 과정입니다. Swift는 런타임에 회복 가능한 에러를 던지고, 잡고,
전파하고, 조작하는 first-class(1급) support를 제공합니다. 몇몇 함수와 메소드는 항상 완전한 실행과 유용한 정보를
출력한다고 보장할 수 없습니다. Optional은 값의 부재를 표현하기 위해 사용되지만 함수가 실패했을때 실패원인을 이해하고
거기에 맞춰 코드를 대응하는게 더 유용할때가 있습니다.
디스크의 파일로부터 데이터를 읽고 처리하는 작업을 예로 들어보겠습니다.
경로에 파일이 없거나 읽기 권한이 없거나, 파일이 호환되지 않는 포맷으로 인코딩 되어있는 것 등 여러가지 이유로 작업이
실패할 수 있습니다. 이러한 상황의 차이를 구별하고 필요하다면 사용자와 커뮤니케이션해야 에러를 해결하고 회복할수 있습니다.
1. 에러의 표현 (Representing Errors)
스위프트에서 에러는 ErrorType 프로토콜을 구현하는 타입값으로 표현됩니다.
Swift enumerations는 특히 관련있는 에러조건(에러의 원인에 대한 정보를 연관값으로 가진채) 그룹을 모델링하는데
적합합니다. 이러한 이유로, Swift enumerations는 ErrorType을 자동으로 구현하고, 컴파일러에 의해 생성된 구현체를 갖습니다.
자판기의 에러조건을 표현하기 위한 예입니다.
enum VendingMachineError: ErrorType {
case InvalidSelection
case InsufficientFunds(required: Double)
case OutOfStock
}
이 예제에서 자판기는 다음과 같은 이유로 실패할수 있습니다.
- 1. 요청한 아이템이 유효한 선택이 아닌 경우. InvalidSelection 으로 표현.
- 2. 요청한 아이템 비용이 입금한 비용보다 더 큰 경우. InsufficentFunds로 표현.
연관된 Double 값은 트랜잭션을 완료하기 위해 필요한 추가요금. - 3. 요청한 아이템의 재고가 없는 경우. OutOfStock으로 표현.
2. 에러 던지기 (Throwing Errors)
함수 또는 메서드가 에러를 던질수 있다는것을 나타내기 위해, 선언부의 파라미터 다음에 throws 키워드를 사용합니다.
만일 return 타입이 있으면, throws 키워드는 리턴 화살표 앞에 표기합니다. 함수, 메소드, 클로져는 명시적으로 표시하지
않으면 에러를 던질 수 없습니다.
func canThrowErrors() throws -> String
func canThrowErrors() -> String
throws를 선언한 함수의 몸체 어느 시점에서도 throw 문을 사용하여 에러를 던질수 있습니다.
아래 예제에서 vend(itemNamed:) 함수는 요청한 아이템의 재고가 없거나, 금액이 부족할 때 에러를 던집니다.
stuct Item {
var price:Double
var count:Int
}
var inventory = [
"Candy Bar": Item(price:1.25, count:7),
"Chips": Item(price: 1.00, count:4),
"Pretzels": Item(price: 0.75, count:11)
]
var amountDeposited = 1.00
func vend(itemNamed name:String) throws {
guard var item = inventory[name] else {
throw VendingMachineError.InvalidSelection
}
guard item.count > 0 else {
throw VendingMachineError.OutOfStock
}
if amountDeposited >= item.price {
amountDeposited -= item.price
--item.count
inventory[name] = item
} else {
let amountRequired = item.price - amountDeposited
throw VendingMachineError.InsufficentFunds(required: amountRequired)
}
}
먼저 quard 문은 item 상수와 count 변수를 inventory의 대응하는 값과 바인드하기 위해 사용되었습니다.
item 이 inventory에 없으면 InvalidSelection 에러를 던집니다. 그 다음으로 요청한 item의 count를 체크해
가용성을 결정합니다. count가 0보다 같거나 작으면 OutOfStock 에러를 던집니다. 마지막으로 요청한 아이템의 가격과
현재 입금된 금액을 비교합니다. 입금된 금액이 아이템의 가격보다 크면, 입금액에서 가격만큼 차감되고, inventory에 있는
재고량이 감소합니다. 그리고 함수는 요청한 아이템을 반환합니다. 그렇지 않으면 초과된 금액이 연관값으로 전달되어 InsufficentFunds 에러를 던져집니다. throw 문은 즉시 프로그램의 제어를 이동시키기 때문에 아이템은 구매를 위한
모든 요구조건이 만족 될때만 판매됩니다.
throwing 함수를 호출할 때, 호출부 앞에 try를 붙힐 수 있습니다. 이 키워드는 함수가 에러를 던질수 있고,
try 이후 코드라인들이 실행되지 않을 수 있음을 암시합니다.
let favoriteSnacks = [
"Alice": "Chips",
"Bob": "Licorice",
"Eve": "Pretzels"
]
func buyFavoriteSnack(person:String) throws {
// a ?? b 구문은 a가 nil이 아니면 a를, nil이면 b를 반환.
let snackName = favoriteSnacks[person] ?? "Candy Bar"
try vend(itemNamed: snackName)
}
buyFavoriteSnack() 함수는 person이 가장 좋아하는 snack을 찾고, 그것을 구매하려고 시도합니다.
좋아하는 snack이 리스트에 없다면 candy bar를 사려고 시도합니다. 에러를 던질수도 있는 vend 함수가 호출되기
때문에 try 를 함수 앞에 사용했습니다. buyFavoriteSnack(_:) 함수는 throwing 함수이기 때문에, vend 함수
던지는 어떤 에러도 buyFavoriteSnack 함수가 호출되는 상위로 전파될수 있습니다..
3. 에러를 잡고 처리하기 (Catching and Handling Errors)
에러를 잡아 처리하기 위해 do-catch 문을 사용할 수 있습니다.
do {
try function that throws
...
} catch pattern {
statements
}
에러가 던져지면 catch 절에 의해 처리 될때까지 에러는 외부 스코프로 전파됩니다. catch 절은 catch 키워드 다음에
매칭할 에러패턴 그리고 실행할 구문집합으로 구성됩니다. switch 문처럼 컴파일러는 catch절이 모두 포괄되어 있는지
검사합니다. 그러한 결정이 이루어지면 에러는 처리됩니다. 해당 스코프에서는 에러를 처리하거나 또는 함수가 throws로
선언되어야 합니다. 에러가 처리됐다는 것을 보장하기 위해 모든 에러들과 매칭되는 패턴을 가진 catch절을 사용합니다.
catch절이 패턴을 명시하지 않으면 그 절은 로컬상수 error에 모든 에러를 매칭하여 바인딩합니다. 패턴 매칭에 관한 더
자세한 정보는 Patterns 장을 참고하세요.
do {
try vend(itemNamed: "Candy Bar")
} catch VendingMachineError.InvalidSelection {
print("Invalid Selection.")
} catch VendingMachineError.OutOfStock {
print("Out of Stock.")
} catch VendingMachineError.InsufficientFunds(let amountRequired) {
print("Insufficient funds. Please insert an additions $\(amountRequired).")
}
위 예제에서, vend(itemNamed:) 함수는 에러를 던질 가능성이 있기 때문에 try 표현식 안에 표현되었습니다.
에러가 던져지면, 실행흐름은 즉시, 전파를 계속할지 결정되는 catch 절로 이동합니다. 만일 에러가 던져지지 않았다면,
vend(itemNamed:)의 반환값은 snack에 할당되고 do문의 남아있는 구문들이 실행됩니다.
4. 에러 전파 비활성화 (Disabling Error Propagation)
사실상 런타임에 에러를 던지지 않는 throwing 함수나 메서드를 알수 있는 경우가 있습니다. 이러한 경우에는
보통 사용하는 try 표현 대신에 try!(forced-try) 표현을 사용하여 throwing 함수나 메서드를 호출할 수 있습니다.
try!를 사용하여 throwing 함수나 메서드를 호출하면 error 전파가 비활성화 되고, 에러를 던지지 않는 런타임
assertion 안에서의 호출로 랩핑합니다. 만일 실제로 에러가 thrown 되면, 런타임 에러를 만나게 될것입니다.
5. 클린업 액션 지정하기 (Specifying Clearn-Up Actions)
defer 구문을 사용해서, 코드 실행흐름이 현재 코드블럭을 떠나기 직전에, 일련의 코드 집합을 실행할 수 있습니다.
에러의 발생유무와는 상관없이 반드시 실행되야할 클린업 작업을 처리할수 있습니다. 예를 들면 열려진 파일 식별자를
닫거나, 수동으로 할당된 메모리를 해제하는 작업이 될 수 있습니다.
defer 문은 현재 scope이 끝날 때까지 실행을 연기합니다. defer 키워드와 나중에 실행할 코드 구문들로 구성됩니다.
연기된 구문들은 제어흐름을 벗어나게 하는 break 또는 return, 에러를 던지는 것과 같은 코드를 포함해서는 안됩니다.
연기된 액션들은 기술된 순서의 역순으로 실행됩니다. 즉 처음 defer 문에 있는 코드는 두 번째 defer 문의 코드 다음에
실행됩니다.
func processFile(filename:String) throws {
if exists(filename) {
let file = open(filename)
defer {
close(file)
}
while let = try file.readline() {
// work with the file
}
// close(file) is called here, at the end of the scope.
}
}
위 예제에서는 open() 함수가 대응하는 close() 함수 호출을 보장하도록 defer문을 사용했습니다. 이 호출은
에러가 던져졌는지 유무와 상관없이 실행됩니다.
'Swift' 카테고리의 다른 글
| [swift] 성능향상 시키기 - 다이나믹 디스패치를 최소화 (0) | 2015.07.16 |
|---|---|
| [swift2] Xcode7 beta3 Swift 언어 변경사항 (0) | 2015.07.11 |
| [swift2] Swift에서 에러 처리 (Error Handling) (0) | 2015.07.10 |
| [swift2] 타입의 텍스트적인 표현 커스터마이징하기 (0) | 2015.07.01 |
| [swift2] Xcode7 beta2 Swift 언어 변경사항 (0) | 2015.06.24 |
| [swift] 문자열 Indexing과 Slicing (0) | 2015.06.23 |
설정
트랙백
댓글
글
[swift2] 타입의 텍스트적인 표현 커스터마이징하기
1. 값의 문자열 표현 얻기
모델 객체를 화면이나 디버깅 코드에 표시할 때, 텍스트적인 표현을 생성하면 유용할 때가 있습니다.
swift에서는 특정 타입을 string 형태로 표현하거나, 디버깅 목적으로 화면에 출력하기위해 CustomStringConvertible과
CustomDebugStringConvert 프로토콜을 따르도록 선언할수 있습니다. 이 프로토콜 중 하나를 따르는 타입을 정의하면 print,
debugPrint와 같은 출력함수와 string interpolation 이 기본객체의 description을 호출하지 않고 이 함수들을 호출할 것입니다.
다음과 같은 그룹채팅을 위한 Message 타입이 있습니다.
struct Message {
let from: String
let contents: String
let date: NSDate
}
messages = [
Message(from: "Sandy", contents: "Hey, what's going on tonight?", date: messageDates[0]),
Message(from: "Michelle", contents: "Studying for Friday's exam. You guys aren't?", date: messageDates[1]),
Message(from: "Christian", contents: "Nope. That's what tomorrow is for. Let's get food, I'm hungry!", date: messageDates[2]),
Message(from: "Michelle", contents: "Maybe. What do you want to eat?", date: messageDates[3])
] 메세지 객체중 하나를 출력하면, struct의 기본 description을 얻을 수 있습니다.
구조체의 모든 정보를 출력하는데, 채팅앱과 같이 특정한 경우의 로깅과 디버깅을 위해 보기좋게 포맷팅 되어 있지는 않습니다.
디버깅을 위해 특정타입이 출력되는방식을 커스터마이징 하기위해 CustomDebugStringConvertiable 프로토콜을 적용할 수 있습니다.
2. 디버깅 정보 출력 - CustomDebugStringConvertible 프로토콜
CustomDebugStringConvertiable 프로토콜은 한개의 필수 속성인 debugDescription를 가집니다.
이 속성에서 반환된 string은 객체의 description을 제공하기 위해 debugPrint 와 같은 함수에 의해 사용됩니다.
CustomDebugStringConvertible 프로토콜을 적용한 뒤 메시지 출력을 시도해보겠습니다.
extension Message: CustomDebugStringConvertible {
public var debugDescription: String {
return "[\(date) From: \(from)] \(contents)"
}
}
debugPrint(messages[0])
이 description은 디버깅에 적합하며, 사용자의 채팅 메시지를 보여주고자 할때, 로컬시간이 적용된 좀더 사용자 친화적인 시간을
보여주고 싶을 수 있습니다. 좀더 사용자 친화적인 버전의 description을 생성하는 CustomStringnConvertible 프로토콜을 구현할 수 있습니다.
3. 사용자 친화적인 형식으로 출력 - CustomStringConvertible 프로토콜
CustomStringCovertible description을 제공하면, string interpolation과 print 함수는 더이상 debugDescription 속성을 사용하지 않습니다.
import Foundation
let dateFormatter = NSDateFormatter()
dateFormatter.doesRelativeDateFormatting = true
dateFormatter.dateStyle = .ShortStyle
dateFormatter.timeStyle = .ShortStyle
extension Message: CustomStringConvertible {
public var description: String {
return "\(contents)\n \(from) \(dateFormatter.stringFromDate(date))"
}
}
print(messages[0])4. 요약
- 디버깅 정보를 출력하기 위해서 CustomDebugStringConvertible 프로토콜을 구현합니다.
- 사용자 친화적인 정보를 출력하기 위해서 CustomStringConvertible 프로토콜을 구현합니다.
'Swift' 카테고리의 다른 글
| [swift2] Xcode7 beta3 Swift 언어 변경사항 (0) | 2015.07.11 |
|---|---|
| [swift2] Swift에서 에러 처리 (Error Handling) (0) | 2015.07.10 |
| [swift2] 타입의 텍스트적인 표현 커스터마이징하기 (0) | 2015.07.01 |
| [swift2] Xcode7 beta2 Swift 언어 변경사항 (0) | 2015.06.24 |
| [swift] 문자열 Indexing과 Slicing (0) | 2015.06.23 |
| [swift] Map, Filter, Reduce (0) | 2015.06.22 |
설정
트랙백
댓글
글
[swift2] Xcode7 beta2 Swift 언어 변경사항
Swift Language Changes
- payload를 가진 Enum case들은 함수처럼 사용될수 있습니다.
enum Either<T, U> {
case Left(T), Right(U)
}
let lefts: [Either<Int, String>] = [1, 2, 3].map(Either.Left)
let rights: [Either<Int, String>] = [“one”, “two”, “three”].map(Either.Right)
// => {{Left 1}, {Left 2}, {Left 3}}
- 구조체, enum, 프로토콜의 non-mutationg 메소드들에 부분적으로 self 파라미터가 적용됩니다.
let a: Set<Int> = [1, 2, 3]
let b: [Set<Int>] = [[1], [4]]
b.map(a.union)
// => [[1, 2, 3], [1, 2, 3, 4]]
- 정적타입 참조 또는 타입객체에 static 메소드처럼 .init를 사용하여 생성자를 초기화함수에 접근할 수 있습니다.
let x = String.init(5)
let stringType = String.self
let y = stringType.init(5)
let oneTwoThree = [1, 2, 3].map(String.init).reduce(“”, combine: +)
// => "123"
String(5) 처럼 정적타입을 사용해서 생성할때도 여전히 .init은 암묵적으로 호출됩니다. .init은 동적타입 객체를 사용할 때, 함수인자로 initializer를 참조할 때 사용됩니다.
거대한 구조체 또는 enum 타입의 코드를 생성할 때 코드 크기를 감소시키도록 개선되었습니다.
이제 Swift는 assertion이 활성화 된채 빌드됩니다. 실용적인 crash report를 생성하는데 도움이 될것입니다.
- 실행결과
'Swift' 카테고리의 다른 글
| [swift2] Swift에서 에러 처리 (Error Handling) (0) | 2015.07.10 |
|---|---|
| [swift2] 타입의 텍스트적인 표현 커스터마이징하기 (0) | 2015.07.01 |
| [swift2] Xcode7 beta2 Swift 언어 변경사항 (0) | 2015.06.24 |
| [swift] 문자열 Indexing과 Slicing (0) | 2015.06.23 |
| [swift] Map, Filter, Reduce (0) | 2015.06.22 |
| [swift2] New in Swift2.0 (0) | 2015.06.19 |
설정
트랙백
댓글
글
[swift] 문자열 Indexing과 Slicing
Indexing and Slicing Strings
Swift string의 Indexing과 Slicing 대해서 알아보겠습니다.
string은 화면에 보여지는 문자들의 순서집합이라고 생각하는 것이 일반적입니다.
하지만 하나의 문자로 보일지라도 메모리 상에서는 다중 혹은 가변길이의 유니코드값으로 표현되는 경우도 있습니다.
유니코드 문자열
예를 들어 hat emoji (🎩)는 문자 a와 같은 문자보다 인코딩에 더 많은 비트가 필요로합니다.
Swift의 String 타입은 이러한 세부사항들을 처리해줍니다. string을 이루는 각 문자는 (메모리에서 차지하는 길이와는 상관없이)
사용자가 인식하는 유니코드 문자를 표현합니다. 이런 추상화 때문에 정수를 사용해서 string의 인덱스를 표현하는 것이 적합하지
않을수 있습니다.
대신 표준라이브러리는 string에서의 위치를 표현하기에 적합한 String.Index를 제공합니다.
문자열 카운팅
다음 문자열은 가변길이의 유니코드 문자들로 구성되어 있습니다.
var str = "Héllo, 🇺🇸laygr😮und!"
// str의 유니코드 문자의 개수: 18
str.characters.count // swift2.0
count(str) // swift1.2
// str의 UTF-16코드 포인트의 개수: 22
str.utf16.count // swift2.0
count(str.utf16) // swift1.2
깃발 (🇺🇸) 문자는 사실 2개의 유니코드 스칼라값이 결합되어 구성되지만 Swift는 1개의 문자로 카운팅합니다.
이런 특성이 폼 유효성 검사, 커서 포지셔닝, 텍스트 wrapping 구현을 더 쉽게합니다.
문자열 카운팅, 인덱싱, 슬라이싱은 모두 메모리가 아닌 사용자가 인식하는 문자 인덱스를 기반으로 하고있습니다.
문자열 slicing
string을 index하고 slice하는데 사용하는 Range 값을 생성해보겠습니다.
let badRange = 4..<12 // Range<Int>를 생성합니다.
str[badRange] // error: 'subscript' is unavailable: cannot subscript String with range of Int
위 코드는 정수형을 string의 인덱스로 사용할수 없다는 컴파일 에러를 발생시킵니다.
대신 다음과 같이 String.Index 객체를 사용할 수 있습니다.
let range = advance(str.startIndex, 4)..<advance(str.startIndex, 12)
string은 startIndex 와 endIndex 속성을 갖습니다.
string의 successor(), predecessor() 함수(swift2.0에서 지원)는 인덱스를 증가 또는 감소시킵니다.
advance() 함수를 사용하여 파라미터의 값만큼 offset를 이동할수 있으며, advance()함수는 인자로 전달받은 offset 만큼 successor(),predecessor()를 호출합니다.
String.Index 타입은 유니코드를 인식할수 있어서 advance()함수가 항상 string index를 적절하게 이동시킬 수 있습니다.
advance() 함수는 인자로 받은 index 타입과 동일한 타입의 값을 반환하기 때문에 ..< range 연산자는 그 타입에 해당하는 range를
생성할수 있습니다.
advance() 함수를 사용하여 string의 특정 index의 문자를 조회해보겠습니다.
str[advance(str.startIndex, 7)] // 🇺🇸를 반환한다.
str[range.startIndex] // o를 반환한다.
str[range.endIndex] // 😮를 반환한다.
str[range] // o, 🇺🇸laygr를 반환한다.
advance() 함수
또 다른 버전의 advance()함수를 사용해서 메시지를 10자로 제한하는 기능을 구현해보겠습니다.
let messageCharacterLimit = 10
let range = advance(str.startIndex, messageCharacterLimit , str.endIndex)..<advance(str.endIndex, 0)
let result = str.substringWithRange(range)
if result.isEmpty {
print("empty result")
}
// "ygr😮und!"를 출력합니다.
advance()함수는 startIndex, offset 외에 세번째 파라미터로 endIndex를 전달하면 offset이 endIndex를 초과하는 경우
end.Index를 반환해줍니다. 세번째 인자는 일종의 상한값으로 동작합니다.
'Swift' 카테고리의 다른 글
| [swift2] 타입의 텍스트적인 표현 커스터마이징하기 (0) | 2015.07.01 |
|---|---|
| [swift2] Xcode7 beta2 Swift 언어 변경사항 (0) | 2015.06.24 |
| [swift] 문자열 Indexing과 Slicing (0) | 2015.06.23 |
| [swift] Map, Filter, Reduce (0) | 2015.06.22 |
| [swift2] New in Swift2.0 (0) | 2015.06.19 |
| [swift] Apple Swift Blog (0) | 2015.06.10 |
