정리하려고 맘 먹고 작성 중인데... 잘못 먹은 것 같다..

함수

  • 앞서 말했 듯, Swift에서의 함수는 일급 시민이다

함수 vs 메서드

  • 메서드 : 구조체, 클래스, 열거형 등 특정 타입에 연관되어 사용하는 함수
  • 함수 : 모듈 전체에서 전역적으로 사용하는 함수
    • 개발자들끼리 약속된 것 같다. 누구는 클래스의 메소드를 메소드라하고, 누구는 함수라 하고, 누구는 펑션이라 한다면 의사소통이 비효율적일 것이기 때문에, 약속(?)한 것이 아닐까
    • 나도 이때부터 확실히 머리속에 박아두고 설명할 때는 인지하며 말하는 편이다.
//매개변수가 없는 함수
func sayHello(){
	print("Hello, My name is tree")
}

//매개변수가 있는 함수
func sayHello(name: String){
	print("Hello, My name is \(name)")
}

//매개변수의 기본값이 있는 함수
func sayHello(name: String = "tree"){
	print("Hello, My name is \(name)")
}

//반환 타입이 있는 함수
func sayHello(name: String) -> String{
	return "Hello, My name is \(name)"
}

+ 매개변수의 기본값이 있는 매개변수는 뒤로 보내야 한다.

 

입출력 매개변수 사용

  • 순서
    • 전달인자 값 복사
    • 함수 내부에서 값 변경
    • 반환 시점에 변경된 값을 매개변수에 할당
  • inout을 사용하면 값 타입도 참조 타입처럼 사용할 수 있다
  • B.U.T. 메모리 관점에서 안전하지 않기 때문에 사용을 지양한다. -> 이에 대해서도 이후에 작성할 것이다
//보통은 값을 복사하여 매개변수로 전달한다

var arr: [Int] = [1,2,3,4,5]

func abc(_ arr: [Int]){
	arr[0] = 100
}

abc(arr)
print(arr[0]) // 1

//입출력 매개변수 사용
func abc(_ arr: inout [Int]){
	arr[0] = 100
}

abc(&arr)
print(arr[0]) // 100

함수를 데이터 타입으로

- 앞서서 여러 차례 말했 듯이 함수는 일급 시민으로써 사용된다

var someFunction: (Int, Int) -> Int

func addInts(_ a: Int, _ b: Int) -> Int{
	return a+b
}

someFunction = addInts

print(someFunction(1,2)) // 3

someFunction이라는 변수에 addInts라는 함수를 지정하여 사용할 수 있다

B.U.T. 타입이 같아야한다.

 

중첩 함수

말 그대로 함수 내에 함수가 존재하는 것이다.

func function1(_ bool: Bool) -> (Int) -> Int{
	func plus(_ i: Int) -> Int{
    	return i+1
    }
    func minus(_ i: Int) -> Int{
    	return i-1
    }
    
    //삼항 연산자
    // ? 앞의 식이 true면 plus를 false면 minus를 return한다
    return bool ? plus: minus
}

var num: (Int) -> Int = func(true) //plus함수 할당
print(num(1)) // 2

 

이외에도 다양한 함수들이 존재한다

  • 종료되지 않는 함수
    • 반환 타입을 Never로 선언
  • 반환값을 무시 가능한 함수
    • @discardableResult 어노테이션 사용

스위프트의 데이터 타입

앞서 변수 선언 방법 

Swift : var value: Int = 100

Java : int value = 100

Kotlin : var value: Integer = 100

......

 

  • Int, UInt
    • 8, 16, 32, 64 비트 형태로 나타낼 수 있음
      • ex) 32비트 형태는 Int32 로 표현
  • Bool
    • true/false를 나타내는 데이터 타입
    • 내부에 toggle()이라는 API를 통해 값을 반전할 수 있음
  • Float / Double
    • Float : 32비트
    • Double : 64비트
  • Character
  • String
    • hasPrefix : 접두어 확인
    • hasSuffix : 접미어 확인
    • uppercased : 대문자로 변경
    • 등의 다양한 API가 존재함
    • 여러 줄의 String 형태를 변수에 담고 싶을 때는
