[Swift] 놓치고 있던 기초 문법 2

‍deprecated·2021년 9월 15일
0

Swift 시작하기

목록 보기
10/12

init

생성자의 파라미터의 이름 또는 개수가 달라지면 여러 개의 생성자 작성이 가능하다.

class User {
    var name:String
    var age:Int
    init(name:String, age:Int){
        self.name = name
        self.age = age
    }
    init(name2:String, age2:Int){
        self.name = name2
        self.age = age2
    }
    init(age:Int){
        self.name = "albert"
        self.age = age
        print("init called")
    }
    deinit{
        print("deinit user")
    }
}
var user = User(name: "gu", age: 23) // 매개변수로 전달
var user2 = User(age: 24)
user2.name
user2.age

존재도 몰랐던 deinit

생성자가 있으면 당연히 소멸자가 있어야 했다 😂
deinitializer는 인스턴스가 메모리에서 해제되기 직전에 실행되며, 클래스의 인스턴스에만 구현 가능하다.

var user3:User? = User(age: 23)
user3 = nil // deinit user 출력
user3 = User(age:88) // init called 출력

스위프트는 인스턴스가 더 이상 필요하지 않으면 자동으로 메모리에서 소멸시킨다. user3에 nil을 넣으면 인스턴스가 더 이상 필요하지 않다고 판단하여 deinit을 호출한다.
deinitializer는 인스턴스 할당 해제 전에 자동으로 호출되며, 수동으로 호출할 수 없다.
deinitializer가 호출될 때까지 인스턴스의 할당 해제는 불가능하기에, 인스턴스의 모든 속성에 접근할 수 있으며, 이들 속성을 가지고 변경 작업을 호출할 수 있다 (가령 파일이 닫힐 때 파일 이름을 찾는 동작). 참고

프로퍼티

저장 프로퍼티

구조체, 클래스에서만 사용.
인스턴스의 변수 또는 상수를 의미한다.

구조체는 value 타입

struct Cat {
    var name:String
    let gender:String // 이들을 저장 프로퍼티라고 함
}

var cat = Cat(name: "hoho", gender: "female")
print(cat)
cat.name = "kaka" // 가능.
// cat.gender = "male"  // 불가. 상수이므로 값 변경 불가

let cat2 = Cat(name: "yaya", gender: "female")
// cat2.name = "yoon" // name이 변수여도 구조체 인스턴스가 상수로 선언되어 변경 불가함.

구조체는 value 타입이기에 상수로 인스턴스를 선언하게 되면 프로퍼티를 변수로 선언해도 변경 안 됨

클래스는 참조 타입

class Lion {
    var name:String
    let gender:String
    init(name:String, gender:String){
        self.name = name
        self.gender = gender
    }
}

let Lion1 = Lion(name: "brian", gender: "female")
Lion1.name = "gugu" // 가능.
print(Lion1.name) // 변경됨.

클래스는 참조타입이기에 인스턴스를 상수로 선언해도 프로퍼티의 값 변경이 가능

연산 프로퍼티

클래스, 구조체, 열거형에서 사용 가능.
값을 저장하는 것이 아니라, 특정 연산을 수행하는 결과값을 의미.
getter, setter를 사용하여 다른 property와 값들을 직접 접근이 가능하다.
set 없이 get만 쓸 경우 읽기 전용 프로퍼티이므로 값 변경이 불가하다.
set에 매개변수 이름을 설정 안 할 경우 default 값인 newValue로 지정된다.

struct Stock{
    var averagePrice: Int
    var quantity: Int
    var purchasePrice:Int {
        get {
            return averagePrice*quantity
        }
        set(newPrice){ // purchaseprice 변경시
            averagePrice = newPrice / quantity
        }
    }
}

var stock = Stock(averagePrice: 2000, quantity: 6)
print(stock)
stock.purchasePrice // get 안의 코드블럭이 실행됨
stock.purchasePrice = 3000 // 변경 시 set 안 코드블럭이 실행됨
stock.averagePrice

프로퍼티 옵저버

프로퍼티 값의 변화를 관찰하고 반응한다.
새로운 값이 기존 값과 같아도 호출된다. = set 될때마다 호출
세 가지 경우에만 사용 가능.
1. 저장 프로퍼티
2, 3. 오버라이딩된 저장, 연산 프로퍼티


class Account{
    var credit:Int = 0 {
        willSet {//값이 저장되기 직전에 실행. 새로 저장된 프로퍼티값이 상수 매개변수로 지정. 지정 않으면 기본값은 newValue
            print("잔액이 \(credit)에서 \(newValue)로 변경될 예정입니다.")
        }
        didSet { // 값이 지정된 직후에 실행. 기존값이 상수 매개변수로 지정. 지정하지 않으면 디폴트는 oldValue이 매개변수 이름이 됨
            print("잔액이 \(oldValue)에서 \(credit)으로 변경되었습니다")
        }
    }
}
var account = Account()
account.credit = 1000

