누군가에게 알려주기 보다는 나 스스로 정리 하며 언젠가 다시 사용할 때를 대비하는 글을 작성할것이다.
참고자료 : swift.org
현재 나는 Objective-C 와 Swift 언어로 iOS앱의 개발을 할 수 있지만 뭔가가 부족한 느낌을 항상 받았다.
거진 다 아는 내용이지만 머리로는 알겠는데 말로 하거나 조금만 심화로 Deep 하게 들어가면 부족한 것을 너무 절실하게 느꼈다.
그래서 기초가 조금 더 탄탄했으면 했는데 그 기초를 다시 공부하면서 조금 더 탄탄하게 쌓을 생각이다.
당연하게도 이 글은 기초 공부지만 어느정도 swift를 아는 놈이 작성하는거라 문서를 보고 하지만 약간 야매같은 느낌으로 이 글을 딱딱하게 책을 읽듯 요약하는게 아니라 나만의 방식으로 유연한 요약을 목표로 할 생각이다.
상수 : let , 변수 : var
let x = 10
var y = 20
여러개의 상수 또는 여러개의 변수 선언시 콤마로 구분해 한줄로 선언 가능
let x = 10, y = 20
자신이 선언한 해당 변수가 후에 코드내에서 저장 값이 변경이 되지 않는다면 let
으로 선언을 해야 한다. (안해도 상관은 없지만 경고가 뜬다.)
앞으로 특별한 언급없이 변수라고 하는 경우에는 상수 또한 마찬가지로 적용되는 사항들이라고 알면된다.
둘은 다른것이지만 포스트 작성의 편의를 위해서 특별하게 구분하는 경우가 아니면 변수로 통일해서 작성을 하도록 하겠다.
변수 선언시 저장할 수 있는 값의 종류를 명확하게 하기 위해 타입을 작성해준다.
swift는 타입 세이프티
와 타입 추론
이 있기에 사용될 타입을 거의 항상 유추할 수 있다.
근데 아무리 추론이 있다고는 하지만 타입을 직접 명시해주는게 속도적인 측면에서도 그렇고 후에 유지보수 및 코드를 읽을때 변수 선언부만 보고도 이게 무슨 타입의 변수인지를 바로 알 수 있기에 타입 명시를 해주는게 좋다.
let num: Int = 10
let number = 10
변수의 이름은 유니코드 문자를 포함해 대부분의 문자를 포함 할 수 있다.
let num = 10
let 숫자 = 10
let 🍔 = 10
하지만 공백, 수학적 기호, 화살표, 내부 사용 유니코드 스칼라 값, 선과 박스를 그리는 문자는 불가능
숫자는 사용이 가능하지만 이름의 시작으로는 사용할 수 없다.
변수를 선언하면 동일 이름으로 다시 선언할 수 없다.
다른 타입으로 저장하게 변경해 선언할 수 없다.
상수를 변수로 바꾸거나 그 역도 할 수 없다.
swift의 키워드와 동일한 이름으로 변수를 제공하고 싶다면 백틱 (```)
으로 묶으면 사용할 수 있는데 "진짜로 무조건 이거를 써야만 해!" 라는게 아니면 쓰지 말것.
print()
함수를 사용하여 변수의 값을 콘솔 창에 출력할 수 있다.
...
은 코드를 의미한다고 필자는 작성했다.
// ...
: 한줄 주석
/* ... */
: 여러줄 주석
세미콜론 (;)
은 필수 조건이 아니나 여러 구문을 한 줄 작성시에는 필수로 사용한다.
let cat = "Cat"; print("\(cat)")
min
과 max
프로퍼티를 통해 각각 최소값, 최대값을 가져올 수 있다.
대부분의 경우 정수의 사이즈를 결정할 필요는 없다.
특정 크기의 정수로 작업하는게 아니라면 Int 를 사용하면 된다.
Int는 -2,147,483,648
과 2,147,483,647
사이의 값을 저장할 수 있으며 일반적 사용에는 문제가 없다.
부호없는 정수 타입이 필요한 경우에만 사용하며 음수가 아니어도 Int를 더 선호한다.
2가지의 부호를 가진 부동 소수점 숫자 타입을 제공한다.
근데 부동소수점은 정확도의 차이가 존재한다.
가장 큰 예 중 하나가0.1 + 0.1 != 0.2
가 되는데 이는 부동 소수점이 가지는 소수점 때문에 발생하는 이슈이다.
Double는 최소 15자리의 소수점 정확도를 가지고 있으나 Float 는 6자리의 정확도를 가진다.
어느 타입으로 작업을 하는지는 코드에 따라 다르겠지만 일반적으로Double
가 더 선호된다.
Swift는 type-safe 언어이다. 타입 세이프 언어를 사용하면 사용할 수 있는 값의 타입을 명확하게 알 수 있으며 String 이 필요한 경우 실수로 Int 를 전달 할 수 없다.
이로 인해 모든 변수의 타입을 지정해야 하는것은 아니다. swift는 특정 타입을 지정하지 않으면 적절한 타입으로 타입 추론을 사용하며 타입 추론을 통해 컴파일러는 코드를 컴파일 시 제공한 값을 검사해 특정 식의 타입을 자동으로 추론할 수 있다.
타입 추론 좋은데 위에도 적었다시피 타입은 최대한 명시하는 편이 필자는 좋다는 생각을 갖고 있다.
정수 리터럴은 다음과 같이 쓸 수 있다.
0b
접두사 : 2진수0o
접수사 : 8진수0x
접두사 : 16진수숫자 리터럴은 읽기 쉽게 만드는 추가 포맷을 포함 할 수 있다. 정수와 부동소수점 모두 추가 0로 채워질 수 있으며 가독성을 위해 _ 포함이 가능하다.
let paddingDouble: Double = 000123.456
let lineInt: Int = 1_000_000_000
만약에 이런게 필요하면 외워둘 필요 없이 그냥 구글 켜서 바로 검색하자 그게 훨씬 가성비가 좋다.
이거 막 뭐라고 써있고 하던데 그냥 솔직히 다 볼 필요 없다.
요약하면 Int형은 Int 끼리 계산하도록 해야 하고 필요하다면 그 타입을 변환을 해줘야 한다! 라는 뜻이다.
|
이건 숫자뿐 아니라 타입간 타입 변환시에 많이 사용하는 방법인데SomeType(InitialValue)
라는 방법으로 타입을 쓰고 괄호 안에 값을 넣어주면 된다.
타입 별칭은 이미 존재하는 타입을 다른 이름으로 정의할 때 사용한다.
typealias Success = Bool
단순히 특정한 타입을 다른 이름으로 정의하는거라 가독성을 위해서 사용하는 경우에 매우 유용하다.
Bool
타입은 true
or false
두가지의 값만 가지므로 논리적참조시 사용된다.
if의 조건으로 사용하기에 아주 좋은 타입으로 많이 사용하게 될것이다.
또한0 == false
와1 == true
가 되기에 해당 방법으로 사용할 수 도 있다.
여러값을 단일 복합 값으로 그룹화 한다. 튜플 내부에는 어떠한 타입도 가능하기에 구성이 되는 요소들이 전부 같은 타입일 필요는 없다.
모든 타입의 튜플을 만들 수 있으며 원하는 만큼 다른 타입을 포함시킬 수 있다.
사용 방법은 아래와 같이 사용할 수 있다.
튜플 정의하고 인덱스를 사용해 개별 요소 값에 접근 할 수 있다.
튜플 분해시 _ 를 사용하여 튜플의 일부를 무시할 수 있다.
튜플 정의시 요소에 이름을 정할 수 있다. 요소에 이름이 있으면 요소로 접근 가능하다.
let tupleTest = (123, "ABC")
print(tupleTest.0)
// 123
let (numTuple, _) = tupleTest
print(numTuple)
// 123
let tupleTest2 = (numTuple2: 123, strTuple2: "ABC")
print(tupleTest2.numTuple2)
// 123
튜플은 간단한 값의 그룹에 유용하며 조금 복잡해지는 경우에는 구조체나 클래스를 사용하는것이 좋다.
옵셔널은 값이 없을 수 있는 경우에 사용한다.
옵셔널을 선언하는 경우에는 변수의 타입 뒤에 ?
를 입력하여 선언한다.
let optionalTest: String?
옵셔널에서 값이 없는 경우에 nil
이라는 특수한 값을 지정하여 해당 상태를 나타낼 수 있다.
옵셔널이 아닌 변수에는 nil을 사용할 수 없고 만약 변수가 값이 없는 상태의 동작이 필요하다면 해당 변수는 옵셔널로 선언해야 한다.
if는 옵셔널과 nil을 비교해 옵셔널 타입에 값이 포함되어 있는지 확인할 수 있다.
강제언래핑
은 !
를 옵셔널 변수 뒤에 적어서 사용을 한다.
하지만 사용전에 값이 nil 이라면 런타입 에러가 나타나게 된다.
! 로 강제 언래핑 하는건 하지 않도록 하자가 아니고 그냥 하지마!
어떻게든 아래서 나올 바인딩 해서 다 풀어서 사용 할 것!
이때 사용하는 방법이 ìf
를 사용하는 방법과 guard
를 사용하는 방법이 있다.
두 방식의 차이점이라고 하면 if 구문에서 바인딩으로 생성된 경우에는 오직 if 구문 내에서만 사요잉 가능하나 guard 로 생성된 경우에는 해당 구문 다음 부터 사용이 가능하다.
let str: Sting? = "ABC"
print(str)
//Optional(ABC)
if let str = str {
print(str)
}
// ABC
guard let str = str as? String else { return }
print(str)
// ABC
guard 문의 경우에는 후에 자세하게 설명할 예정이니 지금은 저러한 방법이 있다는 거만 알 고 있음면 된다.
때로는 처음에 설정 후 항상 값을 갖는것이 분명한 경우가 있다. 이러한 경우에 접근할 때마다 계속 확인하고 언래핑하는 작업을 할 필요는 없다.
그래서 압시적으로 언래핑 된 옵셔널
을 정의하기 위해 ?
대신에 !
를 작성하여 암시적 언래핑 옵셔널을 선언해준다.
let optionalTest: String? = "Test Text"
let optionalTest2: String! = "Test Text"
해당 방법은 !
와 같다고 볼 수 도 있지만 약간 다른 방법으로 돌아간다고 볼 수 있다.
let notOpt: String = optionalTest2
let strOpt = optionalTest2
이렇게 2개의 상수가 선언이 되었을 때 notOpt
의 경우에는 String 이라고 타입 명시가 되어있어 옵셔널이 아님을 나타내고 있다 그렇기에 해당 변수에는 옵셔널이 들어가면 안된다.
그러면 암시적으로 언래핑된 옵셔널 타입이던 optionalTest2는 강제적으로 언래핑 하여 해당 값을 할당한다.
하지만 밑에 strOpt
의 경우에는 타입 추론이기에 기본적으로 옵셔널 값을 받게 된다.
중요한건 나중에 nil 이 될 가능성이 0.0000000000001 이라도 있다면
!
는 사용하는게 아니다.
암시적 언래핑이고 뭐고 필자는 코드에 !가 들어가는것을 좋아하지 않는다.
프로그램이 실행되는 동안 에러 발생 처리를 위해 에러 처리를 사용한다.
이러한 에러 처리 방법에는 여러 가지가 존재 한다.
함수 선언에서 throws
키워드를 붙여서 해당 함수에서 에러가 발생 할 수 있음을 나타낸다.
에러가 발생할 수 있는 함수를 호출 할 때는 표현식 앞에 try
를 붙여서 나타낸다.
catch
절에 의해 처리될 때까지 현재 범위에서 에러를 자동으로 보낸다.
func canThrowAnError() throws {
...
}
do {
try canThrowAnError()
} catch {
print("ERROR: \(error)")
}
이때 do
구문은 에러를 하나이상의 catch
에게 보낼 수 있는 범위를 만든다.
에러처리는 직접 사용을 해보면서 익히는게 제일 빠르며 후에 에러처리 관련해서 자세한 포스트를 작성하면서 조금더 많은 사용법을 작성할 예정이다.
약간 이건 부끄러운 말이지만 해당 용어에 대해서 필자는 처음 들어봤다.
런타임시 발생하는 조건이다. 추가 코드 실행 전 이를 사용해 필수조건이 충족되는지 확인할 수 있다.
역설
은 개발과정에서 실수와 잘못된 가정을 찾는데 도움이 되고
전제조건
은 프로덕션 문제를 감지하는데 도움이 된다.
역설과 전제조건을 사용하는 것은 유효하지 않는 조건이 발생하지 않게 코드를 디자인하기 위함이다.
뭔가 사용은 해본거 같은데 하지 않아서 이 부분에 대해서는 요약이 불가능해서 이건 이 정도로 줄이고 후에 알게 되거나 사용을 했다면 다른 포스트로 찾아오도록 하겠다.
당연 틀린 부분 지적은 감사하나 비난은 정중하게 사양하겠다.