computedProperty

스위프트에서 변수는 property라고도 불리운다. 즉 연산용 변수라고 보면 된다. 

var foo: Double{
     get{
          //return calculater value
     }
     set(newValue){
          //foo has changed to newValue
     }
}

get에 해당하는 영역에는 변수 foo의 값을 불러올 때 사용되는 코드를, set에는 변수 foo에 새로운 값을 대입할 때 동작할 코드를 적어주면 된다. newValue는 대입할 경우에 들어올 새로운 값을 의미한다. 당연히 괄호안의 이름을 바꾸면 해당 이름의 변수로 사용이 가능하다. 생략이 가능하며 생략시에는 무조건 newValue로 사용해야한다.

var temp = 1
var x:Int {
    //x를 호출할 경우 temp를 반환
    get{
        return temp
    }
    
    //x에 대입할 경우 두배의 값을 temp에 대입
    set(newValue){
        temp = newValue * 2
    }
}

//x에 10을 대입(set) -> temp에 20이 저장된다.
x = 10

//x호출(get) -> temp의 값인 20이 출력된다.
print(x)

예제로 입력 값의 두배를 불러오는 computedProperty를 작성해보았다. 주의할 점은 return을 위한 임시변수가 필요하다는 점이다. 즉 x는 저장을 위한 변수가 아닌 말그대로 연산을 위한 변수인 것이다. 

 

아래 예제는 지난번 데모에 computedProperty를 적용한 부분이다. 

    //유일하게 앞면 상태의 카드를 나타내는 속성 이후에 두번째로 오픈되는 카드와 id를 비교하기위해 사용된다.
    var indexOfOneAndOnlyFaceupCard:Int? {
        //indexOfOneAndOnlyFaceupCard 호출시
        get{
            //리턴을 위한 임시 변수 생성. optional로 생성한다.
            var foundIndex:Int?
            
            //카드배열을 돌면서 앞면상태인 카드를 탐색한다.
            for index in cards.indices{
                if cards[index].isFaceUp{
                    //앞면인 카드발견 + foundIndex가 nil, 즉 앞면인 카드가 오로지 한장이라면,
                    if foundIndex == nil {
                        //앞면이 유일한 카드는 해당 카드가 된다.
                        foundIndex = index
                    } else{
                        //foundIndex에 값이 있는경우, 즉 앞면인 카드가 한장보다 많다면, 유일하게 앞면인 상태가 아니므로 nil을 반환한다.
                        return nil
                    }
                }
            }
            //foundIndex의 값으로 indexOfOneAndOnlyFaceupCard설정
            return foundIndex
        }
        //indexOfOneAndOnlyFaceupCard 대입시
        set{
            //카드 배열을 돌면서 카드의 앞면, 뒷면을 설정한다.
            for index in cards.indices{
                //사용자가 선택한 카드번호(nexValue)가 아닌 나머지 카드들은 모두 뒷면으로 설정한다.
                cards[index].isFaceUp = (index == newValue)
            }
        }
    }

Model에 해당하는 Concentration 클래스의 indexOfOneAndOnlyFaceupCard 변수이다. 해당 작업으로 인해 호출부분의 코드가 조금 더 간결해졌다.

추가로 computedProperty는 get만 설정하는것이 가능하다. 

//게임 생성. 카드 쌍의 수는 카드버튼 갯수의 절반이다.
lazy var game = Concentration(numberOfPairsOfCards: numberOfPairsOfCards)
    
var numberOfPairsOfCards: Int{
    //numberOfPairsOfCards 호출시 cardButtons배열 길이의 절반 반환
    return cardButtons.count+1/2
}

get만 설정할 경우 예제와 같이 return만 적는 것이 가능하다. 하지만 반대로 set만 설정하는 것은 불가능하다.


accessControl

접근제어. 아직 작성중인 클래스에 접근 가능역역을 표시하는 작업이다.

internal(default) - 같은 앱 내에서 어떤 객체든 접근 가능한 상태이다 .기본값이라 생략이 되어있다. 앱이나 프레이워크의 모든 요소가 접근이 가능한 상태이다.