결과:
잔액이 0에서 1000로 변경될 예정입니다.
잔액이 0에서 1000으로 변경되었습니다.

타입 프로퍼티

특정 인스턴스에서 사용되는 게 아닌, 특정 타입에서 사용되는 프로퍼티
인스턴스 생성 없이 객체 내 프로퍼티에 접근 가능
따라서 변수에 클래스를 할당해 인스턴스 생성 않고도 프로퍼티 사용 가능하다. static을 사용해 선언한다.
모든 타입이 공통적인 값을 정의하는데 유용하다.

struct SomeStructure {
    static var storedTypeProperty = "some value" // 저장 타입 프로퍼티
    static var computedTypeProperty: Int { // 연산 타입 프로퍼티
        return 1
    }
}
SomeStructure.computedTypeProperty // 타입 이름을 통해서만 접근 가능
SomeStructure.storedTypeProperty
SomeStructure.storedTypeProperty = "hello"
SomeStructure.storedTypeProperty

타입 프로퍼티는 최초 호출시에 메모리에 한번 올라가고 그 후엔 어디서든 해당 프로퍼티를 공유한다. = 인스턴스 생성과 무관하다. = initializer와 상관이 없다. = 초기값이 없을 경우 초기값 세팅할 방법이 없기에 반드시 초기값이 주어져야 한다.

기존 속성이 lazy이기에 지금 예시에선 someStructure.computedTypeProperty를 호출했을 때에야 비로소 메모리에 올라간다.

연산 타입 프로퍼티의 오버라이딩

연산 타입 프로퍼티는 subclass에서 오버라이딩 가능함. 참고한 자료

class : 오버라이딩 가능한 연산 타입 프로퍼티

class Human {
    class var alias: String {
        return "Human Type Property"
    }
}
 
class Sodeul: Human {
    override class var alias: String {
        return "Sodeul Type Property"
    }
}
Human.alias             // "Human Type Property"
Sodeul.alias            // "Sodeul Type Property"

위와 같이 class를 선언한 연산 프로퍼티는 static 선언과 마찬가지로 연산 타입 프로퍼티이다.
하지만 subclass에서 위처럼 연산 타입 프로퍼티를 오버라이딩해서 사용이 가능하다.

static : 오버라이딩이 불가능한 연산 타입 프로퍼티

class Human {
    static var alias: String {
        return "Human Type Property"
    }
}
 
class Sodeul: Human {
    override static var alias: String {     // error! Cannot override static property
        return "Sodeul Type Property"
    }
}

static으로 선언할 경우 오버라이딩이 불가능하다.

클래스와 구조체

공통점

  1. 값을 저장할 프로퍼티 선언 가능
  2. 함수적 기능을 하는 메서드를 선언할 수 있음.
  3. 내부 값에 .을 사용하여 접근 가능
  4. 생성자를 사용해 초기 상태를 설정할 수 있음.
  5. extension을 사용하여 기능 확장 가능
  6. Protocol을 채택하여 기능 설정 가능

차이점

클래스

  1. 참조 타입 → 메모리 스택영역에는 포인터, 인스턴스의 메모리 주소만 할당된다. ㄷ실제 데이터는 힙 영역에 할당된다. 따라서
  2. ARC로 메모리 관리
  3. 상속 가능
  4. 타입 캐스팅을 통해 런타임에서 클래스 인스턴스의 타입 확인 가능. 제드님이 기가 맥히게 설명하시더라. 타입캐스팅은 인스턴스의 타입을 확인하거나(is) 클래스의 인스턴스를 부모 혹은 자식 클래스 타입으로 사용할 수 있는지 확인하는 용도(as)로 사용한다.
  5. deinit을 사용하여 클래스 인스턴스의 메모리 할당 해제 가능
  6. 같은 클래스 인스턴스를 여러 개의 변수에 할당한 뒤 값을 변경하면 모든 변수에 영향을 준다.

구조체

  1. 값 타입 → 변수를 할당하면 스택 영역에 값이 저장된다. 따라서 ARC로 메모리 관리가 안 된다.
  2. 구조체 변수를 새로운 변수에 할당할 때마다 새로운 구조체가 할당된다. 즉 값 자체를 복사 하는 것. 같은 구조체를 여러 개의 변수에 할당한 뒤 값을 변경시켜도 다른 변수에 영향을 주지 않는다.
  3. 상속 불가.
class TempClass {
    var count:Int = 0
}
class TempStruct {
    var count:Int = 0
}
// - 클래스
var class1 = TempClass()
var class2 = class1
var class3 = class1

class3.count = 2
class1.count // 2임. 변수를 복사하더라도 하나의 주소값을 가리키기에

// - 구조체
var struct1 = TempStruct()
var struct2 = struct1
var struct3 = struct2

struct2.count = 3
struct3.count = 4
struct1.count // 0
struct2.count // 3
struct3.count // 4. 값 타입이기에 같은 구조체 인스턴스를 할당하면 매번 새로운 메모리가 할당되기에
profile
deprecated

0개의 댓글