** Objective-C의 Selector
=> Swift는 함수형 프로그래밍언어이고 함수가 일급 객체라서 함수의 참조를 변수에 바로 대입할 수 있고 리턴할 수 있다.
=> Objective-C는 함수가 일급 개겣가 아니라서 함수 단위의 대입이나 리턴을 할 수 없다.
함수 단위로 작업할수 있도록 Selector라는 개념을 도입
애플은 메소드나 함수를 Selector라고 표현한다.
=> iOS 프로그래밍에서 매개변수 중 Selector는 함수나 메소드를 요청하는 것이다.
=> swift에서 Objective-C의 selector로 사용할 수 있는 메소드를 만들 때는 func앞에 @objc를 붙여야 한다.
import UIKit
class OOP{
@objc func attack() -> Void {
print("공격")
}
}
//Timer 클래스 : 일정 시간마다 메소드를 호출
//Timer.scheduleedTimer(timeInterval:주기, target:타이머의 부모가 될 객체, selector:수행할 메소드, userInfo:Dictionary
//userInfo - 부가정보, parameter의 역할
//target 이 될 객체 생성
var oop:OOP = OOP()
var timer:Timer = Timer.scheduledTimer(timeInterval: 5, target: oop, selector: #selector(OOP.attack), userInfo: nil, repeats: true)
** 생성자와 소멸자
=> Constructor : 인스턴스를 생성할 때 호출하는 메소드
인스턴스가 생성될 때 가장 먼저 호출되는 메소드
생성자가 하는 일은 allocation(메모리 할당) 과 initialize(초기화 작업)
Objective-C는 생성자를 호출할 때 alloc + init
모든 객체 지향 언어에서는 클래스를 만들면 매개변수가 없는 생성자를 제공한다.
이 생성자는 개발자가 생성자를 만들면 자동으로 소멸된다.
생성자의 첫번째 줄에는 우리 눈에는 보이지 않지만 상위 클래스의 생성자를 호출하는 코드가 포함되어 있다. 이 때 호출하는 생성자는 매개변수가 없는 생성자이다.
생성자를 만드는 목적은 초기화 작업을 위해서이다.
=> Destructor : 인스턴스가 파괴 될 때 호출되는 메소드
매개변수를 가질 수 없으며 Overloading도 안된다.
사용을 한 외부 자원에 대한 release 작업을 수행한다.
=> swift에서는 생성자를 init이라고 정의
소멸자는 deinit
=> swift의 메모리 정리
swift, Objective-C, kotlin 와 python은 참조 카운팅이라는 개념을 이용해서 메모리 정리
처음 메모리 할당을 받으면 참조 카운트는 1로 설정
이 메모리 역역을 다른 변수가 참조하면 참조 카운트가 1 증가
변수가 소멸되거나 nil을 대입하면 참조 카운트가 1감소
참조카운트가 0이 되면 Garvage Collection이 메모리를 회수
게임이나 오랜시간 동안 사용하는 애플리케이션은 메모리 정리가 중요하다.
**Swift의 상속
1. 상속방법
class 클래스이름:상위클래스이름{
=> 상위클래스의 멤버를 사용 할 수 있다.
}
2. overriding (메소드 재정의)
=> 상위 클래스의 메소드를 하위 클래스에서 다시 정의 하는 것
=> 목적은 상위 클래스에서 제공해주는 메소드의 기능이 부족해서 기능 확장을 위해서 수행
상위 클래스의 메소드를 호출하고 필요한 기능을 추가로 정의
=> swift에서도 overridung을 할 때 반드시 func 앞에 override를 붙여야 한다.
=> 프로퍼티도 오버라이딩 가능
=> init의 오버라이딩(생성자 오버라이딩) : 상위 클래스에 매개변수가 없는 생성자(default Constructor)가 없는 경우 - View를 커스터마이징 할 때 필요하다.
class Customer{
var num:Int!
var name:String!
func disp() -> Void{
print("\(num):\(name)")
}
}
class Individual : Customer{
var jumin:String!
override func disp() -> Void{
//상위 클래스 메소드 호출
super.disp()
//필요한 코드를 추가
print(jumin)
}
}
class Enterprise : Customer{
var code:String!
}
var individual : Individual = Individual()
individual.num = 10
individual.name = "척준경"
individual.jumin = "101010"
individual.disp()
3. 상속관계에서의 대입
=> 상위 클래스 타입으로 선언된 변수에 하위 클래스 타입으로 만들어진 인스턴스의 참조를 바로 대입 할 수 있다.
=> 하위 클래스 타입으로 선언된 변수에 상위 클래스 타입으로 만들어진 변수의 참조는 강제 현변환을 해야만 대입이 가능하다
이 때 책임은 개발자가 져야 한다.
상위 클래스 타입의 변수에 저장된 인스턴스의 참조가 하위 클래스 타입으로 만들어진 경우만 가능하다
4. 인스턴스 참조 변수의 메소드 호출
=> 인스턴스 참조 변수는 기본적으로는 선언되었을 때 사용한 클래스의 멤버만 호출할 수 있는데 예외적으로 오버라이딩 된 메소드는 선언되었을 때 사용한 클래스 타입이 아니고 대입된 인스턴스의 자료형의 메소드를 호출한다.
** enum
=> 나열형 상수
=> 여러 개의 상수를 하나로 묶어 둔 것
=> 옵션(선택 제한)을 만들 때 사용
enum 이름 : 자료형{
case 항목이름 = 값,
case 항목이름 = 값...
}
=> 사용할 때는 이름.항목이름 으로 사용
=> iOS에서는 만드는 것보다 사용하는 것이 더 중요
=> iOS에서는 변수의 자료형이 결정되어 있다면 이름을 생략하고 .항목이름 만드로 대입이 가능
=> iOS SDK의 최근 버전의 가장 큰 변화 2가지는 Swift UI의 도입과 항목이름을 소문자로 사용하는 것
이전에는 대문자로 만들었다.
2019년 이전에 나온 책들에 소스 에러의 대부분은 이부분이다.
* Thread의 우선 순위 설정
1 ~ 10 까지만 사용할 수 있도록 하고 싶다면
우선 순위를 int로 priority로 만들게 되면 1~10 이외의 값을 설정한 경우 어떻게?
자바가 처음에 선택한 방법은
class Thread{
final static int ONE = 1;
final static int TEN = 10;
}
=> priority에는 final 상수를 대입하는 것을 권장
setPriority(Thread.TEM)
다른 정수를 대입할 수 도 있기 때문에 예외 발생 가능성이 있다.
enum ThreadPriority : Int{
case ONE = 1,
case TEN = 10
}
ThreadPrioryty priority 이렇게 선언하면 priority에는 ThreadPriority 안에 있는 값만 대입 가능 - 다른 값을 대입하면 에러
priority = .ONE
//열거형 상수 정의
enum Comm:Int{
case SK = 1
case LG = 9
}
var x : Comm = Comm.SK
x = .LG
//Appleㅇ서 나열(열거)형 상수는 전부 내부클래스 형태로 만들어져 있다.
//iOS Framework를 사용하다가 매개변수가 ?.? 이렇게 되어 있으면 이건 전부 열겨형 상수이다.
** Protocol
=> 자바의 인터페이스와 유사한 개념
자바의 인터페이스가 Protocol을 가져온 개념이다.
=> 공통으로 구현해야 할 기능 목록을 가진 것
=> 메소드와 속성 그리고 init의 선언만 포함할 수 있다.
속성의 경우
var 속성이름 : 자료형{get, set}
2. 메소드의 경우
func 메소드이름(매개변수)
3. init의 경우
init(매개변수)
4. 클래스에서 confirm
class 클래스이름:프로토콜이름 나열{
}
=> 여러 개를 나열하고자 하는 경우는 ,로 구분
=> 프로토콜의 모든 요소를 전부 구현해야 한다.
5. 선택적 구현
메소드나 프로퍼티 앞에 @objc optional 을 추가해주면 된다.
이렇게 만들어진 프로토콜을 confirm 하는 클래스는 클래스 앞에 @objc 를 추가하거나 Objectice-C의 클래스로부터 상속을 받아야 한다.
@objc protocol MediaPlayer{
//필수 구현 메소드
func play()
func stop()
//선택적 구현 메소드
@objc optional func pause()
}
//프로토콜을 구현한 클래스
//이 클래스는 NSObject로 부터 상속받아서 @objc를 생략
class VideoMediaPlayer:MediaPlayer{
func play() {
print("재생")
}
func stop() {
print("중지")
}
}
6. iOS에서는 프로토콜의 이름을 공통된 방법으로 사용
클래스이름DataSource : 데이터 출력을 위한 메소드를 소유한 프로토콜 - 필수 메소드 존재
클래스이름Delegate : 이벤트 처리를 위한 메소드를 소유한 프로토콜 - 선택적 구현
** Extension
=> 클래스의 기능을 확장하는 개념
=> 이전에는 Category 라고한다.
extention 기존클래스이름{
속성
메소드
}
=> 기존 클래스에 속성과 메소드가 추가
현재 프로젝트에서만 적용된다.
=> 하나의 프로젝트에서 추가했더라도 다른 프로젝트에는 영향이 없다.
=> protocol을 conform 할 때 클래스에 직접 메소드를 구현하기 보다는 extension을 이용해서 구현하는 경우가 많다.
=> protocol의 메소드를 클래스에 직접 구현을 하게 되면 이 클래스에서 만들어진 것인지 protocol 에 있는 메소드를 conform 한 것인지 구별이 잘 안되기 때문에 extension을 이용해서 구현
@objc protocol MediaPlayer{
//필수 구현 메소드
func play()
func stop()
//선택적 구현 메소드
@objc optional func pause()
}
//프로토콜을 구현한 클래스
//이 클래스는 NSObject로 부터 상속받아서 @objc를 생략
class VideoMediaPlayer{
}
//프로토콜의 메소드를 구현할 때는 extension을 이용해서 구현하는 것이 코드의 가독성을 높일 수 있는 방법이다.
extension VideoMediaPlayer : MediaPlayer{
func play() {
print("재생")
}
func stop() {
print("중지")
}
}
** Swift 에서 제공하는 자료구조 클래스
1. 종류
=> Array : 배열, 리스트의 개념으로 동작, Double Linked List 구조
=> Set : 중복되지 않도록 해싱을 해서 데이터를 저장하는 자료구조
=> Dictionary : Key와 Value를 쌍으로 저장하는 자료구조
=> Tuple : 데이터이 모임 - DTO의 성격
=> String : Character의 모임
=> Tuple을 제외하고는 전부 동일한 자료형의 모임이다.
여러가지 자료형을 저장하고자 하면 Any, AnyObject를 이용하면 된다.
=> Objective-C에는 Tuple을 제외한 자료형의 클래스가 존재한다
NSArray, NSSet, NSDictionary, NSString : 변경이 불가능한 자료형
NSMutableArray, NSMutableSet, NSMutableDictionary, NSMutableString : 변경이 가능한 자료형
2. String
=> String은 구조체 - 값의 모임
=> =로 대입하면 데이터를 복제해서 대입
=> + 와 += 이 재정의 되어 있어서 다른 문자열과 결합할 수 있다.
append 메소드를 이용해서 이어붙이기를 할 수 있다.
=> 데이터의 모임이므로 for ~ in 을 이용해서 하나의 문자 단위로 접근이 가능
=> 문자열 템플릿(보간) 기능을 소유 : 다른 변수의 값을 문자열 상수 안에 삽입해서 하나의 문자열을 만드는 것이 가능
=> "\u{유니코드}" 를 이용해서 문자열 생성하는 것이 가능
=> count 속성으로 무낮열 개수 파알 가능 : swift4에서 제공되는 문법
//문자열 변수 생성
var str:String = "Swift String"
//문자열 추가
str += "\u{1F496}"
print(str)
var str1:String = "ABCD"
var str2:String = "abcd"
print(str1 > str2)
//struct 는 값의 형태이고 class는 참조의 형태
var str3 = str1
//값을 복사했기 때문에 복사본에 작업을 수행해도 원본에는 영향이 없다.
str3 = "EFGH"
//Swift에서는 String을 Character의 모임으로 보기 때문에 이터레이터를 접근이 가능
for imsi in str1{
print(imsi)
}
3. Array
=> 배열, 동적인 배열이라서 Double Linked List 구조로 동작
1) 배열의 생성
=> 정적 생성 : var 또는 let 배열이름 : [자료형] = [데이터 나열]
=> 동적 생성 : var 또는 let 배열이름 = [자료형]()
2) 배열의 데이터를 추가
=> 마지막에 데이터를 추가 : append(데이터)
=> 중간에 추가 : insert(데이터, at:위치)
=> + 연산자를 이용해서 다른 배열을 추가할 수 있다.
3) 배열의 데이터를 순서대로 순회
=> 빠른 열거(이터레이터) 이용
for 임시변수 in 배열이름{
임시변수를 이용한 데이터의 순차적인 접근
}
=> 직접 순서대로 접근
count 속성을 이용하면 데이터의 개수를 알 수 있다.
[인덱스]를 이용하면 특정 인덱스에 해당하는 데이터에 접근이 가능, 인덱스는 0부터 시작
var idx : Int = 0
while(idx < 배열.count){
배열[idx]
idx = idx + 1
}
4) 배열의 특정 번째 데이터를 가져오는 것
배열[idx]
배열[시작인덱스...종료인덱스] : 범위 내의 데이터를 가져요기 - slicing
5) 배열의 데이터를 정렬하는 것
Array.sorted()를 호출하면 오름차순 정렬 가능 : 배열읠 각 요소가 되는 데이터가 >가 사용 가능해야 한다.
>가 없는 경우에는 함수를 생성해서 대입 : 이 함수는 2개의 매겨변수를 갖고 Bool을 리턴해야 한다.
6) 배열의 전체 데이터를 확인하는 것
=> 배열이름을 print메소드에 대입하면 description이라는 속성을 호출해서 각 요소의 description을 [ ]로 묶어서 출력
=> Java에서는 toString을 이용했지만 swift에서는 description이라는 속성을 이용
=> swift에서 toString을 쓰고자 하면 description이라는 프로퍼티를 오버라이딩 하면 된다.
//배열 생성
//기존 데이터를 가지고 배열을 생성
var names:[String] = ["을지문덕", "척준경", "남이"]
//기존 데이터가 없는 경우
var songs:[String] = [String]()
songs.append("혼자인 나") //마지막에 데이터를 추가
songs.insert("좆같은 인생", at:0) //첫번째에 데이터를 추가
//배열의 데이터 확인
print(names)
print(songs)
//배열의 데이터 정렬
names.sort() //sort는 자신의 데이터를 정령
//sorted()는 정렬한 결과를 리턴
print(names)
/*
//sort 함수의 by라는 매개변수의 두개의 매개변수를 가지고
//Bool을 리턴하는 함수를 대입하면 함수를 가지고 비교해서 데이터를 정렬해준다.
//함수를 생성해서 대입
func mycompare(str1:String, str2:String) -> Bool{
return str1 > str2
}
names.sort(by:mycompare)
print(names)
*/
/*
//클로저를 이용한 정렬
names.sort(by:{(str1:String, str2:String) -> Bool
in return str1 > str2})
print(names)
*/
//트레일링 클로저를 이용한 정렬
//함수가 마지막 매개변수인 경우 함수를 호출하는 구분 바깥에 클로저를 생성 - iOS 샘플코드가 대부분 이 방식
names.sort(){(str1:String, str2:String) -> Bool in
return str1 > str2
}
print(names)
7) 배열에 Map - Reduce Programming 을 위한 함수
=> map : 하나의 매개변수와 리턴 타입을 갖는 함수를 대입
배열의 각 요소를 대입받아서 리턴하는 값의 배열로 리턴해주는 함수
데이터를변환해주는 역할을 수행하는 함수이다.
=> filter : 하나의 매개변수와 Bool 리턴 타입을 갖는 함수를 대입
배열의 각 요소를 대입받아서 true를 리턴하는 요소들의 배열로 리턴해주는 함수
데이터를 필터링해주는 역할을 수행하는 함수
=> reduce : 2개의 매개변수와 하나의 값을 리턴 타입으로 갖는 함수를 대입
첫번째 매개변수는 중간 결과이고 두번째 매개변수는 배열의 각 요소가 되서 집계를 수행해서 리턴하는 함수
4. Set
=> 데이터를 중복없이 저장하는 자료구조
=> 해싱을 이용해서 데이터를 저장
=> 터치를 처리하는 메소드에서 터치의 값을 가져오기 위한 정도로만 사용
5. Tuple
=> 여러가지 종류의 데이터를 하나로 묶을 수 있는 자료형
=> 한번묶이면 수정이 불가능
=> 자료형 만들기
(자료형, 자료형, ...)
=> 데이터 만들기
(값, 값, ...)
=> 데이터 접근
튜플.인덱스
=> 데이터 앞에 이름을 부여하면 인덱스 대신에 이름으로도 접근 가능
=> Swift에서는 DTO를 별도로 잘 안만들고 튜플을 사용하는 것을 권장
//문자열 1개와 정수 1개를 저장하는 튜플을 생성
var user01:(String, Int) = ("아이린", 31)
//전체 출력
print(user01)
//부분철력
print(user01.0)
//튜플을 만들 때 각 항목에 이름을 부여해서 생성하는게 더 좋다.
//마치 DTO 클래스를 만들어서 사용하는 효과
var user02 : (name:String, age:Int) = (name:"수지", age:27)
print(user02)
print(user02.name)
print(user02.age)
6. Dictionary
=> Key 와 Value를 쌍으로 저장하는 자료구조
1) 생성
var 또는 let 변수명 : [key의 자료형 : value의 자료형] = [key:value, key:value,.....]
var 또는 let 변수명:Dictionary<ket의 자료형 : value의 자료형> = [:]
=> key의 자료형은 모든 자료형이 가능하지만 특별한 경우를 제외하고는 String
2) 데이터 개수는 count를 이용해서 찾아온다.
3) 접근
=> 데이터 추가 및 갱신
딕셔너리이름[키] = 값 //존재하는 키면 수정이고 존재하지 않는 키면 삽입 - upsert
=> 데이터를 리턴받을 때는 딕셔너리이름[키]
Swift에서는 딕셔너리로 데이터를 가져올 때는 Optional로 가져온다.
사용할 때는 !를 이용해서 Optional을 해제하고 사용해야 한다.
=> 삭제는 nil을 대입해도 되고 removeValueForkey(키이름)
4) 모든 데이터 접근
=> Dictionary를 for~in을 이용해서 접근하면 모든 데이터를 튜플로 하나씩 리턴
=> keys 속성을 호출하면 모든 key의 값을 리턴
//딕셔너리 생성
var dict1 = ["name":"아이린", "age":"31"]
//값을 여러가지 자료형으로 삽입하고자 할 때는 value의 자료형을 Any로 설정해야 한다.
//스위프트 샘플에서는 Any로 설정하는 경우가 거의 없는데 iOS에서는 Any로 설쟁해서 리턴하는 경우가 많다.
var dict2:Dictionary<String, Any> = ["name":"배수지", "age":27]
//확인
print(dict2)
//name 속성의 값 가져오기
** 예외처리
do{
try{
예외가 발생할 가능성이 있는 코드
}
catch 예외이름{
예외가 발생했을 때 사용할 코드
}
..
}
=> 이전 API에서 예외처리를 강제하는 경우에는 예외처리 구문을 사용해야 하는데 try! 를 앞에 붙이면 예외처리를 하지 않아도 된다.
** defer
=> defer{내용} 을 사용하면 위치에 상관없이 블럭이 끝나면 호출