var str: String = """
abcd
efgh
ijkl
"""

처럼 사용할 수 있다.

 

또한 Swift에는 Any, AnyObject, nil 타입이 존재한다.

  • Any
    • 모든 데이터 타입을 담을 수 있는 데이터 타입
  • AnyObject
    • 모든 Class 타입을 담을 수 있는 데이터 타입
  • nil
    • 알고 있는 null과 같다

 


데이터 타입 고급

  • 타입 확인
    • 컴파일 시점에 확인한다.
      • 타입이 옳지 않는 코드가 존재하면 컴파일러가 알려준다.
        • Application 내에서 오류가 발생하지 않게 방지한다 -> 안전성!!
  • 타입 별칭
    • Typealias
      • 이후에 나오겠지만, 간략하게 내가 타입을 만드는 것이다.
        • ex) 나는 MyInt 라는 Int 타입을 만들고 싶어!
typealias MyInt: Int

라고 선언하면 MyInt라는 데이터 타입을 Int와 같이 사용 가능 하다

 

  • 튜플
    • 지정된 Data의 묶음
    • 선언 : var person: (String, Int, Double) = ("tree", 84, 183.0) 로 선언이 가능하다
      • 하지만 위 방식대로 선언시 String, Int, Double이 의미하는 것이 뭔지 알 수가 없다.
    • 선언-2 : var person: (name: String, weight: Int, height: Double) 로 선언도 가능하다

컬렉션

  • 배열(Array)
    • C언어의 배열처럼 Buffer
    • 크기가 자동조절된다.
    • 별도의 Stack, Queue가 존재하지 않아 Array를 사용하여 구현이 가능
    • 선언 : var names: [String] = ["tree", "randy"]
    • 선언-2 : var names: Array<String> = ["tree", "randy"]
    • first, last, append(), insert(), remove() 등 다양한 API를 통해 조작할 수 있다.
  • 딕셔너리(Dictionary)
    • 키, 값 으로 이뤄진 자료구조
    • 키는 중복이 불가능하다.
    • 다른 언어들의 Map과 유사하다
    • 선언: var dic: [String, Int] = ["weight": 84]
  • 세트(Set)
    • 순서없이 저장된다
    • 값 중복 불가
    • 해시 가능한 값을 ItemType으로 가질 수 있음
    • 선언 : var set: Set<Int> = [84]
    • 다른 언어들의 Set와 유사하다
  • 열거형(Enum)
    • 제한된 선택지를 줄 때 사용 가능
    • 정해진 값 외에는 입력받고 싶지 않을 때 주로 사용
    • 예상된 입력값들이 한정되어 있을 때 사용
    • 항목별로 값(rawValue)을 가질 수도 있다
    • 연관값(AssociatedValue)를 가질 수 있음
    • 주로 switch 블럭과 사용한다.
    • + Optional 또한 enum타입이다.
enum Optional<Wrapped> {
  case none // nil
  case some(Wrapped) // optional value
}

기본 열거형

enum School{
	case elementary
    case middle
    case high
    case university
}

var mySchool: School = School.middle
var yourSchool: School = .high

원시값(rawValue)

enum School: String{
	case elementary = "초등"
    case middle = "중등"
    case high = "고등"
    case university = "대"
}

var mySchool = .middle
print("\(mySchool.rawValue)") // "중등"

연관값

enum MainDish{
	case pasta(taste: String)
    case pizza(taste: String, dough: String)
    case rice
}

var dinner: MainDish = MainDish.pasta(taste: "로제")
dinner = .pizza(taste: "너무 맛나", dough: "쌀가루")

열거형 - 항목 순회

- 모든 case를 알아야할 때 사용 

- ex) for문을 돌 때도 사용 가능

- B.U.T. 연관값을 갖는 다면 사용 불가

enum School: CaseIterable{
	//CaseIterable 프로토콜을 채택
    case elementary
    case middle
    //....
}

let allCases: [School] = School.allCases

순환 열거형

- 열거형 항목의 연관 값이 열거형 자신의 값이고자 할 때 사용

