2021의 게시물 표시

클로져를 이해할 때 따라 쳐 보면 좋은 예제 코드

이미지
어쩌다 보니 클로져를 공부하면서 정리하는 글이 점점 많아집니다. 그만큼 중요하고, 또 많이 사용하고 있기 때문이겠죠. 오늘은 클로져를 이해하면서 사용했던 샘플 코드를 정리해보았습니다. 함수 vs 클로져 함수를 정의하고, 그 함수를 호출하는 것은 많은 분들이 익숙할 것이라고 생각합니다. 보통 다음과 같은 코드를 보면 이해할 수 있죠. func sayHi () { print ( "Hi" ) } sayHi() // Hi 클로져는 이름 없는 함수라고 하죠? 이름이 없는 함수를 만들고 이름을 붙여서 호출 해 보겠습니다. let sayHiClosure = { print( "Hi" ) } sayHiClosure () // Hi 그러니까 잘 살펴보면 클로져 앞에 이름을 붙이고 func 키워드 까지 붙이면 함수랑 똑같이 생겨지는 군요! 매개변수, 반환타입 여기서 부터 헷갈리기 시작합니다. 위의 예제는 아무값도 입력받지 않고 반환하지 않는 아주 단순한 형태입니다. 그렇다면 숫자를 2개 입력받아서 합을 구하는 함수와 클로져를 만들고 비교해볼까요? func sum(_ a : Int, _ b : Int) -> Int { return a + b } print (sum( 3 , 6 ) ) // 9 let sumClosure: (Int, Int) -> Int = { a , b in return a + b } print (sumClosure( 2 , 5 ) ) // 7 결국 우리가 하고 있는 행위는 이름없는 클로져에 이름을 붙이고 있는 것 입니다. 그러면 이쯤에서 왜 클로져를 쓰는가에 대해서 생각해 보아야 합니다. 언제 함수? 언제 클로져? 위 예제에서 두 숫자를 더해서 합을 반환하는 함수를 위에서 만들어 보았습니다. 그렇다면 뺼샘과, 나눗셈과, 곱셈은 어떨까요? 우리의 함수를 쓸지 안쓸지도 모르는데 구현해 놓는다면 너무나 힘들 것 입니다. 그냥 (Int, In

[인터뷰질문 018] Swift 함수에서 return 키워드가 필요없는 상황은 어느때 인가요?

이미지
return 키워드 return 키워드는 함수의 반환값을 표시할 때 쓰입니다. 이 함수에서 이 값이 반환됩니다 라는 의미지요. 함수가 무엇인지 반환값이 무엇인지 모른다면 먼저 공부하고 읽으시는 것을 추천 드립니다. return값이 없을 때 함수에 반환값이 필요 없는경우 당연히 return 키워드가 필요하지 않습니다. func sayHello () -> Void { print ( "Hello" ) } sayHello() // Hello Error 상황 함수가 실행되다가 정상적인 종료가 아닌 예외처리 하는 부분에서는 return 키워드가 필요 없습니다. func devidedValue (numerator : Double, denominator: Double) -> Double { if denominator == 0 { fatalError ( "can not devided by zero" ) //print("can not devided by zero") } else { return numerator / denominator } } print (devidedValue(numerator: 3 , denominator: 6 )) print (devidedValue(numerator: 3 , denominator: 0 )) 함수에서 반환타입이 있다면 반환값을 지정해주어야 합니다. 하지만 fatalError() 구문이 있으면, return 하지 않아도 됩니다. 왜냐하면 fatalError() 을 만나는 순간 함수가 종료되기 때문입니다. 한줄로 작성된 함수의 반 func devidedValue ( numerator : Double, denominator : Double) - > Double { numerator / denominator } print (devidedValue( n

swift 에서 dictionary와 NScache의 차이와 thread safe 하다는 뜻

