동시성

냐옹·2024년 7월 11일
0

IOS

목록 보기
9/32

본 작성글은 swift 공식 문서를 참고하였습니다.

동시성 Concurrency

  • 여기서부터 잘 봐야
  • 비동기적으로 실행되는 함수를 나타내기 위해서 async를 사용한다.
  • asyncfunction 키워드 앞에 붙이는 점이 Javascript와 다르다.
func fetchUserID(from server : String) async -> Int{
// 전달인자레이블을 from으로 지정
// 반환형 Int, 비동기 함수 async
	if server == "primary"{
    	return 97
    }
  	return 501
}
  • 앞에 await을 작성해서 비동기 함수를 호출하는 것을 나타낸다.
func fetchUserName(from server : String) async -> String{
	let userID = await fetchUserId(from : server)
    
    if userID == 501{
    	return "John Appleseed"
    }
  	return "Guest"
}

async let 비동기 함수로 초기화하는 변수 선언

  • 비동기 함수를 호출하기 위해서async let을 사용해서 다른 비동기 코드와 병렬로 실행할 수 있다.
  • await을 작성하여 반환된 값을 사용한다.
func connectUser(to server : String) async{
	async let userID = fetchUserId(from : server)
    async let userName = fetchUsername(from : server)
    
    let greeting = await "Hello \(userName), user ID \(userID)"
}

do-catch

  • swift에서의 do-catchjavascript에서의 try-catch와 비슷한 동작을 수행한다.

  • 잠재적으로 예외를 발생할 수 있는 코드를 do 코드블록 안에 삽입하고 catch에서 이를 처리한다.

  • 기본구조의 예시는 다음과 같다.

do{
	try 잠재적으로 오류를 발생시킬 수 있는 코드
}catch{
  	오류를 처리하는 코드
}
  • 많이 중요하기 때문에 예시코드를 여러개 살펴보자.
enum NetworkError : Error {
	case invalidURL
  	case noData
  	case decodingError
}

struct User : Codable{
  

}

중간에 알아야 할 내용들을 먼저 살펴보자면,
1. JSON - SWIFT object
2. Codable
을 먼저 살펴보겠다.

JSON과 SWIFT 객체
  • JSONSWIFT 객체디코딩/인코딩을 거치지 않고서는 서로 호환되지 않는다.

  • 디코딩/인코딩에 들어가기 전 Codable을 알아야하는데 이는 데이터 인코딩과 디코딩을 쉽게 처리할 수 있게 해주는 프로토콜이다.

  • Codable은 실제로 EncodableDecodable프로토콜의 type alias이다. 주요특징은 다음과 같다.
    ㄴ 1. 자동구현
    대부분의 경우에 swift 컴파일러가 자동으로 인코딩 / 디코딩 로직을 생성한다.
    ㄴ 2. JSON 변환
    JSON과 swift 객체 간의 변환을 쉽게 할 수 있다.
    ㄴ 3. 사용자 정의
    필요한 경우 인코딩 / 디코딩 프로세스를 커스터마이징할 수 있다.
    ㄴ 4. 다양한 데이터 형식 지원
    JSON 뿐만 아니라 PropertyList 등 다양한 형식을 지원한다.

  • Codable 예시코드
  1. JSON -> SWIFT object (디코딩)
// Codable
struct User : Codable {
	let id : Int
    let name : String
    let email : String
}

let jsonString = """
{
	"id" : 1,
    "name" : "John Doe",
    "email" : "john@example.com"
}

// JSON -> swift object
let jsonData = Data(jsonString.utf8)
let decoder = JSONDecoder()
"""

do{
	let user = try decoder.decode(User.self, from : jsonData)
  	print("User ID : \(user.id), Name : \(user.name), Email : \(user.email)")
}catch{
	print("Decoding Error : \(error)")
}
  1. SWIFT object -> JSON (인코딩)
struct User : Codable {
	let id : Int
    let name : String
    let email : String
}

let user = User(id : 2, name : "John Doe", email : "jane@example.com")

let endcoder = JSONEncoder()
encoder.outputFormatting = .prettyPrinted

do{
  let jsonData = try encoder.encode(user)
  
  if let jsonString = String(data : jsonData, encoding : .utf8){
  	print("JSON : \(jsonString)")
  }
}catch{
  	print("Encoding error : \(error)")
}
암시적 오류 바인딩
  • swift에서는 catch절에서 오류를 암시적으로 바인딩할 수 있다.
  • 몇가지 형태가 있는데 다음과 같다.
  1. 일반적인 catch