- indirect 키워드를 사용

 


흐름 제어

조건문

  • if
    • 항상 true/false만 가능하다 -> 다른 언어에서의 0, 1, -1 등의 값을 넣을 수 없다
    • else 생략 가능
  • switch
    • break는 선택 사항
    • 이어서 실행하려면 fallthrough를 사용하면 된다
      • case가 0부터 5까지 있을 때, 다른 언어에서는 0 수행 블럭 끝에 break를 적지 않으면 1이 수행되었다
      • Swift는 반대라고 생각하면 된다. 적지 않아도 자동으로 break 동작을 한다
      • B.U.T. case 0을 수행하고 1을 수행하고 싶다면 0 블럭 끝에 fallthrough를 적어주면 된다.
    • default: 도 존재한다
    • switch에서 다양한 Swift 패턴들을 사용할 수 있다
    •  
// 1번째 예제
switch tuple{
	case ("tree", 183):
    	print(...)
    case (_, 183): // 와일드카드 패턴
    	print(...)
    case ("tree", let height): //값 바인딩 패턴
    	print(height) // height 사용 가능
}

// 2번째 예제

let 직급 = "사원"
let 연차 = 1
let 인턴 = false

switch 직급{
	case "사원" where 인턴 == true: //where는 뒤쪽에서 포스팅 하겠다
    // 직급이 사원이며, 인턴을 했다면
    // ....
    case "사원" where 연차 < 2 && 인턴 == false:
    // ....
}



반복문

  • for-in
for i in 0...10{
	print(i)
}

for char in str{
	print(char)
}
for(i in 0..10){
	println(i)
}

for(char in str){
	println(char)
}

위는 스위프트 아래는 코틀린 문법이다. 같은 동작을 한다

 

  • while
    • Boolean 타입을 사용해야 한다
  • repeat-while
    • 다른 언어에서의 do-while 문과 같다

 

야곰님의 Swift Programming을 공부하며 혼자 끄적였던 것들을 옮긴다.

Swift를 공부하는 다른 개발자들에게도 0.1만큼의 도움이 되었으면 좋을 것 같다는 마음으로 시작!

 

Swift

- 고차원적 언어

- ARC(자동 참조 카운팅)으로 메모리를 관리한다

  - 자바 기반의 GC와 비슷한 역할을 하지만, 전혀 다르다고 말할 수 있다. 이에 대해서는 이후에 작성할 것임

- Object-C의 동적 객체 모델과 매개변수를 도입했다

 

특징

  • 안전성
    • guard, 옵셔널, 오류처리 등으로 보다 안전하다
    • 타입에 대해 엄격하다 -> 개발자에게는 오히려 불편하지만, 프로그램 관점으로 본다면 안전하다
  • 신속성
  • FP(함수형 프로그래밍) 패러다임을 적용
  • POP(프로토콜 지향 프로그래밍)

함수형

  • 대규모 병렬처리에 유용하다
  • 상태의 변화없이 데이터를 처리할 수 있다
    • 함수형이 아니라면 포인터, 레퍼런스 등이 변경되어 함수 내부 처리에 영향을 준다
      • 즉, 순수함수를 작성하기 힘들다.
  • 함수를 일급시민으로 다룬다.
    • 전달인자(매개변수)로 전달 가능
    • 동적 프로퍼티로 할당 가능
    • 변수나 데이터 구조 내에 할당 가능
    • 반환 값으로 반환 가능
func sum(first: Int) -> (Int) -> Int{
	return { second in
    	return first+second
    }
}

sum(first:10)(5)

해당 메소드처럼 하나의 매개변수만 담을 수 있게 하는 기법을 커링이라 한다.

 

커링이란?

- 여러개의 매개변수를 갖는 함수를 매개변수 하나를 갖는 함수의 나열로 표현

 

프로토콜 지향 프로그래밍 POP

- 뒤~에서 설명

 

 

 

 

 

 

 

 

 

 

 

 

우선 Swift의 메모리 관리에 대해서 먼저 알아보자

-> 순서가 뒤죽박죽이긴 한데, 이후에 ARC에 대해서도 따로 적을 것이다.

 