private - 다른 앱에서 접근 불가능한 상태이다. 

private(set) - 변수를 위한 옵션이다. 다은 사람이 값에 접근이 가능하지만 설정은 할 수 없다. (읽기전용)

fileprivate - 해당 파일 안에 있는 어떠 것이든 서로 접근가능한 상태.

 

프레임 워크에만 사용하는 접근제어

public - 외부에서 접근 가능

open - 외부에서 접근 가능 + 서브클래스 생성 등 여러가지 수정 및 추가가 가능

 

*공유할 일이 없으면 비공개처리를 해두는 것이 좋다. 

아울렛 변수의 경우 공유할 경우가 없으니 모두 비공개로 하자.


extension

클래스에 함수 및 속성(변수)의 추가

extension Int{
    //arc4Random함수 추가. 기존의 UInt32의 자료형을 출력하는 함수를 Int형으로 반환하게 수정하였다.
    func arc4Random() -> Int{
        assert(self > 0, "it has to be more than 0")
        return Int(arc4random_uniform(UInt32(self)))
    }
}

assert(condition:Bool, message:String)함수는 조건식을 체크하고, 조건식이 맞지 않으면(false), 오류메세지(String)를 출력한다.

Int 클래스에 arc4random()함수를 추가하였으며, 해당 함수는 0보다 큰 정수만 입력받으므로 assert()함수로 조건을 체크한다. 

이렇게 추가를 하게되면 기존의 지저분하게 보이던 코드를 정리할 수 있다.

extension Int{
    var arc4random: Int{
        if self > 0{
            //0보다 큰 정수라면, 0과 정수사이의 랜덤 숫자 반환
            return Int(arc4random_uniform(UInt32(self)))
        } else if self < 0 {
            //0보다 작은 정수라면, 0과 음수사이의 랜덤 숫자 반환
            return -Int(arc4random_uniform(UInt32(abs(self))))
        } else {
            //0이라면, 0반환
            return 0
        }
    }
}

해당 함수는 변수로도 설정이 가능하며, arc4random()함수의 경우 0보다 큰 정수만 입력받는것을 보완하여 추가하였다.

아래의 코드처럼 호출부가 더욱 간결해졌다.

//기존의 두줄이었던 코드
let randomIndex = Int(arc4random_uniform(UInt32(emojiChoices.count)))
emoji[card.identifier] = emojiChoices.remove(at: randomIndex)

//Int클래스에 arc4random을 추가한 후
emoji[card.identifier] = emojiChoices.remove(at: emojiChoices.count.arc4random)

enum

열거형 데이터타입이다. 가질 수 있는 값들을 미리 지정해서 사용하는 것이 특징이다.

enum fastFoodMenuItem{
    case hamburger
    case fries
    case drink
    case cookies
}

//fastFoodMenuItem이라는 자료형을 사용할 수 있다.
var burger:fastFoodMenuItem = .hamburger

if burger == .hamburger{
    print("it is hamburger")
}

사진처럼 지정된 값들만 입력이 가능하며 좌변에 fastFoodMenuItem이라 명시를 해줘서 우변에는 타입추론이 가능하다.

타입추론을 지원하지 않는다면 var burger:fastFoodMenuItem = fastFoodMenuItem.hamburger 라고 적어야 했다.

enum fastFoodMenuItem{
    case hamburger
    case fries
    case drink
    case cookie
    
    func showMenu() {
        switch self{
        case .hamburger : print("hamburger")
        case .fries : print("fries")
        case .cookie : print("cookie")
        case .drink : print("drink")
        }
    }
}

//메뉴 생성
var menu0: fastFoodMenuItem = .hamburger

//메뉴 출력
menu0.showMenu()

함수 또한 설정이 가능하며 예시와 같이 해당 메뉴가 무엇인지 출력하는 코드를 작성해보았다.

enum fastFoodMenuItem{
	//연동자료 선언
    case hamburger(numberOfPatties: Int)
    //당연히 연동자료를 enum으로 선언이 가능하다.
    case fries(size: fryNumberOfSize)
    case drink(String, ounces:Int)
    case cookie
    