catch{
	print("Decoding error : \(error)")
  	// error는 암시적으로 바인딩된 오류 객체이다.
}
  1. 특정 오류 타입을 명시적으로 잡는 catch
catch let decodingError as DecodingError{
	print("Specific decoding error : \(decodingError)")
}
  1. 패턴 매칭을 사용한 catch
catch DecodingError.keyNotFound(let key, _){
	print("Missing key : \(key)")
}
  • 3번이 살짝 이해가 안가서 조금 더 알아보겠다.
    3번을 살펴보면 DecodingError라는 에러 객체를 볼 수 잇는데, 이는 열거형 enum이다. 이를 대상으로 다시 세부적인 예시코드를 보자.
do{
	
}catch DecodingError.keyNotFound(let key, let context){
	// JSON에서 필요한 키를 찾지 못했을 때
  	// key : 찾지 못한 키의 이름, context : 오류에 대한 추가정보
}catch DecodingError.valueNotFound(let type, let context){
	// 예상된 값이 JSON에 없을 때 발생
  	// type : 찾지 못한 값의 예상타입
}catch DecodingError.typeMismatch(let type, let context){
	// JSON의 값 타입이 예상과 다를 때 발생
  	// type : 예상된 타입
}catch{
	print("ERROR: \(error)")
}
  • 아래 2개를 안하고서는 샘플 코드를 이해하기가 힘들다....

guard let , if let

  • if letguard let은 옵셔널 값을 확인하고 해당 값을 일시적으로 변수상수바인딩하는 기능을 수행한다.

  • 그럼 옵셔널이란
    Swift에서 옵셔널은 값이 존재할 수도, 존재하지 않을 수도 있는 상황을 표현하는 타입이다.
    예시로 Int?를 들 수 있음
    옵셔널을 선언할 때 초기값을 지정하지 않으면 자동으로 nil이 할당된다.
    nil이 할당된 값에 접근하는 경우에 런타임 에러가 발생하기 때문에 옵셔널 바인딩, 옵셔널 체이닝, 옵셔널기본값 사용 등을 통해 nil값에 대한 처리가 필요하다.

  • 옵셔널 바인딩
    옵셔널 바인딩이란 강제로 옵셔널을 여는게 아니라 안전하게 확인을 해보고 나서 언래핑하는 방법이다.
    즉 if문을 이용하여 옵셔널에 할당된 값을 임시변수 또는 상수에 할당을 해주는 방식이다.
    ㄴ 1. 강제로 언래핑 : !를 써서 강제로 옵셔널 추출
    ㄴ 2. 옵셔널 바인딩 (안전한 언래핑) : if let, guard let을 써서 옵셔널 추출 if let 또는 if var를 사용하여 옵셔널의 값이 존재하는지 검사하고, 존재하면 그 값을 다른 변수에 대입한다. 만약에 해당 옵셔널의 값이 nil이면 그냥 넘어가고, nil이 아니면 코드 블럭 안의 구문을 실행한다. 옵셔널 바인딩의 예시코드는 다음과 같다.
let x : String? = "TEST"
let y : String? = nil
// 초기화해주지 않으면 자동으로 nil로 초기화된다.

if let xx = x{
	print("\(xx)는 nil이 아니었음")
}

if let yy = y{
	print("\(yy)는 nil이 아니었음")
}

// 출력 : TEST는 nil이 아니었음
  • 다음으로 강제로 실행의 예시코드는 다음과 같다.
let x : String? = "TEST"
let y : String? = nil

print("\(x!)")
print("\(y!)")
// 첫번째 x는 출력되지만 Y는 널참조로 런타임 에러가 발생한다. 
  • 옵셔널 바인딩을 여러개 할 수 도 있다.
let name1 : String?
let name2 : String?
// 둘다 자동으로 nil로 초기화 된다. 
  
if let name11 = name1,
  let name22 = name2{
	print(name11, name22)
}
  • 옵셔널 체이닝
    언래핑없이 옵셔널 값에 접근한다.
  1. 예를 들어서
struct TestStruct{
	wrapper : TestInnerStruct?
}

struct TestInnerStruct{
	innerValue : Int? = 10
}

라고 한다.

이제 여기에서

let result = TestStruct?.wrapper?.innerValue;

를 했다고 쳐보자.
1. result의 type은 일단 옵셔널<맨 뒤의 타입이다. 여기서는 innerValue>
2. 만약에 중간에 wrapper가 nil이었다. 그러면?
런타임에러는 발생하지 않고, resultnil이 된다.
ㄴ 이어지는 innerValue는 평가하지 않는다

  • 옵셔널 기본값
