[iOS] iOS 스터디
iOS 스터디
for문
- 일반적인 사용법
let arr = [1,2,3,4,5,6,7,8,9,10]
for i in arr {
if i % 2 == 0 {
print(i)
}
}
- where 문을 사용했을 때
for i in arr where i % 2 == 0 {
print(i)
}
- depth가 깊지 않게 코드를 작성할 수 있다(가독성이 좋아진다)
- forEach
arr.forEach {
print($0)
}
- closure 방식
- enumerate
let dict = [
[1: "apple"],
[2: "banana"],
[3: "grape"],
[4: "watermelon"],
]
for (key, value) in dict.enumerated() {
print("key: \(key)")
print("value: \(value)")
}
조건문
- 스위프트에서는 조건문의 괄호를 제거해도 된다(권장)
// 1번
if (flag) {
print("true")
}
// 2번
if flag {
print("true")
}
// 3번
if (flag && (num1 == num2)) {
print("true")
}
// 4번
if flag && (num1 == num2) {
print("true")
}
- 여러개의 조건을 썻을 때 가독성이 좋아진다
- ,(콤마)로 조건문 표현
// 1번
if flag1 && flag2 {...}
// 2번
if flag1, flag2 {...}
- 두 조건문의 의미는 똑같지만 2번 조건문은 앞에 조건(flag1)이 false일 경우 뒤에 있는 조건문은 건너뛸 수 있다.(속도 향상)
switch문
- 조건문과 똑같이 괄호 생략 가능
// 1번
switch (value) {
case pattern:
code
default:
code
}
// 2번
switch value {
case pattern:
code
default:
code
}
- break를 안적어도 된다
switch grade {
case "A":
print("A")
break
case "B":
print("B")
break
case "C":
print("C")
break
default:
print("D")
break
}
switch grade {
case "A":
print("A")
case "B":
print("B")
case "C":
print("C")
default:
print("D")
}
- case에 여러 패턴을 명시할 수 있다
switch grade {
case "A", "B":
print("A, B")
case "C", "D", "E", "F":
print("C, D, E, F")
default:
break
}
- if 문과 비교
if grade == "A" {
print("A")
} else if grade == "B" {
print("B")
} else if grade == "C" {
print("C")
} else {
print("D")
}
switch grade {
case "A":
print("A")
case "B":
print("B")
case "C":
print("C")
- 조건이 3개 이상부터는 switch문의 성능이 더 좋다
배열
// 1번
let arr1 = Array<String>()
// 2번
let arr2 = [String]()
- 2번 코드가 더 권장되는 코드
guard let, if let
- optional 값을 사용할 때 안전하게 사용할 수 있도록 해준다
var value: Int?
value = 1
// 1번
func test1() {
print("value: \(value!)")
print("value: \(value ?? 0)")
}
// 2번 guard let
func test2() {
guard let value = value else {
print("value is empty")
return
}
print(value)
}
// 3번 if let
func test3() {
if let value = value {
print(value)
} else {
print("value is empty")
}
}
- 1번 방법과 같이 사용하면 컴파일 에러가 날 수 있다.
- 코드에 !가 많으면 좋지 않다.
- guard let 을 사용할 경우 if 와 다르게 괄호 밖에서도 값을 사용할 수 있다.
_의 의미
- 값을 받아서 사용하지 않을 때 사용
guard let _ = value else {
return
}
let _ = sumNum(1,2)
- 함수 호출시 파라미터의 이름을 생략할 때 사용
// 1번
func underbarTest(name: String, age: Int) {
print("name: \(name), age: \(age)")
}
underbarTest(name: "Example", age: 10)
// 2번
func underbarTest(_ name: String, _ age: Int) {
print("name: \(name), age: \(age)")
}
underbarTest("Example", 10)
함수
- 여러개의 값을 리턴할 수 있다.
func printAvg(num1: Int, num2: Int, num3: Int) -> (total: Int, avg: Int) {
let total = num1 + num2 + num3
let avg = total/3
return (total, avg)
}
- 여러개의 인자를 받을 수 있다.
func arithmeticMean(_ numbers: Double...) -> Double {
var total: Double = 0
for number in numbers {
total += number
}
return total / Double(numbers.count)
}
- 읽는 방법
- 함수타입은 일반적으로 right-associative
- Int -> Int -> Int == Int -> (Int -> Int)
func stock(_ money: Int) -> Int {
return money - 1
}
func bitcoin(_ money: Int) -> Int {
return money - 2
}
func investment(highRisk: Bool) -> (Int) -> Int {
return highRisk ? bitcoin : stock
}
let myChoice = investment(highRisk: true)
let currentMoney = 3
print(myChoice(currentMoney))
프로퍼티 get, set, didSet, willSet
1. 기본적인 사용법
public var someValue: Int {
get {
return someValue
}
set(newValue) {
someValue = newValue
}
}
- 위와 같이 경고나 표시되는것을 확인할 수 있다.
2. 올바른 사용법
private var realValue: Int = 0
public var tempValue: Int {
get {
return realValue
}
set(newValue) {
realValue = newValue
}
}
- 프로퍼티에 값이 할당 될 때 적절한 값인지 검증하기 위해
- 다른 프로퍼티 값에 의존적인 프로퍼티를 관리할 때
- 프로퍼티를 private하게 사용하기 위해
3. 응용
private var realValue: Int = 0
public var tempValue: Int {
get {
return realValue
}
set(newValue) {
if newValue < 1 {
print("값은 1보다 작을 수 없습니다.")
} else {
realValue = newValue
}
}
}
4. didSet, willSet
- 스위프트는 프로퍼티 옵저버로 didSet, willSet을 제공한다. 값이 변경되기 직전, 직후를 감지
- 따라서 이때 원하는 작업을 수행 할 수 있습니다. 기본적인 syntax는 다음과 같다.
var someValue: Int = 10 {
didSet(oldVal) {
//someValue의 값이 변경된 직후에 호출, oldVal은 변경 전 someValue의 값
}
willSet(newVal) {
//someValue의 값이 변경되기 직전에 호출, newVal은 변경 될 새로운 값
}
}
5. 응용
var score: Int = 0 {
didSet {
scoreLabel.text = "Score: \(score)"
}
- 값이 변경되고 ui에 변경된 값을 적용할 때 사용할 수 있다.
class func vs static func
- 두개 다 똑같이 객체를 생성하지 않고 호출할 수 있다.
class MyClass {
static func test1() {
print("static func")
}
class func test2() {
print("class func")
}
}
class SubClass {
func printSomething() {
MyClass.test1()
MyClass.test2()
}
}
- 상속에서의 차이
Performance of Map, Filter, Reduce vs for-in loop in Swift
1. for 문과 map의 성능 비교
// Map
let celsius = fahrenheit.map { (degreesFahrenheit) -> Double in
return (degreesFahrenheit - 32.0) / 1.8
}
// For-in loop
var celsius = [Double]()
for degreesFahrenheit in fahrenheit {
celsius.append((degreesFahrenheit - 32.0) / 1.8)
}
- map의 성능이 더 좋은것을 알 수 있다.
2. for 문과 filter의 성능 비교
// filter
let colds = fahrenheit.filter { (degreesFahrenheit) -> Bool in
return degreesFahrenheit <= 68.0
}
// For-in loop
var colds = [Double]()
for degreesFahrenheit in fahrenheit {
if degreesFahrenheit <= 68.0 {
colds.append(degreesFahrenheit)
}
}
- filter의 성능이 좀 더 좋은것을 알 수 있다.
3. for 문과 reduce의 성능 비교
// reduce
let sum = fahrenheit.reduce(0.0) { (result, degreesFahrenheit) -> Double in
return result + degreesFahrenheit
}
// For-in loop
var sum: Double = 0.0
for degreesFahrenheit in fahrenheit {
sum += degreesFahrenheit
}
- reduce의 성능은 for문보다 좋지 않다
4. 함수 chaining (map + filter + reduce)의 성능
// map + filter + reduce
let sum = fahrenheit.map({ (degreesFahrenheit) -> Double in
return (degreesFahrenheit - 32.0) / 1.8
}).filter({ (degreesCelsius) -> Bool in
return degreesCelsius <= 20.0
}).reduce(0.0) { (result, degreesCelsius) -> Double in
return result + degreesCelsius
}
// For-in loop
var sum: Double = 0.0
for degreesFahrenheit in fahrenheit {
let degreesCelsius = (degreesFahrenheit - 32.0) / 1.8
if degreesCelsius <= 20.0 {
sum += degreesCelsius
}
}
- for 문이 성능이 더 좋다
- 이전 함수의 각 결과에 대해 반복돼서 for 문보다 성능이 좋지 않다.
- map의 시간 복잡도 = O(n), filter의 시간 복잡도 = O(n), reduce의 시간 복잡도 = O(n)이기 때문에 3개의 시간 복잡도는 O(3n)
5. RxSwift에서의 함수 Chaining 성능
Observable.from(fahrenheit)
.map({ (degreesFahrenheit) -> Double in
return (degreesFahrenheit - 32.0) / 1.8
})
.filter({ (degreesCelsius) -> Bool in
return degreesCelsius <= 20.0
})
.reduce(0.0, accumulator: ({ (result, degreesCelsius) -> Double in
return result + degreesCelsius
}))
.subscribe(onNext: { sum in
print(sum)
})
.disposed(by: disposeBag)
- 성능이 매우 좋지 않다는것을 확인할 수 있다.
Closure
1. 기본적인 Closure
{ (매개변수 목록) -> 반환타입 in
실행 코드
}
let sum: (Int, Int) -> Int = { (num1: Int, num2: Int) in
return num1 + num2
}
let sumResult: Int = sum(1, 2)
print(sumResult)
2. Trailing Closures
- Closure 표현식을 함수의 인자들 중 마지막 인자로 호출하여 넘길때 사용한다.
- 마지막 인자 라벨은 사용하지 않는다. Closure 표현식이 길 때 유용하다.
- 함수에서 여러 후행 클로저가 올때, 첫번째 인자 라벨은 생략하지만, 나머지 클로저들은 인자라벨을 남겨둔다
func loadPicture(from server: Server, completion: (Picture) -> Void, onFailure: () -> Void) {
if let picture = download("photo.jpg", from: server) {
completion(picture)
} else {
onFailure()
}
}
loadPicture(from: someServer) { picture in
someView.currentPicture = picture
} onFailure: {
print("Couldn't download the next picture.") ß
}
- 함수의 파라미터가 클로저 1개만 제공 되면 함수 뒤의 괄호를 생략할 수 있음
func someClosure(completion: () -> Void) {
print("someClosure")
}
someClosure {
// do somethins
}
3. Escaping Closures
- 클로저를 함수의 파라미터로 넣을 수 있는데, 함수가 끝나고 실행되는 클로저이다.
- 비동기로 실행되거나 completionHandler로 사용되는 클로저는 파라미터 타입 앞에 @escaping이라는 키워드를 명시해야 한다.
-
- @escaping 를 사용하는 클로저에서는 self를 명시적으로 언급해야 한다.
class SomeClass { var x = 10 func doSomething() { // 클로저 안의 self가 class의 instance를 참조하면 강한 참조 사이클을 유발할 수 있다. withEscapingClosure { self.x = 100 } // self implicity withNonescapingClosure { x = 200 } } }
- @escaping 를 사용하는 클로저에서는 self를 명시적으로 언급해야 한다.
- 위 함수에서 인자로 전달된 completionHandler는 someFunctionWithEscapingClosure 함수가 끝나고 나중에 처리 된다. 만약 함수가 끝나고 실행되는 클로저에 @escaping 키워드를 붙이지 않으면 컴파일시 오류가 발생.
UserDefault
- 로컬 데이터 베이스