라벨이 Swift인 게시물 표시

[swift] Xcode에서 Unit Test 연습하기

이미지
[swift] Xcode에서 Unit Test 연습하기 진행하고 있는 프로젝트를 리팩토링 하면서, 내가 한 리팩토링이 잘 되었는지는 둘째치고 이전에 기능들은 잘 되고 있는지에 대한 불안감이 생겼다. 원래 되고있던 기능이 뭐지 제대로 되었다는것이 어떤 것을 의미하지 테스트 코드의 작성을 통해 위 두 문제를 해결할 수 있다고 해서 정리하기 시작했다. 리소스 다운 원문 번역본 모든 내용을 다 정리하지도 해 보지도 않았다 앞으로 필요한 테스트를 위한 기본 몸풀기 정도만 해 보았다. 추후에 해당 아티클들의 뒷 부분이 필요한 때에 다시 해 보고 정리하기로 한다. 테스트를 위한 네비게이션이 있었다. 뭐하는데 쓰는 것일까 궁금했더 곳이다. Command(⌘)+6 으로 창을 열 수 있다. 왼쪽 아래의 + 버튼을 눌러 새로운 유닛테스트를 위해 New Unit Test Target… 을 눌러 생성한다. 처음에 만들면 4개의 메소드가 있다. setUpWithError() : 테스트를 위해 설정 tearDownWithError() : 테스트가 종료 된 후 삭제 testExample() -> 삭제 testPerformanceExample() -> 삭제 BullsEye 우리는 50으로 부터 랜덤으로 값을 생성하고, 슬라이더를 움직여 목표 값과 비슷한 값을 입력하는 게임을 테스트 하려한다. 프로젝트 설정 1 . @testable import BullsEye 를 입력해 테스트 코드 파일의 최상단에 프로젝트를 임포트 해주자. 2 . var sut: BullsEyeGame! 프로퍼티를 class BullsEyeGame 타입으로 만들어준다. setUpWithError super.setUp() sut = BullsEyeGame() sut.startNewGame() 인스턴스를 생성하고, startNewGame까지 실행한 상태로 설정한다. tearDownWithError sut = nil s

[swift] fatalerror에 대해 처음 알아보았다