이미지
공부를 하다보니 NScache를 알게 되었습니다. 사실 무엇인지 잘 모르고 사용하고 있었는데, 이 걸 모르는 사람들은 Dictionary를 쓴 경험이 있다고 합니다. 그래서 무엇이 비슷했길래 사용했는지, 그리고 무엇이 다르기에 용도에 맞게 사용해야 하는지 정리를 해보겠습니다. 또한 공부하다 알게 된 thread safe 하다는 것에 대해서도 정리하겠습니다. dictionary와 NScache의 비슷한 점 var dictionaryInstance: Dictionary = Dictionary () var cacheInstance: NSCache = NSCache () 생성을 해보면 정말 비슷해서 아무 생각없이 사용한다면 쓸 수 있겠다라는 생각을 했습니다. 결국 string을 키값으로 데이터를 저장하고 탐색하는데 사용할 수 있기 때문입니다. 그러면 이렇게 비슷한데 그냥 사용하면 무엇이 안 좋은지 한 번 알아보도록 하겠습니다 dictionary와 NScache의 다른 점 크게 두가지 다른점이 있다고 생각합니다. NSCache는 디바이스의 메모리가 부족해지면 캐시된 데이터를 삭제하기도 합니다. 가장 큰 차이점 입니다 NScache는 Thread safe 합니다. 값의 입력과 출력하는 방법이 조금 다릅니다.(키값으로 접근하는 것은 같습니다.) 그렇다면 가장 큰 차이점인 Thread safe 하다 그렇지 못하다는 것은 어떤 의미일까요? Thread safe 하다는 것 해석해보면 thread에 안전하다는 뜻 입니다. 그러면 thread가 무엇인지 우리가 어떤 환경에서 개발하는지 알아야 합니다. thread란 쉽게 말해 작업의 단위라고 보면 쉽습니다. 우리는 multi-threaded 환경에서 개발을 하고 있습니다. 그러면 어려개의 작업은 각각의 thread를 만들고 공유 자원을 이용할 수 있겠죠? 좀 더 쉽게 예를 들어보겠습니다. 냉장고가 하나 있고, A와 B가 물건을 넣기도 빼기도 한다고 해봅시다. 현실에서의 냉장고는 동시에 물건을 넣거

2021 첫 iOS 멘토링을 마치며

이미지
우연히 좋은 기회가 생겨 다른 주니어 개발자들을 멘토링 할 기회가 생겼습니다. 저도 주니어라서 누가 누구에게 멘토링을 하겠냐 라는 생각도 했지만 반대로 내가 맨 처음에 랜딩하면서 걲었던 어려움과 어떻게 극복했는지를 전달 해 주어야 겠다라는 생각으로 시작하게 되었습니다. 멘토링 순서 1주차 : 로드맵 확인 / 개발 성장 과정 조언(HIG, 학습방법 등) 2주차 : TableView(1) - facebookMe / 함수명, nib, MVC,MVVM, Life Cycle이해하기 3주차 : TableView(2) - MVC, MVVM 활용해서 만들어오기 4주차 : 공식문서에 대해서 ( 함수명 짓는 법, 뜯어보는 개발팁 ), swift를 문법의 실질적인 중요성을 github들을 뜯어보면서 확인 ( 접근제어자,enum,옵셔널) / enum 의 중요성 다시 한 번! 5주차 : 코딩쇼! ㅎㅎ (아이폰 알람앱 벤치마킹) / 앱 스토어 살펴보면서 어플 출시까지 맛보기 6주차 : ScrollView / 이 날은 다들 백신으로 아프셔서 쉬어가는 타임! 7주차 : CollectionView / 디버깅 방법 (break활용, 뷰 레이어 구조 보기 활용하면서) 8주차 : 네트워크에 대해 이해하기 / URLSession이해하기 & 클로저 이해하기 9주차 : Api활용 / debugDescription / 모델의 중요성, 목데이터 등 10주차 : 애니메이션(1) 11주차 : 애니메이션(2) 뜯어보면서 현호님의 공부 방법 공유 / 라이브러리 활용해보기 12주차 : 다른 사람들의 소스코드 뜯어보기 / 앞으로 어디로 가야하는가 기본적인 철학 어차피 내가 할 줄 아는 것을 알려줘 봤자 새로운 것이 나오면 또 문제를 해결하지 못할 것 이다. 그러므로 스스로 문제를 푸는 방법을 알려주자 였습니다. 그래서 우리가 마주한 문제가 무엇인지, 이 문제를 스스로 해결하는 방법이 어떤 것인지 멘토링을 진행했습니다. 진행 기본적으로는 과제를 내주고, 그 과제를