let testValue1 : Int? = 10
let testValue2 : Int? 
// testValue2는 자동으로 nil
  
let testValue3 = testValue2 ?? 20 // testValue2가 nil이라면 20

let testValue4 :Int?
  
let testValue5 = testValue2 ?? testValue4 ?? 30 // 둘다 nil이라면 30
guard let
  • if let과 비슷하지만, guard let에서는 else인 부분만 작성이 가능하다.
  • 즉, 값이 nil이어서 옵셔널 추출이 되지 않을 때만 어떠한 행동을 취할 수 있다.
  • 만약에 nil값이 아닐 걸 확인하고 옵셔널을 성공적으로 추출했다면, guard let문을 통과하게 된다.
  • (유의) guard let문을 통과하게 되면 저장된 상수는 전역변수로써 사용이 가능하다.
  • (유의) guard let 문의 else 안에는 항상 return 아니면 throw문이 와야한다.

예시코드를 살펴보면

let value1 : Int? = 10 
let value2 : Int?

guard let test1 = value1 else{
	return print("value1는 optional")
}

print(test1)

guard let test2 = value2 else{
	return print("value2는 optional")
}

print(test2)

// 이렇게 되면 출력이 10, value2는 optional이라고 뜨는데 
// 여기서 test1은 전역변수로써 사용이 가능하다. 

in , 익명함수

swift에서 익명함수를 만들기 위해서 다음과 같은 절차를 따른다.

    1. 함수 선언 없이 중괄호로 감싼 함수 몸체만 만들고
    1. 파라미터 목록이나 반환타입 설정이 필요한 경우에, 중괄호 영역 첫째줄에 선언하고 in 키워드를 입력한다.

ex) 예시 함수 (인자가 없는 경우)

func exampleFunc() {
	self.Something.x += 20
}

위와 동일한 기능을 하는 익명함수 (이상하게 생김.. 진짜로.. 아이폰은 이쁜데 얘는 왜 이렇게 생겼지..)

{
	() -> () in 
    self.Something.x += 20
}

ex) 예시 함수 ( 인자가 있는 경우 )

func exampleFunc(testValue : Int){
	self.Something.x += testValue
}

위와 동일한 기능을 하는 익명함수

{
	(testValue : Int) -> () in
    print("test value : \(testValue)")
}
  • 익명함수를 활용하는 방법 1 : 인자를 줄때 아예 거기서 선언
UIView.animateWithDuration(0.4, animation : whatToAnimate, completion : whatToDoLater)

위 코드를 익명함수를 사용한다면

UIView.animateWithDuration(0.4, 
                           animation : 
                           	{() -> () in 
								self.myButton.frame.origin.y += 20
							}
						   completion : 
							{
                              (finished : Bool) -> () in
                              	println("finished : \(finished)")
                            }
  • 익명함수는 한번 더 축약할 수 있다. OTL..
    1. 반환타입을 생략할 수 있다.
UIView.animateWithDuration(0.4, animation : {
                           		() in
  								~~~본문
                           }, completion : {})
    1. 파라미터가 없으면 in 라인을 생략할 수 있다.
UIView.animateWithDuration(0.4, animation : {
                           		~~~본문
                           }, completion : {})
    1. 파라미터 타입을 생략할 수 있다.
UIView.animateWithDuration(0.4, animation : {
                           		(testValue) -> () in 
  								~~~본문
                           }, completion : {})
                           
// 파라미터 타입과 반환값 생략
UIView.animateWithDuration(0.4, animation : {
                           		(testValue) in 
  								~~~본문
                           }, completion : {})
    1. 중괄호를 생략할 수 있다.
UIView.animateWithDuration(0.4, animation : {
                           		testValue in
                           		~~~ 본문
                           }, completion : {})
    1. 파라미터가 있는 때에도 in 라인을 생략할 수 있다.
UIView.animateWithDuration(0.4, animation : {
                           		print("first param : \($0)")
                           }, completion : {})
    1. 파라미터 이름을 생략할 수 있다. (파라미터가 필요없는 동작만 할 경우)
UIView.animateWithDuration(0.4, animation : {
                           		_ in
                           		print(~~)
                           }, completion : {})
  • 익명함수의 응용
let arr = [2,4,6,8]

func doubleMe(i : Int) -> Int{
	return i*2
}

let arr2 = arr.map(doubleMe)

// 익명함수로 축약하면 다음과 같이 가능하다.

let arr3 = arr.map{
	element -> Int in 
    return element * 2
}
    
let arr4 = arr.map{
	return $0 * 2
}

let arr5 = arr.map{ $0 * 2 }

`

0개의 댓글