이미지
[swift] fatalerror 코드를 리팩토링 하다 에러문구를 만났고, Fix를 누르니 다음과 같은 코드가 생겼다. 이미 init 함수는 있는데 무슨 용도일까 해서 찾아 보았다. required init?(coder aDecoder: NSCoder) { fatalError("init(coder:) has not been implemented") } override init(frame: CGRect) 은 view를 만들 때 사용된다. required init?(coder: NSCoder) 스토리 보드에 생성될 때 사용된다. fatalError는 언제 무슨 용도로 쓰는지 알아보자. 설명 은 다음과 같았다. Unconditionally prints a given message and stops execution. 무조건 출력하고 실행을 멈춘다? 정의는 다음과 같다. func fatalError(_ message: @autoclosure () -> String = String(), file: StaticString = #file, line: UInt = #line) -> Never Never을 반환한다. 이 의미는 호출이 되면 더 이상 앱이 진행되지 않는다. 즉 개발자가 인지 하지 못하는 상황에서 예외처리를 이 함수로 한다면, 실행 되었을 때 문제가 있는 부분을 알 수 있다. 다른 예를 하나 더 들어보자. 테이블 뷰를 쓸 때 자주 구현하는 메소드이다. override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell { guard let cell = tableView.dequeueReusableCell(withIdentifier: "Cell", for: indexPath) as? MyCustomCell else {

[dailycoding] 위장

이미지
[dailycoding] 위장 문제 clothes의 각 행은 [의상의 이름, 의상의 종류]로 이루어져 있습니다. 스파이가 가진 의상의 수는 1개 이상 30개 이하입니다. 같은 이름을 가진 의상은 존재하지 않습니다. clothes의 모든 원소는 문자열로 이루어져 있습니다. 모든 문자열의 길이는 1 이상 20 이하인 자연수이고 알파벳 소문자 또는 ‘_’ 로만 이루어져 있습니다. 스파이는 하루에 최소 한 개의 의상은 입습니다. 입출력 예 clothes return [[yellow_hat, headgear], [blue_sunglasses, eyewear], [green_turban, headgear]] 5 [[crow_mask, face], [blue_sunglasses, face], [smoky_makeup, face]] 3 해결의 과정 문제는 단순히 수학문제에서 마주할 수 있는 조합의 문제이다. 옷의 종류가 a,b,c가 있다면 a b c가지가 있다. 하지만 옷을 입지 않은 가짓수도 있기 때문에 (a+1)(b+1)(c+1) 가지의 조합이 나온다. 마지막으로 하나도 입지 않은 가짓수는 제외해야하기 때문에 (a+1)(b+1)(c+1) - 1 을 계산한다. 입력 받은 옷 중에 같은 이름은 없기 때문에 결국 옷 종류별로 몇개씩 있는지만 고려하면된다. 옷 종류별로 몇벌씩 가지고있는지 계산한다. 종류별 옷을 전부 +1 한뒤 곱한다. 마지막에 -1을 해준다. import Foundation func solution(_ clothes:[[String]]) -> Int { var clothType : [String : Int] = [:] var answer = 1 clothes.forEach {item in // 옷 종류별 갯수를 계산한다. if clothType.keys.contains(item[1]){

[swift] table view를 처음 써보고 남기는 글

이미지
[swift] table view basic TableView Main.storyboard에서 Library(⌘ + ⇧ + L)를 열어서 Table view controller를 추가한다. 처음에 만들면, 진입 포인트가 없기 때문에 Attribute inspector에서 is initial view controller 를 체크해 최초 진입 포인트로 지정해준다. table view cell은 만들어 놓고 재 사용할 수 있다. cell은 identifier를 이용해 식별 할 수 있다. identifier에 이름을 줘보자. 이제 테이블을 구현해보자. 먼저 테이블의 줄 수를 정한다. // 1000 줄짜리 테이블을 그려준다. override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int { return 1000 } 빈칸이 무수한 테이블이 생겼다. 이제 내용을 채워주자 // 테이블의 셀에 내용을 채워주지 않아 아무것도 그려지지 않는다. // Attribute inspector의 Accessory에서 checkmark를 선택해준다. override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell { let cell = tableView.dequeueReusableCell(withIdentifier: "ChecklistItem", for: indexPath) return cell } indexPath는 행을 식별하기 위한 식별자이다. [section,row]의 형태로 이루어져있다. section은 table을 나누는 기준 중 하나이다. row는 의미 그대로 테이블의 행이다. // 특정 조건을 줘서 출력해보자. override fun

[swift] 메모리 관리 - Retain Cycle : strong, weak, unowned

이미지
[swift] Retain Cycle : strong, weak, unowned strong, weak, unowned 사용 방법 클래스의 객체를 가리키는 참조의 기본값은 강한 참조이다. 그리고 적어도 하나의 강한 참조가 있다면 객체의 메모리는 ARC(Automatic reference counting)에 의해서 해제되지 않는다. 아래 예제를 통해 살펴보자 // Case1 : 일반 참조와 해제 class TestClass { init(){ print("init") } deinit{ print("deinit") } } var testClass: TestClass? = TestClass() print("Instance is created!") testClass = nil // print init Instance is created! deinit TestClass 객체가 생성되면서 init 을 출력한다. 그 다음에 "Instance is created!" 를 출력한다. testClass 변수에 nil을 할당하면서 참조의 갯수가 0이 되고 deinit 을 출력한 뒤 메모리에서 해제된다. 다음 예제를 통해 ARC가 동작하지 않아 메모리에서 해제되지 않는 상황을 살펴보자. // Case 2 : 강한 상호참조 class TestClass{ var testClass: TestClass? = nil init(){ print("init") } deinit{ print("deinit") } } var testClass1: TestClass? = TestClass() var testClass2: TestClass? = TestClass() testClass1?.testClass = testClass2

[swift] 구조체(struct)와 클래스(class)의 비교

이미지
[swift] 구조체(struct)와 클래스(class) 구조체와 클래스는 코드블럭을 만들 때 쓰이고 그 문법 또한 매우 닮았다. 하지만 다른점이 있기 때문에 그 특성을 잘 파악하고, 필요한 부분에서 활용할 수 있도록 정리한다. 정의 방법 (Definition) 구조체는 struct , 클래스는 class 라는 키워드를 사용하여 정의한다. struct Student{ // properties and methods } class Student { // properties and methods } 프로퍼티와 메소드 (Properties and Methods) 이 예제에서는 학생의 구조체와 클래스의 정의 예제이다. SomeStudent struct 와 AnotherStudent class 모두 firstName, lastName, grade라는 properties를 가지고 grade를 출력해주는 printGrade() 메소드를 가진다. 다른 점은 클래스에서는 init() 함수를 따로 정의 해줘야 한다. struct SomeStudent{ // properties let firstName: String let lastName: String var grade: Int // methods func printGrade(){ print("grade is \(grade)") } } class AnotherStudent { // properties let firstName: String let lastName: String var grade: Int // methods init(firstName: String, lastName: String, grade: Int){ self.firstName = firstName self.lastName = lastName self.grade = grade } func printGrade

[swift] 함수(fuction)의 기본 개념정리

이미지
[swift] 함수(fuction) 정리 같은 코드를 복사 붙여넣기 하면서 쓰지 않기 위해 함수를 사용한다. 함수의 기능에 대해 정리하면서 남긴 기록. 정의하는 방법 사용법은 간단하다 func 키워드 뒤에 함수 이름과 ()를 붙여주면된다. func printHello(){ print("hello") } 사용하는 방법 간단하다 함수이름과 빈()를 붙여주면 된다. printHello() // hello 출력 인자 넘겨주기 ()사이에 이름과 타입을 명시해주면 된다. let speedLimit = 100 let mySpeed = 80 func printSpeedViolationStatus(speed: Int){ print(speed > speedLimit ? 'Speed violation!' : 'Keep driving.') } printSpeedViolationStatus(speed: mySpeed) // Keep driving. 여러개의 인자를 넘겨주는 방법은 , 로 추가해서 넘겨준다 let speedLimit = 80 let mySpeed = 100 func printSpeedViolationStatus(speed: Int, speedLimit: Int){ print(speed > speedLimit ? 'Speed violation!' : 'Keep driving.') } printSpeedViolationStatus(speed: mySpeed, speedLimit: speedLimit) // Speed violation! 인자 기본 값(Default Parameter Values) 기본값을 넣을 수 있다. speedLimit: Int = lowLimit 와 같이 작성한다. let mySpeed = 100 let lowLimit = 80 func printSpeedViolationStatus

[swift] 오버로딩(overloading)과 오버라이딩(overriding)

이미지
오버로딩(overloading)과 오버라이딩(overriding) 오버로딩과 오버라이딩 오버로딩과 오버라이딩에 대해 정리해 놓는다. 오버로딩(overloading) 오버로딩이란 같은 이름의 메소드에 매개변수는 다르게 선언 할 수 있는 것을 의미한다. class Student { var name: String var age: Int? init(name: String, age: Int?) { self.name = name self.age = age } func printInfo(){ print("my name is \(name)") } func printInfo(age: Int){ print("my name is \(name) my age is \(age)") } } var hyunho = Student(name: "hyunho", age: nil) var jihye = Student(name: "jihye", age: 1) hyunho.printInfo() jihye.printInfo(age: jihye.age!) // my name is hyunho // my name is jihye my age is 17 위의 코드에서 이름은 필수지만 나이는 옵션이다. 이 때 자기소개 출력 함수의 경우 이름만 있을 경우와 이름과 나이가 있는경우에 다른 결과가 출력 되어야 하기 때문에 오버로딩을 통해 다른 결과를 출력했다. 오버라이딩(overriding) 상위 클래스에서 선언한 메서드를 하위 클래스에서 재정의해서 사용하는 것. class Student { var name: String var age: Int? init(name: String, age: Int?) { self.n

[swift] Optional값 다루기

이미지
[swift] Optional값 다루기 변수의 타입 뒤에 ? 이 붙어있는 데이터를 사용하다가 자꾸 에러가 떠서 검색해보니 Optional이다. Optional에 대해 이번기회에 정리하려 한다. Optional의 구현 Optional은 enum으로 구현되어있다. enum Optional<T> { case none case some(<T>) } optional의 타입은 generic이다. 그러므로 Int, String 등 모든 타입이 들어갈 수 있다. Optional에는 두 종류가 있는데, 하나는 none이고, 나머지 하나는 some(<타입>) 이다. none은 아직 타입이 설정되지 않은 경우이고, some은 선언 된 타입의 값을 가지고 있다. Optional은 enum으로 구현되어 있지만 enum으로 사용하지 않는 이유는 Optional을 사용할 때마다 switch문을 사용하는 방법이 번거롭기 때문이다. switch문을 사용하면 항상 같은 none과 some에 대한 경우를 구현해야하니 코드가 길어질 것이다. 그래서 ?, !, ?? 등으로 Optional을 사용할 수 있게 만든것이다. 아래 예를 들어 Optional의 특징에 대해 설명한다. 거주민의 차 현황에 대해 입력하는 아래와 같은 코드가 있다. var residentName: String = "John" var age: Int = "23" var carNumber: String = "1422" residentName과 age는 모든 사람이 가지고 있지만 carNumber는 없을 수도 있다. 빈칸을 입려할 수 있지만. 값이 없는 상태를 나타내는 nil값을 넣는 방법도 있다. Optional 사용방법 변수의 타입 뒤에 ? 를 붙여준다. 그러면 해당 타입의 데이터를 입력할 수 있고, 아무값도 아닌 nil값을 입력할 수 있다. var carNumber: St

[swift] enum 타입의 사용법 정리

이미지
[swift] enum 타입의 사용법 정리 Enumerations “열거” 임의의 관계를 맺는 값들을 하나의 타입으로 묶은 타입이다. 사용법 enum Fruits { case banana case apple case melon case tomato } 타입지정 각각의 사례항목의 값을 지정하려면 타입을 지정하면 된다. enum Fruits: String { case banana case apple case melon case tomato } // Fruits.banana.rawValue == "banana" 구분 Enum 이름이 다르기 때문에 같은 이름과 타입을 가졌어도 구분할 수 있다. enum SomeEnums: Int{ case one, two, three, four } enum AnotherEnums: Int { case one, two, three, four } var a: SomeEnums = .one // 타입이 분명하므로 헷갈릴 염려가 없다. 케이스 매칭 enum의 항목의 값을 구분해서 쓸 때는 주로 switch case문을 사용한다. let myFavorite: Fruits = .banana switch myFavorite { case .banana print(.banana) case .apple print(.apple) case .melon print(.melon) case .tomato print(.tomato) } 연관값 연관값은 사례항목마다 각 타입값을 다르게 할 수 있다. 바코드를 정의하는 방법은 2가지가 있을 수 있다. 1차원과 2차원. enum을 사용하면, 이렇게 2종류의 타입을 정의 할 수 있다. enum Barcode { case upc(Int, Int, Int, Int) case qrCode(String) } var productB