Swift/문법 및 이론

Swift : 기초문법 [인스턴스 #1 이니셜라이저, 매개변수)

서근 2022. 1. 14.
공지사항
사파리보다는크롬으로 접속을 권장합니다
사파리로 접속시 gif 파일이 표시되지 않을 수 있습니다

본 게시글은 yagom님의 Swift 프로그래밍 3판을 참고하여 작성되었습니다.

 

인스턴스 생성

이니셜라이저는 클래스, 구조체, 열거형 인스턴스를 사용하기 위해 준비 작업을 하는 단계이다. 이 단계에서 각 저장 프로퍼티의 초기값을 설정한다. 초기화 과정은 initializer를 정의하는 것으로 구현할 수 있다.

 

이렇게 구현된 이니셜라이저는 새로운 인스턴스를 생성할 수 있는 특별한 메서드가 된다. Swift의 이니셜라이저는 값을 반환하지 않는다. 이니셜라이저의 역할은 단지 첫 사용을 위해 초기화하는 작업만 한다. 또, 초기화와 반대로 여러 값과 자원의 해지를 위해 deinitializer도 사용할 수 있다.

 

이니셜라이저는 해당 타입의 새로운 인스턴스를 생성하기 위해 호출한다. 이니셜라이저는 func 키워드를 사용하지 않고 init 키워드를 사용하여 사니셜라이저 메서드임을 표현한다. (이 기능은 멤버와이즈 이니셜라이저에서 확인할 수 있습니다.)

 

구조체를 호출하기 위해서는 초기화 즉, 이니셜라이저 해야 한다. 목적 : 블루프린트 같은 struct를 복사해서 사용하기 위함

예를 들어 이러한 도표 위해 자동차를 그렸다고 생각하면 이제 이 자동차를 실제 객체로 바꿔야 한다.

class SomeClass {
   init() {
    // 초기화할 때 필요한 코드 작성
   }
}

struct SomeStruct {
   init() {
    // 초기화할 때 필요한 코드 작성
   }
}

enum SomeEnum {
  case someCase
  
  init() {
    // 열거형은 초기화시 반드시 case 중 하나가 되어야 한다.
    self = .someCase
    // 초기화할 때 필요한 코드 작성
  }
}
struct town {
    let name: String
    var family: [String]
    let description: [String: Int]
    
    init(name: String, myFamily: [String], description: [String: Int]) {
        self.name = name
        self.family = myFamily
        self.description = description
    }

    func someMethod() {
        print("struct안에 func키워가 있다면 그것은 메서드 이다.")
    }
}

var myTown = town(name: "서근", myFamily: ["미진", "초코", "희진"], description: ["미진": 19, "희진": 29, "초코": 6])
myTown.family.append("루키")
print(myTown.family)  //["미진", "초코", "희진", "루키"]


var ghostTown = town(name: "처녀귀신", myFamily: [], description: ["달걀귀신": 100, "처녀귀신": 120])
ghostTown.someMethod() // struct안에 func키워가 있다면 그것은 메서드 이다.

클래스, 구조체, 열거형 등의 구현부 또는 해당 타입 익스텐션 구현부 쪽에 위치하는데, 클래스의 지정 이니셜라이저는 익스텐션에서 구현해줄 수 없다. 

 

위 코드에서 주의 깊게 봐야할것은 열거형 enum에서 이니셜라이저를 지정하기 위해서는 반드시 case 중 하나가 되어야 한다는 점이다.

프로퍼티 기본값

구조체, 클래스의 인스턴스는 처음 생성 시 옵셔널 저장 프로퍼티를 제외한 모든 저장 프로퍼티에 초깃값을 할당해야 한다. 이니셜라이저가 실행될 때 저장 프로퍼티에 초깃값을 할당할 수 있다. 그러므로 초기화 후, 값이 할당되어 있지 않으면 저장 프로퍼티는 존재할 수 없는 것이다. 

 

프로퍼티를 정의할 때 프로퍼티 기본값을 할당하게 되면 이니셜라이저에서 따로 초깃값을 할당하지 않아도 프로퍼티 기본값으로 저장 프로퍼티 값이 초기화된다.

// 이니셜라이저에서 기본값 할당

struct Person {
   var name: String
   
   init() {
     name = "서근 개발블로그" // 이니셜라이저에 초깃값을 할당했다.
   }
}

let blog: Person = Person()  //새로운 인스턴스 생성
print(blog.name) // "서근 개발블로그"
// 저장 프로퍼티에 기본값 할당

struct Person {
   var name: String = "서근 개발블로그" // 저장프로퍼티에 기본값을 할당했다.
}

let blog: Person = Person()  //새로운 인스턴스 생성
print(blog.name) // "서근 개발블로그"

TIP
 
 

초기화와 프로퍼티 옵저버
이니셜라이저를 통해 초깃값을 할당하거나, 프로퍼티 값을 통해 처음의 저장 프로퍼티가 초기화될 때에는 프로퍼티 옵셔널 메서드가 호출되지 않는다.

다음 예제는 구조체인 User 이라는 프로퍼티를 선언하고 이니설라이저에 초기화하는 코드이다.

struct User {
    var username: String
    }
    
var user = User(username: "서근")
print(user.username)
//서근

이렇게 기본적인 구조체로 사용자 이름을 제공했는데, 여기서 기본 이니셜라이저를 교체(초기화) 할 수 있다.

struct User {
    var username: String
    
    init() {
        //USer의 초기값을 설정해준다.
        username = "서근"
        print("새로운 회원입니다.")
    }
}

이니셜라이저를 쓰기 전에 func키워드를 쓰진 않지만, 이니셜라이저가 끝나기 전에 모든 프로퍼티가 있는지 확인해야 한다.

var user = User()
print(user.username)
//새로운 회원입니다.
//서근

이니셜라이저 매개변수

함수나 메서드를 정의할 때처럼 이니셜라이저도 매개변수를 가질 수 있다. 인스턴스를 초기화하는 과정에 필요한 값을 전달받을 수 있다는 의미이기도 하다.

struct Person {
  var name: String
   
  init(Myname my: String) {
  name = my + " 입니다!"
  }
  
  init(YourName name: String) {  //self.name은 저장프로퍼티의 name을 지정함
  self.name = name + " 입니다"     //name + " 입니다" 의 name은 전달인자 레이블을 지정함
  }
  
  init(value: String) {
  name = value
  }
  
  init(_ value: String) {
  name = value
  }
}

let first: Person = Person(Myname: "서근")
print(first.name) //서근

let second: Person = Person(YourName: "철수")
print(second.name) //철수 입니다

let third: Person = Person(value: "유리")
print(third.name) //유리

let fourth: Person = Person("훈이")
print(fourth.name) //훈이

첫 번째 이니셜라이저는 name 프로퍼티에 할당하는 이니셜라이저와 String을 입력받아서 name 프로퍼티에 할당하는 이니셜라이저이다.

 

두 번째 이니셜라이저는 self 프로퍼티(self.name)를 사용하여 이니셜라이저의 전달 인자 레이블인 name과 저장 프로퍼티인 name을 구준비 었다.

 

세 번째 이니셜라이저는 전달 인자 레이블을 사용하지 않고, value라는 이름의 매개변수가 있으므로 자동으로 지정되는 전달 인자 레이블 value가 필요하지 않다면 value를 사용하여 전달 인자 레이블을 없애줄 수 있다.

옵셔널 프로퍼티 타입

저장 프로퍼티에서도 한 번 나온 적이 있는데 다시 한번 보자면, 인스턴스가 사용되는 동안 값을 꼭 갖지 않아도 되는 저장 프로퍼티가 있다면 해당 프로퍼티를 옵셔널로 선언할 수 있다. 또는 초기화 과정에서 값을 지정하기 애매하거나 어려운 경우 옵셔널로 선언할 수 있다.

 

옵셔널로 선언한 저장 프로퍼티는 초기화 과정에서 값을 할당해주지 않는다면 자동으로 nil이 할당된다.

 

구조체 - 저장 프로퍼티가 옵셔널이 아니어도 이니셜 라이저를자동 생성함

클래스 - 옵셔널이 아니면 기본값을 지정해주거나 이니셜라이저를 통해 반드시 초기화해줘야 함

 

class Person {
  var name: String
  var height: Int?
  //height는 옵셔널 이기 때문에 초기화할 필요가 없다.
  init(name: String) {
    self.name = name
  }
}

//새로운 인스턴스 생성
let Seogun: Person = Person(name: "서근")
//height을 값을 알게 되었을때 값을 할당해 줄 수 있다.
Seogun.height = 200
Seogun.name = "철수"

위 코드의 이니셜라이저에서 초기화해주지 않았지만 자동으로 nil이 할당되어 있다.

상수 프로퍼티

위 코드를 다시 보면 Seogun.name 이 "철수"로 값이 변경되었는데, 이렇게 되면 전혀 다른 사람이 되어버리는 경우가 생긴다. 이를 방지하기 위해서는 저장 프로퍼티인 namevar 변수가 아닌 let 상수로 할당해준다면 해결 가능하다. 이때 유의해야 할 점이 있는데, 상수로 선언된 저장 프로퍼티는 인스턴스를 초기화하는 과정에서만 변경이 가능하며, 처음 할당된 후 에는 값 변경이 불가능하다.

class Person {
  let name: String
  init(name: String) {
    self.name = name
  }
}

//새로운 인스턴스 생성. 상수 저장프로퍼티 값을 "서근" 으로 할당
let Seogun: Person = Person(name: "서근")

//error: name은 상수 프로퍼티이기때문에 값을 변경할수 없다.
Seogun.name = "철수"

TIP
 
 

상수 프로퍼티와 상속
클래스 인스턴스의 상수 프로퍼티는 프로퍼티가 정의된 클래스에서만 초기화 가능하다. 해당 클래스를 상속받은 자식클래스의 이니셜라이저에서는 부모클래스의 상수 프로퍼티값을 초기화할 수 없다.

 

이니셜라이저 TEST :  문제를 풀려면 이곳을 클릭해주세요.

 

 

읽어주셔서 감사합니다🤟

 

 

728x90
  • 이전글
  • 다음글