Swift는 이전에 Object-C에서 파생된 언어이기 때문에, 메모리 관리를 비슷하게 따라가지만, 개발자에게 좀 더 편하게 바뀌었다.

 

MRC -> ARC 로 변화된 것이다.

이로써 개발자들은 일일히 객체들의 참조 카운트를 retain/release 할 필요가 없어졌다.

 

물론 ARC를 통해 모든 Dangling Pointer들이 처리되면 좋겠지만 아쉽게도 아니다. -> 이후에 적을 것...

 


알아보기로 한 autorelease를 알아보자.

AutoRelease란?

  • 참조 카운트를 나중으로 미루기 위한, 카운트가 나중에 감소되는 것을 보장받는 기법
  • 객체의 참조 카운트를 감소시킬 때, release 대신 autorelease를 사용하면 예약이 된다.
    • 실제로 release를 수행하면 카운트가 바로 줄지만, autorelease는 직후에는 감소되지 않는다.
  • autoRelease된 객체들은 autoReleasePool에 등록된다. -> 간단한게 쓰레드풀을 떠올리면 될 것 같다.
    • 이 autoRelease이 해제될 때, 내부의 객체들이 모두 release된다!!
    • 해당 autoreleasePool은 foundation Framework에 있다.
func useManyImages() {
    let filename = pathForResourceInBundle

    for _ in 0 ..< 5 {
        for _ in 0 ..< 1000 {
            let image = UIImage(contentsOfFile: filename)
        }    
    }
}

 

이미지를 5천번 가져오는 반복문을 예로 들어보자.

메소드가 종료되기 전까지는 생성된 5000개의 image가 메모리 공간에 유지되며 함수가 종료될 때, 해당 image들을 가르키는 참조 카운트가 줄어들며 release된다.

-> 즉, 함수가 끝나기 전까지 5000개의 이미지가 메모리 안에 존재한다는 말!! -> 만약 iOS 폰 내에서 많은 메모리를 사용하고 있고, 메모리가 부족하다면 원치 않게 App이 죽는 경우가 생길 수도 있다.

 

이러한 상황을 막기 위해 AutoRelease를 사용한다.

func useManyImages() {
    let filename = pathForResourceInBundle

    for _ in 0 ..< 5 {
      autoreleasepool {
        for _ in 0 ..< 1000 {
            let image = UIImage(contentsOfFile: filename)
        }   
      }
    }
}

똑같이 이미지를 5천번 가져오는 반복문이다.

 

이미지 1천번을 가져오는 for문을 autoRelease로 감싸고 해당 블럭을 5회 진행한다.

-> 

이미지 1000개를 메모리에 올리고, autoReleasePool이 비워지면서 해당 1000개가 메모리에서 내려가고

이미지 1000개를 메모리에 올리고, autoReleasePool이 비워지면서 해당 1000개가 메모리에서 내려가고

이미지 1000개를 메모리에 올리고, autoReleasePool이 비워지면서 해당 1000개가 메모리에서 내려가고

이미지 1000개를 메모리에 올리고, autoReleasePool이 비워지면서 해당 1000개가 메모리에서 내려가고

이미지 1000개를 메모리에 올리고, autoReleasePool이 비워지면서 해당 1000개가 메모리에서 내려가고

방식으로 작동할 것으로 예상된다.

 

메모리 사용량 그래프를 보면 쉽게 이해할 수 있다.

 

Not AutoRelease
AutoRelease

사용하지 않았을 때, 순간 메모리 사용량이 43MB을 사용했지만, 사용했을 때는, 순간 최고 사용량이 14MB로 현저히 낮은 것을 볼 수 있다.

물론 실제 코드에 사용된다면 내에서 해당 이미지들을 사용하고, 유지하기 위해 다른 참조카운트를 증가시키면 결과는 달라질 수 있지만

우선은 AutoReleasePool의 역할을 알아보았다.

 

참조 : stackoverflow.com/questions/25860942/is-it-necessary-to-use-autoreleasepool-in-a-swift-program

 

 

+ Recent posts