Notice
Recent Posts
Recent Comments
Link
관리 메뉴

김종권의 iOS 앱 개발 알아가기

[iOS - swift] OptionSet으로 flag 리펙토링하기 (플래그 관리, Bool대신 bit 사용하기) 본문

iOS 응용 (swift)

[iOS - swift] OptionSet으로 flag 리펙토링하기 (플래그 관리, Bool대신 bit 사용하기)

jake-kim 2024. 9. 27. 01:24

flag 관리하는 방법

  • 유사한 성격의 flag를 관리할때 보통 아래처럼 작성

ex) 앱 유저 세팅에 관한 플래그를 관리하는 클래스

class UserSettings {
    var isDarkModeEnabled: Bool
    var isNotificationsEnabled: Bool
    var isLocationServicesEnabled: Bool
    var isAutoUpdateEnabled: Bool
    var isPrivacyModeEnabled: Bool
    var isSoundEnabled: Bool
    var isVibrationEnabled: Bool
    var isEmailUpdatesEnabled: Bool
    var isTwoFactorAuthenticationEnabled: Bool

    init(
        isDarkModeEnabled: Bool = false,
        isNotificationsEnabled: Bool = true,
        isLocationServicesEnabled: Bool = false,
        isAutoUpdateEnabled: Bool = true,
        isPrivacyModeEnabled: Bool = false,
        isSoundEnabled: Bool = true,
        isVibrationEnabled: Bool = false,
        isEmailUpdatesEnabled: Bool = true,
        isTwoFactorAuthenticationEnabled: Bool = false
    ) {
        self.isDarkModeEnabled = isDarkModeEnabled
        self.isNotificationsEnabled = isNotificationsEnabled
        self.isLocationServicesEnabled = isLocationServicesEnabled
        self.isAutoUpdateEnabled = isAutoUpdateEnabled
        self.isPrivacyModeEnabled = isPrivacyModeEnabled
        self.isSoundEnabled = isSoundEnabled
        self.isVibrationEnabled = isVibrationEnabled
        self.isEmailUpdatesEnabled = isEmailUpdatesEnabled
        self.isTwoFactorAuthenticationEnabled = isTwoFactorAuthenticationEnabled
    }
}
  • 위 형태의 문제점
    • 1. 만약 "특정 플래그만 제외하고 다 초기화해줘"라는 의미의 clearAll(except:)라는 메서드를 구현할 경우 매우 복잡함
    • 2. 메모리 효율성이 낮음
  • 이것을 더욱 효율적으로 관리하고 싶은 경우?
    • OptionSet을 활용
  • OptionSet을 활용할때의 이점
    • 0. 간결성
    • 1. 비트 OR, AND 연산을 통해 충돌 없이 다중 상태 표현하기가 용이
    • 2. 메모리 효율성
    • 3. 비트 연산은 하드웨어적으로 매우 빠른 연산
      * OptionSet 및 이점에 관한 구체적인 개념은 이전 포스팅 글 참고

OptionSet으로 리펙토링하기

  • flag의 성격만 가지고 있으므로, 기존의 UserSettings 이름을 EnabledUserSettings로 바꾸고 여기에 속한 것들은 모두 enabled되어있다고 판단하여 사용
struct EnabledUserSettingsFlags: OptionSet {
    let rawValue: Int
    
    static let darkMode                = EnabledUserSettingsFlags(rawValue: 1 << 0)
    static let notifications           = EnabledUserSettingsFlags(rawValue: 1 << 1)
    static let locationServices        = EnabledUserSettingsFlags(rawValue: 1 << 2)
    static let autoUpdate              = EnabledUserSettingsFlags(rawValue: 1 << 3)
    static let privacyMode             = EnabledUserSettingsFlags(rawValue: 1 << 4)
    static let sound                   = EnabledUserSettingsFlags(rawValue: 1 << 5)
    static let vibration               = EnabledUserSettingsFlags(rawValue: 1 << 6)
    static let emailUpdates            = EnabledUserSettingsFlags(rawValue: 1 << 7)
    static let twoFactorAuthentication = EnabledUserSettingsFlags(rawValue: 1 << 8)

    static let all: EnabledUserSettingsFlags = [
        .darkMode,
        .notifications,
        .locationServices,
        .autoUpdate,
        .privacyMode,
        .sound,
        .vibration,
        .emailUpdates,
        .twoFactorAuthentication
    ]
}
  • 여기에 clearAll메서드도 구현에서도, 교집합을 사용하여 간결하게 표현이 가능
mutating func clearAll(except exceptions: EnabledUserSettingsFlags = []) {
    self = self.intersection(exceptions)
}
  • 사용하는 쪽)
var flag = EnabledUserSettingsFlags.all

print(flag.contains(.darkMode)) // true
flag.clearAll(except: .darkMode)
print(flag.contains(.darkMode)) // true
print(flag.contains(.notifications)) // false
  • 활용할땐 이 객체를 싱글톤으로 만들고 플래그 값이 필요할 때 아래처럼 사용
if EnabledUserSettingsFlags.shared.contains(.darkMode) {
  // darkmode에 관한 처리...
}
  • OptionSet을 쓴 결과 얻은 점
    • 0. 간결성
    • 1. 비트 OR, AND 연산을 통해 충돌 없이 다중 상태 표현하기가 용이
    • 2. 메모리 효율성
    • 3. 비트 연산은 하드웨어적으로 매우 빠른 연산
      * OptionSet 및 이점에 관한 구체적인 개념은 이전 포스팅 글 참고
Comments