- 파운데이션(Foundation) 프레임워크란? MacOS, iOS 애플리케이션 개발의 기초적인 기능을 제공하는 프레임워크로 클래스 이름 앞에 NS~로 시작되는 클래스로 가장 최상위 부모 클래스는 NSObject 클래스이다.
- 파운데이션 프레임워크에 있는 주요 기능
- 데이터 다루기
- 네트워크
- 파일 다루기
- 멀티 쓰레드
- Swift와 Foundation 프레임워크 : Swift2까지는 Objective-C 기반의 클래스와 API를 유지하여 NSString, NSMutableString, NSArray, NSMutableArray 등으로 제공했지만, Swift3부터 Swift 용으로 포팅하여 API Guideline을 적용하여 NS를 붙이지 않아도 사용 가능해졌다. NSString -> String 으로도 사용 가능하다.
- 파운데이션 프레임워크에서 가장 기본 클래스 : NSObject
- 제공되는 기능
- 객체 비교
- 메모리 관리
- 타입 체크
- 셀렉터
- NSObject 클래스를 상속하여 사용할지 아니면 그냥 Swift에서 제공하는 클래스를 사용할지는 셀렉터를 사용하면 무조건 NSObject 클래스를 상속받아 사용하고 그렇지 않은 경우는 NSObject 클래스를 상속하지 않아도 무관하다.
- 셀렉터 : 셀렉터는 메소드를 식별하는 정보로 사용되며, 주로 클래스에 정의된 메소드를 검사한 후에 실행하는데 사용된다.
- 사용방법셀렉터 사용방법
// 셀렉터 정의
#selector(TYPE.Method_Name)
// 셀렉터 검사
func responds(to aSelector : Selector!) -> Bool
// 셀렉터 실행
// 파라미터가 없는 메소드 실행
func perform(_ aSelector : Selector!) -> Unmanaged<AnyObject>!
// 파라미터가 있는 메소드 실행
func perform(_ aSelector : Selector!, with object:Any!) -> Unmanaged<AnyObject>! - 파라미터가 없는 메소드에서 셀렉터 사용방법파라미터가 없는 메소드
// Class 정의
class MyClass : NSObject {
@objc func greeting() { print("Hello") }
}
let obj = MyClass()
// 셀렉터 정의
let sel = #selector(MyClass.greeting)
// 셀렉터 검사
if obj.responds(to : sel) {
// 셀렉터로 메소드 실행
obj.perform(sel)
} - 파라미터가 있는 메소드에서 셀렉터 사용방법파라미터가 있는 메소드
// Class 정의
class MyClass : NSObject {
@objc func sayHello(who : String) {
print("Hello \(who)")
}
}
let obj = MyClass()
// 셀렉터 정의
let sel = #selector(MyClass. sayHello(who : ))
// 셀렉터 검사
if obj.responds(to : sel) {
// 셀렉터로 메소드 실행
obj.perform(sel, with : "Selena")
} - 셀렉터로 사용되는 메소드 앞에 @objc 키워드는 Objective-C 메소드로 사용한다는 의미로 셀렉터로 사용하려는 메소드 앞에 반드시 붙여야 하고, 붙이지 않을 경우 에러가 발생한다.
- 파일 다루기
- FileManager : 파일 시스템을 다룬다.
- 사용 방법 : let filmanager = FileManager.default 로 선언하여 사용한다.
- 파일 존재확인파일 존재 확인 방법
// FileManager 선언
let fm = FileManager.default
let checkFilePath = "/User/Document/Test.txt"
// 파일 존재여부 확인
if(fm.fileExists(atPath : checkFilePath)) {
print("파일 존재")
} else {
print("파일 존재하지 않음")
} - 폴더 내 파일 목록 확인폴더 내 파일 목록 확인 방법
// FileManager 선언
let fm = FileManager.default
let path = "/User/Document"
// 폴더 존재여부 확인
if(fm.fileExists(atPath : path)) {
let fileList = try? fm.contentOfDirectory(atPath : path)
print("파일 목록 : \(fileList)")
} else {
print("파일 존재하지 않음")
} - 파일 복사파일 복사 방법
// FileManager 선언
let fm = FileManager.default
let srcPath = "/User/Document/Test.txt"
let desPath = "/User/Test/Test.txt"
//복사하려는 파일 존재여부 확인
if(fm.FileExists(atPath : srcPath) == false) {
print("복사할 파일이 없음")
} else if(fm.FileExists(atPath : desPath) == false) {
print("복사할 폴더에 이미 파일이 있음")
} else {
print("복사 중...")
do{
try fm.copyItem(atPath : srcPath, toPath : desPath)
} catch let err {
print("복사 실패 : \(err)")
}
} - 파일 이동파일 이동 방법
// FileManager 선언
let fm = FileManager.default
let srcPath = "/User/Document/Test.txt"
let desPath = "/User/Test/Test.txt"
// 이동하려는 파일 존재여부 확인
if(fm.FileExists(atPath : srcPath) == false) {
print("이동할 파일이 없음")
} else if(fm.FileExists(atPath : desPath) == false) {
print("이동할 폴더에 이미 파일이 있음")
} else {
print("이동 중...")
do{
try fm.moveItem(atPath : srcPath, toPath : desPath)
} catch let err {
print("이동 실패 : \(err)")
}
} - 파일 삭제파일 삭제 방법
// FileManager 선언
let fm = FileManager.default
let delPath = "/User/Document/Test.txt"
// 삭제하려는 파일 존재여부 확인
if(fm.FileExists(atPath : delPath) == false) {
print("삭제할 파일이 없음")
} else {
print("삭제 중...")
do{
try fm.removeItem(atPath : delPath)
} catch let err {
print("삭제 실패 : \(err)")
}
} - 직렬화
- 직렬화는 생성한 객체를 바이너리 데이터로 변경하는 것으로 객체를 바이너리 데이터로 변환하는 것을 아카이브(Archive) 즉, 직렬화이고, 바이너리 데이터를 다시 객체로 변환하는 것을 언아카이브(Unarchive) 즉, 역직렬화라고 한다.
- 직렬화는 네트워크 전송이나 파일에 내용을 쓸 때 주로 사용된다.
- 바이너리 데이터는 Data 객체로 변환된다.
- 데이터 직렬화 방법
- NSKeyedArchiver : 객체를 바이너리 데이터로 변환 (Any -> Data, File)
- NSKeyedUnarchiver : 바이너리 데이터를 객체로 변환 (Data, File -> Any)
- 사용방법객체 직렬화
let num = 2020
// 직렬화
let data : Data = NSKeyedArchiver.archivedData(withRootObject : num)
// 역직렬화
if let num2 = NSKeyedUnarchiver.unarchiveObject(withData : data) as? Int {
print("Dat에서 복원 성공 : \(num2)")
} else {
print("복원 실패 ")
} - 다수의 데이터 직렬화 방법
- NSMutableData : 키 - 값 방식으로 직렬화를 진행한다.
- encode : 값, 키를 파라미터로 받는 메소드로 값을 직렬화 한다.
- decode : 키를 파라미터로 받는 메소드로 키에 해당하는 값을 역직렬화 한다.
- 사용방법다수의 데이터 직렬화
let mdata = NSMutableData()
// 인코딩
let archiver = NSKeyedArchiver(forWritingWith : mdata)
archiver.encode(true, forKey : "BoolData")
archiver.encode(177, forKey : "IntData")
archiver.encode("Hello", forKey : "StrData")
archiver.finishEncoding()
let data = archiver.encodedData
// 디코딩
let unarchiver = try?NSKeyedUnarchiver(forReadingFrom : data)
let boolData = unarchiver.decodeBool(forKey : "BoolData") // true 저장
let intData = unarchiver.decodeInteger(forKey : "IntData") // 177 저장
let strData = unarchiver.decodeObject(forKey : "StrData")
// Hello 저장 - 커스텀 타입의 직렬화 방법
- NSCoding 프로토콜 : 인코딩 / 디코딩 함수를 정의하며 클래스만 가능하고 구조체는 불가능하다.
- 사용되는 메소드
- 인코딩 메소드 : func encode(withCoder aCoder : NSCoder)
- 디코딩 메소드 : init?(coder aDecoder : NSCoder)
- 사용방법커스텀 타입의 직렬화
// 클래스 정의
class Person : NSObject, NSCoding {
var name : String
var birthYear : Int
// 인코딩 메소드
func encode(with aCoder : NSCoder) {
aCoder.encode(name, forKey : "Name")
aCoder.encode(birthYear, forKey : "Birth")
}
// 디코딩 메소드
required init(coder aDecoder : NSCoder) {
name = aDecoder.decodeObject(forKey : "Name") as! String
birthYear = aDecoder.decodeInteger(forKey : "Birth")
}
// initializer 로 정보를 받아옴
init(name : String, birthYear : Int) {
self.name = name
self.birthYear = birthYear
}
}
// 인코딩 / 디코딩을 사용한 객체 생성
// 객체 생성
var person = Person(name : "박문수", birthYear = 1355)
// 인코딩
let data = NSKeyedArchiver.archivedData(withRootObject : person)
// 디코딩
let obj = NSKeyedUnarchiver.unarchiverObject(with : data) as! Person
print("name : \(obj.name), birthYear ; \(obj.birthYear)")
// name : 박문수, birthYear : 1355 출력 - 타이머
- 타이머란? 수행할 동작을 특정 시간 이후에 실행하도록 하는 기능이다.
- Timer 클래스를 이용하여 타이머를 시작하고, 중지할 수 있다.
- 사용방법타이머 사용
// 클래스 정의
class Alarm : NSObject {
// 특정 시간 이후에 실행할 동작을 정의한 메소드
@objc func ring(timer : Timer) {
print("Wake Up!!")
}
}
// 객체 생성
let alarm = Alarm()
// 타이머 생성
var timer = Timer.scheduledTimer(timeInterval : 1, target : alarm, selector:#selector(Alarm.ring), useInfo : nil, repeats : false)
// 타이머 바로 시작
timer.fire() // Wake Up!! 출력
// 클로저를 이용한 Timer
Timer.scheduledTimer(withTimerInterval : 1.0, repeats:false) {
timer in print("클로저를 이용한 타이머")
} - 알림
- 알림이란? 특정 조건에 수행할 동작을 옵저버를 이용하여 동작하는 기능이다.
- 알림 구성
- 알림(Notification)
- 알림 센터(NotificationCenter) : 알림을 발송하고, 알림 감시자(Observer)를 등록한다.
- 알림 감시자(Observer) : 알림 발생 시 동작을 정의하고, 더 이상 필요하지 않다면 제거한다.
- 사용 방법알림 사용
// 클래스 정의
class MyClass : NSObject {
// 특정 시간 이후에 실행할 동작을 정의한 메소드
@objc func handleNoti(noti : Notification) {
print("Hello Swift Custom Alarm")
}
}
// 알림 센터 선언
let notiCenter = NotificationCenter.default
// 알림 이름 정의
let customNoti = Notification.Name("CustomNotification")
// 옵저버 추가
let myClass = MyClass()
notiCenter.addObserver(myClass, selector : #selector(MyClass. handleNoti), name: customNoti, object: nil)
// 알림 발생
notiCenter.post(name: customNoti, object: nil)
// Hello Swift Custom Alarm 출력 - 멀티 쓰레드
- 쓰레드란? 오래 걸리는 작업을 사용자가 기다리지 않고 수행할 수 있도록 처리하는 기능이다.
- 쓰레드 사용방법
- 셀렉터를 이용한 쓰레드셀렉터를 이용한 쓰레드
// 클래스 정의
class MyClass : NSObject {
// 0부터 10까지 출력하는 메소드
@objc func countTen() {
for count in 0...10 {
print(count)
}
}
}
// 객체 선언
let obj = MyClass()
// 셀렉터를 이용한 쓰레드 - 1
obj.performSelector(inBackground : #selector(MyClass.countTen), with: nil)
// 셀렉터를 이용한 쓰레드 - 2
Thread.detachNewThreadSelector(#selector(MyClass.countTen), toTarget: obj, with: nil)
sleep(10)
// 슬립을 주지 않으면 쓰레드가 실행되기 전에 프로그램이 종료되어 쓰레드가 동작하는 것을 볼 수 없음 - 클로저를 이용한 쓰레드클로저를 이용한 쓰레드
// 클로저를 이용한 쓰레드
Thread.detacthNewThread {
for count in 0...10 {
print(count)
}
}
sleep(10)
// 슬립을 주지 않으면 쓰레드가 실행되기 전에 프로그램이 종료되어 쓰레드가 동작하는 것을 볼 수 없음 - Thread의 자식 클래스를 작성한 쓰레드Thread의 자식 클래스를 사용
// 쓰레드 정의
class MyThread : Thread {
// 쓰레드가 수행할 동작 정의
override func main() {
for count in 0...10 {
print(count)
}
}
}
let thread = MyThread()
thread.start() // 쓰레드 동작 시작
sleep(10)
// 슬립을 주지 않으면 쓰레드가 실행되기 전에 프로그램이 종료되어 쓰레드가 동작하는 것을 볼 수 없음 - 멀티 쓰레드는 이런 쓰레드가 여러개 동작하는 것을 의미한다.
- 예시는 아래와 같다.멀티 쓰레드 - 쓰레드가 여러개 동작
// 쓰레드 정의
class MyThread : Thread {
// 쓰레드가 수행할 동작 정의
override func main() {
for count in 0...10 {
print(count)
}
}
}
let thread1 = MyThread()
thread1.start() // 쓰레드 동작 시작 - 1개
let thread2 = MyThread()
thread2.start() // 쓰레드 동작 시작 - 2개
// 클로저를 이용한 쓰레드
Thread.detacthNewThread {
for count in 0...10 {
print(count)
}
} // 쓰레드 동작 시작 - 3개
sleep(10)
// 슬립을 주지 않으면 쓰레드가 실행되기 전에 프로그램이 종료되어 쓰레드가 동작하는 것을 볼 수 없음 - 멀티 쓰레드를 조절하는 멀티 쓰레드 클래스 Operation, OperationQueue
- Operation은 큐를 기반으로 동작한다.
- 동시 동작하는 쓰레드 개수를 조절 : var maxConcurrentOperationCount : int
- 쓰레드 동작/취소
- 쓰레드 동작 : func addOperation(_ op : Operation) 함수에 추가
- 쓰레드 취소 : func cancelAllOperations()
- 큐를 이용한 쓰레드 관리 : OperationQueue 클래스
- 사용방법Operation을 이용한 멀티 쓰레드 사용
// Operation 정의
class MyOperation : Operation {
// 수행할 동작 정의
override func main() {
for count in 0...10 {
print(count)
}
}
}
// OperationQueue 선언
let opQueue = OperationQueue()
// 최대 실행 개수 2개까지 허용
opQueue.maxConcurrentOperationCount = 2
let operation1 = MyOperation()
let operation2 = MyOperation()
let operation3 = MyOperation()
// 클로저를 이용한 Operation 실행
opQueue.addOperation({
for count in 0...10 {
print(count)
}
}) // 1개 실행
opQueue.addOperation(operation1) // 2개 실행
// 대기 : 한번에 최대 2개까지만 실행하게 설정
opQueue.addOperation(operation2)
// 위 2개 중에 하나의 실행이 끝날 때 까지 대기
opQueue.addOperation(operation3)
// 위 3개 중에 둘의 실행이 끝날 때까지 대기
sleep(10)
// 슬립을 주지 않으면 쓰레드가 실행되기 전에 프로그램이 종료되어 쓰레드가 동작하는 것을 볼 수 없음 - 네트워크
- 네트워크 사용 시 필요 정보는 URL(리소스 위치 정보), 요청(URLRequest), 응답(URLResponse) 이다.
- URL 정보
- 네트워크를 통해 접속할 위치 정보를 의미한다.
- URL 허용 문자는 알파벳, 숫자, 하이픈(/), 닷(.) 등으로 제한된 문자만 허용한다.
- 그 이외 문자는 퍼센트 인코딩 필요
예) https://www.google.com/search?q=아이폰
-> https://www.google.com/search?q=%EC%95%84%EC%9D%B4%ED%8F%B0URL을 위한 퍼센트 인코딩/디코딩let urlStr = "http://www.google.com?q=아이폰"
// 인코딩
let encoded = urlStr.addingPercentEncoding(withAllowedCharacters : .urlQueryAllowed)!
/* 출력 : http://www.google.com?q=%EC%95%84%EC%9D%B4%ED%8F%B0 */
// 디코딩
let decoded = encoded.removingPercentEncoding!
/* 출력 : http://www.google.com?q=아이폰
*/ - URL 분석
- URL 주소는 Scheme, host, path, query 로 구성된다.URL 구성 요소
let url = URL(string: "http://google.com?q=iPhone&format=json")!
// scheme
url.scheme // http
// host
url.host // google.com
// path
url.path //
// query
url.query // q=iPhone&format=json - 요청(URLRequest)과 응답(URLResponse)
- 사용 예시를 통해서 URL 주소에 요청하고 요청한 내용을 응답받는 예제는 아래와 같다.URL 요청과 응답 처리
// 요청할 URL
let url = URL(string: "http://google.com?q=iPhone&format=json")!
// 요청 : URLRequest
let request = URLRequest(url : url)
let session = URLSession(configuration: URLSessionConfiguration.default)
let task = session.dataTask(with: request) {
// 클로저를 이용한 응답
(data : Data?, response : URLResponse?, error : Error?) in
let httpResponse = response as! HTTPURLResponse
print("status code : ", httpResponse.statusCode)
let headers = httpResponse.allHeaderFields
for(key, value) in headers {
print("\(key) : \(value)")
}
}
task.resume()