누군가에게 알려주기 보다는 나 스스로 정리 하며 언젠가 다시 사용할 때를 대비하는 글을 작성할것이다.
참고자료 : swift.org
현재 나는 Objective-C 와 Swift 언어로 iOS앱의 개발을 할 수 있지만 뭔가가 부족한 느낌을 항상 받았다.
거진 다 아는 내용이지만 머리로는 알겠는데 말로 하거나 조금만 심화로 Deep 하게 들어가면 부족한 것을 너무 절실하게 느꼈다.
그래서 기초가 조금 더 탄탄했으면 했는데 그 기초를 다시 공부하면서 조금 더 탄탄하게 쌓을 생각이다.
당연하게도 이 글은 기초 공부지만 어느정도 swift를 아는 놈이 작성하는거라 문서를 보고 하지만 약간 야매같은 느낌으로 이 글을 딱딱하게 책을 읽듯 요약하는게 아니라 나만의 방식으로 유연한 요약을 목표로 할 생각이다.
swift는 문서에 따르면 새로 배우기 쉽고 표현하기 좋으며 재밌는 언어다.
또한 최신 프로그래밍 패턴을 채택해 많은 클래스의 일반적 오류를 정의한다.
강력한 타입 추론과 패턴 매칭을 통해 간결한 표현이 가능하다.
결국에는 Swift에 대한 자랑이라서 중요하다 싶은 부분만 요약하고 넘어가겠다.
swift는 입력/출력 또는 문자열 처리와 같은 기능을 위한 별도의 라이브러리가 필요 없다.
전역 범위로 작성한 코드는 전체에서 사용되기에 main()
이 필요 없다.
모든 구문의 끝에는 세미콜론도 필요 없다.
해당 부분에서는 swift에 대한 간단한 코드 작성 방법에 대해서 알려준다.
swift는 main() 필요없고 세미콜론(;)도 굳이 붙일 필요 없음
상수 선언 = let / 변수 선언 = var
항상 타입을 명시해야 하는것은 아니다. 값을 제공하면 컴파일러는 타입을 유추한다. 하지만 초기값이 충분한 정보를 제공하지 않거나 없는 경우 뒤 콜론(:)으로 구분해 타입을 지정한다.
let intNumber: Int = 30
만들어진 값은 다른 타입의 값으로 절대 변경되지 않는다.
값을 다른 타입으로 변경해야 하면 원하는 타입의 인스턴스를 만들어야 한다.
Int에서 String로 변환하는 경우에는 다음과 같이 할 수 있다.
let intNumber: Int = 10
let strText: String = "Test Text"
let numberText: String = strText + String(intNumber)
이렇게 작성을 하는 방법도 있지만 조금 더 쉬운 방법도 존재
let intNumber: Int = 10
let numberText: String = "Test Text \(intNumber)"
이렇게 작성을 하게 되면 두 코드는 똑같은 값을 내보내게 될 것이다.
또한 \()
안에 인스턴스가 들어가기에 단순 변수나 상수 뿐 아니라 내부에서 특정한 동작
을 할 수 있다.
String 에서 여러 문자열을 입력하고 싶으면 """
를 사용해서 작성한다.
대괄호 [ ]
를 사용하여 배열(Array) 딕셔너리(Dictionary)를 생성할 수 있다.
let arrayTest: [String] = ["A","B","C"]
let dictTest: Dictionary<String, String> = ["A":"a","B":"b"]
요소를 추가함에 따라 자동으로 그 크기는 증가한다.
변수와 마찬가지로 빈 값
으로 선언도 가능하다.
var arrayEmpty: [String] = []
var dictEmpty: [Sting: String] = [:]
빈 값
으로 배열과 딕셔너리를 할당하려면 그 타입을 명시해줘야
한다.
Dictionary의 타입 명시 방법은 위와 아래 방법 편한거로 사용하면 된다.
타입 추론이 참으로 좋은 방법이긴 하고 편하긴 하지만 개인적으로 나는 모든 타입의 선언시에 전부 타입을 명시하는 편이긴하다.
|
속도 면에서도 그렇지만 유지보수를 위해 단순히 변수만 보고 이 변수가 어떠한 타입인지 바로바로 알 수 있다는 큰 장점이 있기 때문이다.
|
여러 타입에 대해서는 후에 더 자세하게 설명이 될 예정이다.
조건문
if (조건) {
...
} else if (조건2) {
...
} else {
...
}
switch (조건) {
case (요소1):
...
case (요소2):
...
default:
...
반복문
break
를 사용한다.for i in 0 ..< 5 {
...
}
while i < 10 {
...
}
repeat {
...
} while i < 10
조건문과 반복문에 대해서는 조금 더 심화 과정이 있으나 해당 부분에서는 단순히 이런게 있다는 느낌으로 진행이 되기에 조금 심화 부분은 후에 해당 부분에서 작성할 예정이다.
함수
func
을 사용한다.->
이후에 작성한다func testMakeFunc(first: Int) -> Int {
return 1
}
매개변수 vs 인수
인수는 해당 함수를 호출하여 데이터를전달하는 값
을 인수(argument)라고 하는거고 함수를 만들때 함수의 선언부에 들어가는 즉전달된 값을 받는
변수를 매개변수(parameter)라고 한다.
|
요거는 용어적인 구분인데 이정도는 제대로 구분을 하고 있는게 좋을 듯 하여 이렇게 따로 작성을 하였다.
함수는 1급 타입 = first-class type 으로 이것은 함수가 다른 함수를 값으로 반환할 수 있다는 뜻이다.
또한 다른 함수를 인수로 받을 수 있다.
func testFunc(first: (Int) -> Bool) -> ((Int) -> Bool) {
func addTest(number: Int) -> Bool {
if number == 1 {
return true
}
}
if first {
return addTest(number: 1)
} else {
return addTest(number: 2)
}
}
func checkResult(number: Int) -> Bool {
if number == 1 {
return true
}
return false
}
testFunc(first: checkResult)
함수는 거의 이게 전부라고 보면 될 정도로 그 사용이 복잡하지는 않다. 하지만 조금 심화되는 부분이 있기에 해당 부분에 대해서는 함수 파트에서 따로 작성할 예정이다.
클로저
클로저에 대해서는 함수의 일종이라 보면 된다.
해당 부분은 이곳에서 짧게 다룰바에 그냥 클로저만 따로 다루는게 맞다 생각해 이 부분은 넘기도록 하겠다.
클래스
소괄호 ( )
를 넣어 클래스의 인스턴스를 생성할 수 있으며 인스턴스의 프로퍼티와 메서드에 접근하기 위해서는 점 .
구문을 사용한다.클래스를 생성할 때 내부의 프로퍼티라던가 init, getter, setter, self, 등 여러 부분에 대해서는 후에 클래스와 관련된 부분에서 조금 더 자세하게 작성할 예정이다.
열거형
enum
을 사용한다. 열거형도 메서드를 가질 수 있다.구조체
struct
을 사용한다.열거형과 구조체의 좀 더 자세한 활용 방법은 뒤에서 다룰 예정이다
구조체와 클래스
이 두 타입은 매우 비슷한 동작을 지원한다.
하지만 둘의 가장 큰 차이점은 구조체는 항상 복사되어 값으로 전달이 되지만 클래스의 경우에는 참조로 전달이 된다는 차이가 있다.
|
해당 내용에 대해서는 구조체 부분에서 조금더 자세히 다룰 예정이다.
비동기적 실행 함수를 나타내기 위해서 async
를 사용한다.
func asyncFunc() async -> Int {
return 1
}
앞에 await
를 작성하여 비동기 함수를 호출하는 것을 나타낸다.
let asyncLet = await asyncFunc()
비동기 함수 호출 위해 asnyc let
을 사용해 다른 비동기 코드와 병렬 실행이 가능하다. await
를 사용해 반환값을 사용한다.
비동기 함수의 반환을 기다리지 않고 동기 코드에서 비동기 함수 호출 하려면 Task
를 사용한다.
func asyncLetFunc() async {
async let number = asyncFunc()
}
Task {
await asyncLetFunc()
}
이 부분은 이렇게 글로 요약 된걸 봐봤자 잘 모른다.
그냥 동기와 비동기가 무슨 차이인지 둘을 왜 구분을 해서 사용을 해야 하는건지 그런거만 잘 생각해보고 자세한 내용은 후에 확인을 하도록 하자
프로토콜
protocol
을 사용한다.확장
확장 (extension)
을 사용한다.프로토콜과 확장
이 두 가지는 매우 중요하고 자주 쓰이는 부분이다.
자세한 활용 방법은 뒤에서 다시 작성하겠다.
에러를 던지기 위해서 throw
를 사용하고 에러를 던질 수 있는 함수를 나타내기 위해서 throws
를 사용한다.
함수에서 에러가 발생하면 함수는 즉시 반환된고 호출한 코드가 에러를 처리한다.
func errorThrowFunc() throw {
...
}
에러를 처리하는 방법은 몇가지가 존재 하는데 하나의 방법으로는 do-catch
를 사용하는 것이다.
do 블럭
내에서 앞에 try
를 작성해 에러가 발생할 수 있는 코드임을 표시한다.
func errorDoCatchFunc() {
do {
let check = try errorThrowFunc()
} catch {
print("ERROR: \(error)")
}
}
catch 블럭 내에서 에러는 다른 이름으로 지정하기 전까지는 error
이라는 이름으로 주어진다.
특정 에러를 처리하는 여러개의 catch 블럭을 제공할 수 있으며 switch 에서 하는 것처럼 사용하면 된다.
해당 부분을 자세하게 다루는 방법은 후에 조금 더 자세하게 설명하도록 하겠다.
제너릭은 꺾쇠 < >
안에 이름을 작성해서 생성한다.
제너릭은 함수와 메서드 뿐 아니라 클래스와 열거형, 구조체도 만들 수 있다.
func makeGenericFunc<T>(num: T) -> T {
return num
}
enum GenericEnum<T> {
case none
case some(T)
}
요구사항의 리스트들을 지정하기 위해서는 본문 바로 전에 where
을 사용해야 한다. 타입이 프로토콜을 구현하도록 요구하거나 클래스에 특정 상위 클래스가 있어야 한다든가 하는 요구를 필요로 할 때 말이다.
제너릭도 잘 활용하면 아주 유용한 도구가 되므로 지금은 이런게 있다는것만 알아두면 된다.
당연 틀린 부분 지적은 감사하나 비난은 정중하게 사양하겠다.