    func showMenu() {
        switch self{
        //연동자료를 사용하기 위해서는 let을 이용하면 된다. 정해진 연동자료보다 많이 선언하면 오류가 출력된다.
        case .hamburger(let pattyCount) : print("hamburger with \(pattyCount) patties.")
        case .fries(let size) : print("fries \(size) size.")
        case .cookie : print("cookie.")
        case .drink(let brand, let ounces) : print("drink. a \(ounces)oz \(brand)")
        }
    }
}

enum fryNumberOfSize{
    case large
    case small
}

var menu0: fastFoodMenuItem = .hamburger(numberOfPatties: 2)
var menu1: fastFoodMenuItem = .fries(size: .large)
var menu2 = fastFoodMenuItem.drink("sprite", ounces: 13)

menu0.showMenu()
menu1.showMenu()
menu2.showMenu()

mutating func changeTo(menu:fastFoodMenuItem){
        self = menu
    }

self를 사용하여 값의 변경이 가능하다. 유의할 사항은 mutating인데, 간단히 말하면 값의 변경이 일어난 다는 것을 스위프트에게 알리는 문구라고 한다.

var menu0: fastFoodMenuItem = .hamburger(numberOfPatties: 2)
menu0.showMenu()

menu0.changeTo(menu: .fries(size: .small))
menu0.showMenu()

해당 함수 호출로 메뉴를 변경하였다.


optional

스위프트를 접하고 가장 궁금했던 부분이 옵셔널이다. 이 옵셔널이 결국 enum이다. 

enum optional<T>{ //a generic type like Array<Element>, Dictionary<Key,Value>
    case none
    case some(<T>)
}

<T>의 의미는 어떤 형태든 들어올 수 있다는 얘기다. 

값이 존재하지 않으면 .none, 어떠한 값이 존재하면 .some()을 통해서 switch을 통해 우리가 알고있는 옵셔널로 동작하게 되는것이다. 

상수 y를 보면 x, foo(), bar, z가 모두 옵셔널의 형태로 연결되어 하나라도 nil이라면, nil을 반환하게 되는 구조이다. 이러한 구조를 옵셔널 체인이라고 부른다.


automaticReferenceCounting

자바는 가비지 컬렉션을 통해 힙메모리를 관리한다. 반면 스위프트는 가비지 컬렉션이 아닌 자동참조횟수정책을 이용하여 힙메모리를 관리한다. 힙내의 참조타입에 포인터를 만들때마다 스위프트는 어딘가에 있는 카운터에 1을 추가. 해당 포인터가 가리키는것이 없어지거나, 더이상 가리키지 않게 되었을 때 ,카운트를 줄이면서 0이 되면 힙메모리에서 해당하는 참조타입을 제거하게 된다. 

influencing ARC

참조횟수계산에 영향을 미치는 3가지 방식의 포인터가 있다. 

strong

일반적인 참조횟수계산방식이다. 포인터가 무언가를 가리키는 한 힙내에 계속 놔둔다.

weak

힙내의 어떤것을 가리키지만 가리키는 대상이 strong 포인터를 가지고 있으면 힙내에 유지, 더이상 strong 포인터를 가지고 있지 않게되면 해당 포인터에는 nil을 반환하고 가리키는 대상은 힙메모리에서 제거한다. (ex. optional, oulet)

unowned

참조횟수계산에 참여하지 않는다. 힙내부의 어떤 것을 가리키고 있을 때, strong포인터로서 인식하지 않는다. 가리키고 있는 대상이 힙내에서 사라졌을때, 해당 포인터에 접근하지 않는다. → 메모리 사이클을 피하기 위해 사용한다.

*메모리사이클: 힙 메모리내에서 서로를 참조하여 힙 메모리내에 계속해서 남게되는 일

'iOS > Stanford' 카테고리의 다른 글

day06_multiTouch  (0) 2022.03.26
day05_view  (0) 2021.12.30
day04_swift_part2  (0) 2021.10.25
day02_MVC  (0) 2021.10.03
day01_ios  (0) 2021.09.28

+ Recent posts