오늘은 스태틱에 대해서 알아볼건데요.
처음 스태틱을 보고 떠올렸던건 다름아닌.. 롤인데요..
잡소리 컷하고 바로 본론으로 들어가봅시다.
Swift는 method와 property에 static 접두사를 사용해서 인스턴스가 아닌 타입(선언된 타입)과 연결할 수 있도록 합니다.
static 이 붙으면 타입
이라는 용어가 추가됩니다.
이 키워드를 사용한 프로퍼티나 메소드는 다른 인스턴스를 찍어내더라도 호출되지않고, 변경되지 않습니다.
때문에 타입자체에 속한 속성이라서 이렇게 칭한다고 합니다.
class Hanabi {
//타입속성
static let language = "Swift"
//타입 메서드
static func work(){
print("\(language)로 APP을 만듭니다")
}
}
static property의 가장 일반적인 사용 사례는 환경 설정(Configuration)입니다.
static 키워드를 사용해서 설정(색상, 폰트)들을 관리하는 이유는 namespace(함수, 변수 등과 같은 내부 식별자에 범위를 제공하는 선언적 영역, 유효 범위를 제공하는 영역)를 제공하기 때문입니다.
일반적인 예시는 스타일 가이드로 사용하는 것입니다.
만약 특정 색상이나 글꼴을 변경하고 싶을 때 프로젝트를 모두 뜯어봐야 한다면 비효율적이고 정확하지 않을 것입니다. (빠지는 부분들이 있기 때문에)
따라서, 코드 전체에 값을 분산시키는 것보다 한 곳에서 색상, 글꼴 폰트 및 크기 등을 정의하는 것이 좋습니다.
enum AppStyles {
enum Colors {
static let mainColor = UIColor(red: 1, green: 0.2, blue: 0.2, alpha: 1)
static let darkAccent = UIColor(red: 0.2, green: 0.2, blue: 0.2, alpha: 1)
}
enum FontSizes {
static let small: CGFloat = 12
static let medium: CGFloat = 14
static let large: CGFloat = 18
static let xlarge: CGFloat = 21
}
}
위와 같이 한 곳에서 관리할 경우, 메인 컬러를 수정해야 하는 상황에서 한 부분만 변경
하면 됩니다.
(위의 코드에서 열거형을 통해서 static property를 사용한 이유는 설정 객체를 인스턴스로 만들지 못하도록 하기 위함입니다.
구조체에서 private initializer를 사용해도 똑같이 동작하지만, 열거형은 initializer를 갖고 있지 않기 때문에 보다 적합한 방법이라고 할 수 있다.)
static property를 통해서 앱에서 사용되는 제한된 문자열 집합을 정의할 수 있고
(ex, UserDefaults의 키 값, Notification Center를 통해 전송되는 알림의 이름 등 .. )
앱 전체에서 일정하게 유지되어야 하는 전역 설정을 configuration object를 전달할 필요 없이 사용할 수 있습니다.
static property의 또 다른 용도는 그것들을 캐시로 사용할 수 있다는 것이다. 특정 object를 생성하는 것은 많은 비용이 들 수 있습니다.
예를 들면, dateformatter가 있는데요.
(글에서는 값 비싼 객체의 좋은 예시로 dateformatter가 있다고 표현)
struct Hanabipost {
private static var dateFormatter = ISO8601DateFormatter()
let publishDate: Date?
// other properties ...
init(_ publishDateString: String /* other properties */) {
self.publishDate = Hanabipost.dateFormatter.date(from: publishDateString)
}
}
Hanabipost 인스턴스를 얼마나 만드는 것과 상관없이 string을 변환하는 dateFormatter는 언제나 ISO8601DateFormatter 가 됩니다.
만약, dateFormatter를 static으로 선언하여 type과 연관시키지 않고 인스턴스와 연관시켰다면, (아무런 키워드 없이 작성)
Hanabipost 인스턴스가 생성될 때마다 dateFormatter도 같이 생성하게 됩니다.
같은 역할을 하는 dateFormatter에 대해서 인스턴스가 생기는 것과 함께 중복적으로 만들어지는 것은 낭비적인 일입니다.
따라서 생성하는데 비용이 많이 들면서 안전하게 재사용 될 수 있는 object에 대해서는 정적으로 정의하는 것이 좋습니다.
그러면 한번만 생성하고 사용할 수 있겠죠?
프로그래밍에서 일반적인 패턴은 팩토리 패턴이 있습니다.
factory는 대상 object의 초기화에 대한 특정 세부사항을 숨기면서도 간단하게 object를 만들 수 있습니다.
enum HanabiFactory {
static func create(withTitle title: String, body: String) -> Hanabipost {
let metadata = Metadata(/* metadata properties */)
return Hanabipost(title: title, body: body, createdAt: Date(), metadata: metadata)
}
}
위와 같이 코드를 작성했다면,
HanabiFactory를 통해 Hanabipost의 새로운 인스턴스를 간단하게 만들 수 있습니다.
결론부터 말하면 하위 클래스(상속 받은 클래스)가 클래스 메서드를 재정의 할 수 있다는 점이 주된 차이점입니다.
class SomeClass {
class func date(from string: String) -> Date {
return ISO8601DateFormatter().date(from: string)!
}
}
class SubClass: SomeClass {
override class func date(from string: String) -> Date {
return DateFormatter().date(from: string)!
}
}
위와 같인 date함수에 대해서 하위 클래스가 재정의 하는 경우, 이러한 작업은 class method에서만 가능하고 static method에서는 불가능합니다.
즉, static 키워드를 통해서
✅ 설정을 관리하거나
✅ 비싼 object에 static property를 사용해서 보다 효율적으로 코드를 작성할 수 있다는 것과
✅ 간단한 factory 패턴을 구현할 수 있다는 것을 알 수 있습니다.