ARC와 weak, strong, memory leak의 관계에 대한 정리

이미지
면접 질문에도 자주 등장하고, 스스로도 코딩을 하면서 자주 놓치게되는 부분이기에 실제 어떻게 사용해야하는지 되새길 겸 정리하려고 합니다. 먼저 단어들에대서 설명하고, 어떤 문제가 생길 수 있기 때문에 알고있어야 하는지 그리고 어떻게 해결할 수 있는지에 대해 순서대로 정리하도록 하겠습니다. ARC(Automatic Reference Counting) 이름에서 알 수 있듯이 자동으로 참조하고있는 숫자를 세어주는 녀석입니다. 그렇다면 참조숫자는 왜 필요할까요? 메모리1 - [객체1| 객체2 | 객체3 | 객체1을바라봄 | 객체1을바라봄 | 객체2를바라봄 | 객체3을바라봄 ||| 빈공간들 || ] 위와 같이 메모리를 사용하고 있는데 객체1이 메모리에서 사라지면 어떻게 될까요? 참조하러 갔는데 비어있으면 크래시가 나겠죠? 아니면 예상하지 못한 동작을 할 것 입니다. 그러므로 지금 메모리에 올라가 있는 이 녀석을 지워도 되는지 말아야 하는지에 대한 기준을 바로 ARC로 잡는 것입니다. ARC가 0이라면? 아무도 참조하고 있지 않으니 메모리에서 해제해도 아무런 이상이 없을 것 입니다. 반대로 0이 아니라면 어디선가, 누군가인지는 모르지만 사용하고 있으니 해제하면 안된다는 뜻으로 알고 메모리에 남겨둡니다. 개인적으로는 지하주차장 천장에 있는 녹색등과 초록등으로 이 아래에 차가 있는지 없는지 알려주는 장치와 비슷하다고 생각했습니다. 이 장치가 완벽하게 동작한다면 주차가 되어있다고 빨간불이 들어와있는 구역에 주차하러 왔다가 낭패를 보는 일은 없을테니까요. ㅎ ㅎ 메모리에서 해제해도 돼, 안돼! 그러면 ARC를 증가시키는 방법을 간단하게 살펴보겠습니다. import Foundation class Student { let name: String let grade: Int init (name: String , grade: Int ) { self .name = name self .grade = grade

정의된 프로콜을 보고도 감이 잘 안오는 분들을 위한 글

이미지
디자인 패턴을 공부하다 보니 protocol까지 흘러들어갔습니다. 문법책에있는 내용은 정말 빌드 시 에러가 나지 않을 만큼만 이라서 이 프로토콜을 정리 할 필요가 있다고 생각되었습니다. protocol 정의 protocol Student { var name: String { get } var grade: Int ? { get } } struct Leeo : Student { let name: String let grade: Int ? } let leeo = Leeo (name: "hyunho" , grade: nil ) print (leeo) 위와 같이 protocol이라는 키워드를 사용해서 프로토콜을 정의 해줍니다. 그리고 정의한 프로토콜을 채택해서 구현 해주어야 합니다. 프로토콜에는 크게 프로퍼티와 메소드를 정의 해줄 수 있습니다. 그러면 하나씩 어떻게 정의할 수 있는지 살펴보겠습니다. 프로퍼티 정의 프로토콜로 프로퍼티를 정의할 때는 크게 두가지가 신경 쓰입니다. let, var 의 정의 get set의 정의 먼저 let, var를 짚고 넘어가겠습니다. 프로토콜에는 let으로 정의할 수 없습니다. var로 정의해 놓은 변수를 let으로 구현할지 var로 구현할지는 구현하는 사람에게 달렸습니다. protocol Student { var name: String { get } var grade: Int ? { get } } struct Leeo : Student { let name: String let grade: Int ? } struct Vabat : Student { var name: String var grade: Int ? } let leeo = Leeo (name: "hyunho" , grade: nil ) let vabat = Vabat (name: "

adiOS 앱을 만들었습니다!

클로져(closures)에서 좌절한 사람을 위한 글

이미지
함수라고 알고 있던 클로져를 공부하면서 기록합니다. 어느 순간 이해가 안되는 코드들이 클로져로 이루어져있다는 것을 알게된다음 정리와 적용과정이 필요하다는 것을 알고 정리해 놓습니다. 클로저란? 클로저 는 코드에 전달되어 사용할 수있는 독립적인 기능 블록입니다. Swift의 클로저는 C 및 Objective-C의 블록 및 다른 프로그래밍 언어의 람다와 유사합니다. 라는 정의가 공식문서에 적혀있습니다. 하지만 이 개념은 이해가 가지 않았고, 개인적으로는 이름이 없는 함수이고 축약을 위한 다른 규칙들이 있다라고 이해하고 있습니다. 실제 저는 아래의 단계에 따라서 클로져를 공부하고 있더라구요. 비슷하거나 아니면 이에 해당하시는 분들은 미래를 예측 해 보시기 바랍니다. swift 문법을 공부합니다. 그리고 한 단원은 클로져입니다. 그래서 공부했습니다. 나는 클로져를 알고 있는데, 고차 함수인, map, fitler, reduce를 잘 몰라서 공부하다 보니까 클로져까지 왔습니다. 위에까지 다 공부해서 클로져를 알고 있다고 생각했는데 @escaping completeHandler: (() -> ())? 를 만나서 공부하다 보니 클로져까지 다시 오게 되었습니다. 이 전 단계를 다 공부하고 왔는데 아래와 같은 request { (result) in completed(.success( data )) } 코드를 보고 막혔다가 공부하러 왔습니다. 한 2개월에 한 단계씩 올라간 것 같습니다. 물론 순서대로는 아니지만 생각보다 한번의 예제나 하나의 실습으로 익혀지지 않는 것은 확실한 것 같네요. 클로저의 문법 간단한 함수의 생김새와 사용방법 let name = "Leeo" func printName () { print (name) } printName() // Leeo 간단한 클로져의 생김새 let name = "Leeo" let print Name = { print (name) }

모달이 사라진 뒤 테이블 뷰 업데이트 하는 방법

이미지
iOS 개발을 하다보면, 내가 만든 UI에게 지금 일어나고 있는 일을 전달하고 싶을 때가 있습니다. 이 때 내가 원하는 기능을 동작하게 하는 방법은 다양합니다. 내가 하고 있는 일이 어떤 것인지, 정리 해보도록 하겠습니다. 크게 3가지 방법이 있습니다. Notification , Delegate , closure . 이 것들에 대한 비교는 더 자세하고 좋은 글들이 많으니 실제 사용 측면에서 보면 좋을 것 같습니다. 실제 환경 우리는 아래와 같은 코드가 있다고 합시다. 제가 가장 고민없이 만들 수 있는 테이블 뷰 입니다. :) 이해하는데 어렵지는 않죠? add 버튼을 누르고 nameTextField에 이름을 입력하고, 버튼을 누르면 리스트에 추가되는 구조 입니다. // ViewController.swift import UIKit var items: [ String ] = [ "Leeo" , "Hyunho" , "Vabat" ] class ViewController : UIViewController { @IBOutlet weak var tableView: UITableView ! override func viewDidLoad () { super .viewDidLoad() tableView.delegate = self tableView.dataSource = self } @IBAction func didTapAddButton ( _ sender: Any) { let vc = (storyboard?.instantiateViewController(identifier: "AddViewController" )) as ! AddViewController vc.parentsViewController = self present(vc, ani

URLSession 공부의 시작점

이미지
iOS 개발 뿐 아니라 많은 서비스 들을 개발하다 보면 네트워크를 다루게 됩니다. 네트워크를 처음 접한 건 라이브러리를 사용해서 입니다. 그런데 구현체가 어떻게 생겼는지 모르고, 네트워크 통신 규약 문서를 볼 수 없는 상태로 사용만 하는 것은 문제가 생겼을 때 해결할 수 없었습니다. 그래서 moya 나 alamofire 과 같은 라이브러리들이 어떻게 구현되어 있는지 그리고 URLSession 은 어떻게 이루어져 있는지 정리하려고 합니다. 동기 비동기 네트워크를 공부하기 전에 동기, 비동기를 먼저 공부하는 걸 추천 드립니다. 왜냐하면 네트워크는 여러가지 요청과 응답이 처리되어야 하는데, 이 작업들이 동기로 처리되어야 하는지 비 동기로 처리되어야 하는지를 판단하거나 이해하지 못하면 전반적인 내용에 대해서 이해하기 어렵습니다. 해당 내용은 여기 에 정리한 적이 있습니다. 네트워크 프로세스 가장 많이 만나는 코드 중 하나를 가지고 예를 들어 보도록 하겠습니다 let urlSession = URLSession(configuration: . default ) // 1. let task = urlSession.dataTask( with : url) { data, response, error in // 2. print(response ?? "no response" ) } task.resume() // 3. URLSession 클래스를 이용해 세션을 하나 만듭니다. 그 세션은 여러가지 일을 할 수 있는데, URLSession에 구현되어있는 메소드로 어떤 일(task)을 추가할 수 있습니다. 그리고 그 일을 하는 task 객체를 반환 합니다. task는 resume() 으로 실행합니다. 그럼 순서대로 알아보도록 하겠습니다. 세션 만들기 URLSession 은 세션을 만든다고 언급했습니다. 그러면 어떻게 만들 수 있을까요? 코드를 살펴보면 init(configuration: URLSessionConfiguration)

[인터뷰질문 017] protocols을 주니어 개발자에게 어떻게 설명해 줄 것 인가요?

이미지
프로토콜이란? 프로토콜은 인터페이스 입니다. swift에서는 특정 기능을 정의합니다. 실제 구현은 하지 않고 정의만 합니다. 마치 설계도와 같죠. 간단한 예제 간단한 예제를 보도록 하겠습니다. 자동차의 기능을 구현하기에 앞서 일단 정의하려고 하는데요. 자동차가 주행에 필요한 것들을 나열해 봅시다. 간단하게 생각해서 출발과, 정지를 할 수 있으면 자동차가 주행할 수 있겠죠? 앞에 나오는 조건들이 주행에 필요한 요소들이라고 했으니 이 조건들을 모두 갖추면, 주행이 가능하다고 할 수 있습니다. 그럼 한번 설계 해 볼까요? protocol Driveable { func start () func stop () } 이렇게 설계 해 놓으면 이 프로토콜을 채택하면, 아 주행가능하구나 라고 생각하면됩니다. 아래와 같이 구현되겠죠? struct Sonata : Driveable { func start () { print ( "주행 시작합니다." ) } func stop () { print ( "주행 종료합니다." ) } } 사실 위에서 Driveable 프로토콜을 채택하지 않아도 돌아가는데는 문제가 없습니다. 하지만 다른 곳에서 사용할 때 프로토콜을 채택하고 있지 않으면 주행이 가능한건지에 대한 보장이 되지않겠죠? 그렇기 때문에 설계와 기능의 보장을 위해 프로토콜을 정의해서 채택해서 써야합니다. 여러 프로토콜 하나의 프토콜이 한 종료의 기능을 보장해준다면, 여러개의 기능은 아래와 같이 여러개의 프로토콜을 채택해서 구현할 수 있을 것 입니다. 거꾸로 이 객체가 어떤 기능을 하는지에 대한 것들은 프로토콜을 살펴보는 것 만으로도 가능 합니다. protocol Driveable { func start () func stop () } protocol Openable { func open () f

[인터뷰질문 016] operator overloading을 주니어 개발자에게 어떻게 설명해 줄 것 인가요?

이미지
오버라이딩 이 문제를 이해하기 위해서는 오버라이딩이라는 개념을 알고 있어야 합니다. 오버라이딩은 쉽게 말해 같은 이름의 다른 타입의 함수를 정의하는 것 입니다. 두개의 인자를 받아서 더하는 함수를 정의해 보겠습니다. func sum(_ a : Int, _ b : Int) -> Int { return a + b } func sum(_ a : Double, _ b : Double) -> Double { return a + b } print (sum( 3 , 2 ) ) // 5 print (sum( 4.1 , 4.2 ) ) // 8.3 두 숫자의 합을 구하고 싶을 때, 매개별수로 정수와 실수가 들어가야 한다면, 이렇게 한 함수를 오버라이딩해서 사용하는 사람이 편하게 쓸 수 있도록 구현할 수 있습니다. 연산자 오버라이딩 이름에서 알 수 있듯이, 연산자를 오버라이딩 하는 것 입니다. 우리가 알고있는 연산자는 다음과 같습니다. + , - , / , * 등등... 그렇다면, 언제 연산자를 오버라이딩 할까요? 아래 예제를 보면서 생각해 보겠습니다. struct Point { var x = 0.0 var y = 0.0 } let p1 = Point(x: 12.3 , y: 34.5 ) let p2 = Point(x: 67.2 , y: 89.6 ) 두개의 좌표가 있을 때, 좌표의 합을 구하고 싶다면, 어떻게 해야할까요? 쉽게드는 생각은 p1 + p2 하면 x는 x끼리 y는 y끼리 더해주었으면 좋겠는데, 실제 돌려보면 binary operator '+' cannot be applied to two 'Point' operands print(p1 + p2) 와 같은 에러가 발생합니다. 왜냐하면 + 연산자는 좌표를 더하는 것에 대한 처리가 되어있지 않기 때문입니다. 그렇다면? 위에서 봤던 예제처럼 연산자를 오버라이딩 하면 문제를 해결할 수 있을것 같습니다. struct Poi

[인터뷰질문 015] 두 개의 튜플의 값이 동일한지 어떻게 비교하시겠습니까?

이미지
튜플의 비교 튜플은 이름없는 구조체 라고도 하죠. 튜플이 같은지 비교하는 방법은 간단합니다 == 기호를 써줍니다. 여러 예제를 통해 비교해 보도록 하겠습니다. let temp1 = ( 2 , 4 ) let temp2 = ( 2 , 4 ) let temp3 = ( 3 , 4 ) let temp4 = ( top : 2 ,bottom: 4 ) print (temp1 == temp1) // true print (temp1 == temp2) // true print (temp1 == temp3) // false print (temp1 == temp4) // true 당연히 자기 자신과 비교 하면, 같다 라는 결과가 나옵니다. 값을 비교하는 연산자 이기 때문이죠. 그리고 다른 변수에 할당 하더라도 값이 같으면 true 의 결과값이 나옵니다. 그리고 (2,4)와 (3,4)는 같지 않기 때문에 false 가 출력되는 것을 볼 수 있습니다. 또한 tuple은 element의 이름을 지정할 수 있는데, 두 튜플이 같다고 해서 elemene의 이름까지 같음을 보장하지는 않습니다. 그러므로 temp4와 temp1이 다른 element 이름을 가지고 있어도, 같은 튜플로 인식합니다.

[인터뷰질문 013] Result 타입은 언제 사용할 수 있나요

이미지
Result 타입은 Swift5에 추가되었습니다. 그렇다면 왜 추가되어야 했는지 Result 타입이 없었을 때는 어떤 문제가 있는지 알아보면서 Result에 고마움에 대해 알아보도록 하겠습니다. Result가 없는 세상 Result 타입은 네트워크 요청 쪽에서 많이 사용되었습니다. 예를 들면 아래와 같은 상황에서 말이죠. import Foundation func getData <T: Decodable> (urlString: String, completion: @escaping (T?) -> Void ) { let url = URL (string: urlString)! URLSession .shared.dataTask(with: url) { data, response, error in if let error = error { return completion( nil ) } guard let data = data else { return completion( nil ) } guard let response = response as ? HTTPURLResponse , response.statusCode == 200 else { return completion( nil ) } guard let parsedData = try ? JSONDecoder ().decode( T . self , from: data) else { return completion( nil ) } completion(parsedData) }.resume() } URLSession.shared.dataTask 에서 요청한 응답 중 에러를 처리하려면, 정상적인 케이스와 정상적이지 않은 케이스들을 구