2024년 3월 13일 수요일
- observing하여 특정 preperty의 값 변경 전후에 추가 동작을 수행할 수 있는 Property Observer와,
- 변수나 객체의 타입을 다른 타입으로 변환하는 프로세스인 Type Casting,
- 코드 요소에 대한 외부 접근을 제한하여 모듈 간의 접근성과 보안을 조절하는 Access Modifier 이해하기
#1. Property Observer (프로퍼티 옵저버)
#2. Type Casting (타입 캐스팅)
#3. Access Modifier (접근 제한자)
#1. Property Observer
- 변수에 프로퍼티 옵저버를 정의하여 프로퍼티 값의 변경 사항을 모니터링하고, 미리 구현한 코드로 이에 대응
- 다시 말해, 해당 프로퍼티를 관찰(observe)하면서 변경 사항이 발생할 때 실행
- willSet과 didSet을 둘 다 작성했을 경우 willSet이 먼저 실행
- 추가할 수있는 경우
- 저장 프로퍼티(stored property)
- 연산 프로퍼티(computed property)
○ didSet 과 willSet
- ☑️ didSet
- didSet은 새 값이 저장된 직후에 호출
- 이전 프로퍼티의 값이 oldValue 로 제공
- willSet은 값이 저장되기 직전에 호출
- 새로운 프로퍼티의 값이 newValue 로 제공됩니다.
var myProperty: Int = 20{
didSet(oldVal){
//myProperty의 값이 변경된 직후에 호출, oldVal은 변경 전 myProperty의 값
}
willSet(newVal){
//myProperty의 값이 변경되기 직전에 호출, newVal은 변경 될 새로운 값
}
}
var name: String = "Unknown" {
willSet {
print("현재 이름 = \(name), 바뀔 이름 = \(newValue)")
}
didSet {
print("현재 이름 = \(name), 바뀌기 전 이름 = \(oldValue)")
}
}
name = "Peter"
// willSet이 먼저 실행됨
// 현재 이름 = Unknown, 바뀔 이름 = Peter
// 현재 이름 = Peter, 바뀌기 전 이름 = Unknown
class UserAccount {
var username: String
var password: String
var loginAttempts: Int = 0 {
didSet {
if loginAttempts >= 3 {
print("로그인 시도가 3회 이상 실패하였습니다. 계정이 잠겼습니다.")
lockAccount()
}
}
}
var isLocked: Bool = false {
didSet {
if isLocked {
print("계정이 잠겼습니다.")
} else {
print("계정이 잠금 해제되었습니다.")
}
}
}
init(username: String, password: String) {
self.username = username
self.password = password
}
func login(with enteredPassword: String) {
if enteredPassword == password {
print("로그인 성공!")
loginAttempts = 0 // 로그인 성공 시 로그인 시도 횟수 초기화
} else {
print("잘못된 비밀번호입니다.")
loginAttempts += 1 // 로그인 실패 시 로그인 시도 횟수 증가
}
}
private func lockAccount() {
isLocked = true
}
func unlockAccount() {
isLocked = false
}
}
// 사용자 계정 생성
let user = UserAccount(username: "user123", password: "password123")
// 로그인 시도
user.login(with: "wrongpassword")
// 출력:
// 잘못된 비밀번호입니다.
user.login(with: "wrongpassword")
// 출력:
// 잘못된 비밀번호입니다.
user.login(with: "wrongpassword")
// 출력:
// 잘못된 비밀번호입니다.
// 로그인 시도가 3회 이상 실패하였습니다. 계정이 잠겼습니다.
// 계정이 잠겼습니다.
// 계정 잠금 해제
user.unlockAccount() // 계정이 잠금 해제되었습니다.
#2. Type Casting
- 변수나 객체의 타입을 다른 타입으로 변환하는 프로세스
- ☑️ is
- is 연산자는 타입을 체크하는 연산자로, 비교 결과를 bool 타입을 반환(타입 체킹)
let char: Character = "A"
print(char is Character)
// 출력값: true
print(char is String)
// 출력값: false
let bool: Bool = true
print(bool is Bool)
// 출력값: true
print(bool is Character)
// 출력값: false
☑️ as, as!, as? ( type에 대해 복습하기 )
- as
- as 연산자는 컴파일 단계에서 캐스팅이 실행 → 타입 캐스팅이 성공할 경우에만 사용 가능
- 캐스팅에 실패할 경우 에러가 발생
- 캐스팅하려는 타입이 같은 타입 이거나 수퍼클래스 타입이라는 것을 알 때 as 연사자를 사용
- as?
- as? 연산자는 런타임에 캐스팅이 실행
- 성공하면 옵셔널 타입의 인스턴스를 반환하고 실패하면 nil 을 반환
- 실패할 가능성이 있으면 as?를 사용하는 것이 좋다.
- as!
- as! 연산자는 런타임에 특정 타입으로 강제 캐스팅
- 강제 타입 캐스팅에 실패할 경우 런타임 에러가 발생
- 캐스팅에 성공한 경우 인스턴스를 반환(옵셔널 x)
class Person {
var id = 0
var name = "name"
var email = "hgk@gmail.com"
}
class Worker: Person {
// id
// name
// email
var salary = 300
}
class Programmer: Worker {
// id
// name
// email
// salary
var lang = "Swift"
}
// 업캐스팅 - as
let person1 = Person()
let worker1 = Worker()
let programmer1 = Programmer()
let personList = [person1, worker1, programmer1] // 타입을 선언하지 않았지만 Person 타입으로 인식 -> 즉 업캐스팅이 되었음
personList[1].name
//personList[1].salary // Person 타입으로 보고 있기 때문에 salary에 접근하지 못함
let worker2 = Worker()
worker2.salary
let workerPerson = worker2 as Person
//workerPerson.salary // Person 타입으로 보고 있기 때문에 salary에 접근하지 못함
// 다운캐스팅 - as? / as!
// as?
let pro = programmer1 as? Programmer // 타입 변환이 될 수도 있고 안될 수도 있기 때문에 옵셔널을 리턴
if let person2 = programmer1 as? Programmer {
person2.lang
}
if let person3 = worker1 as? Programmer {
person3.lang
}
// as!
let pro2 = worker2 as! Programmer // Error : 타입 변환 실패시 오류
#3. AccessModifier
- 다른 소스 파일이나 모듈의 코드에서 코드 일부에 대한 접근을 제한
- [제약 적음] open < public < internal < fileprivate < private [제약 많음]
- open : 모든 소스 파일에서 해당 level 접근 가능 + 모든 곳에서 서브클래싱 가능
- public : 모든 소스 파일에서 해당 level 접근 가능 + 같은 모듈 내에서만 서브클래싱 가능
- internal : 같은 모듈 내에서만 접근 가능
- fileprivate : 같은 소스파일 내에서만 접근 가능
- private : 클래스 내부에서만 접근 가능
- 접근 제한자를 작성하지 않으면 internal로 판단
- 상위 요소보다 하위 요소가 더 높은 접근 수준을 가질 수 없다.
private struct Car {
public var model: String // 🚨 에러
}
- 모듈과 소스파일
- 모듈(module)
- 배포할 코드의 묶음 단위
- 하나의 프레임워크/ 라이브러리/ 어플리케이션이 모듈 단위가 될 수 있다.
- import 키워드를 통해 불러올 수 있다.
- 소스파일
- 하나의 swift 소스 코드 파일을 의미
- 모듈(module)
- public, open
- 둘 다 모듈 외부까지 접근할 수 있다.
- open은 클래스와 클래스 맴버에서만 사용할 수 있고 다른 모듈에서 서브클래싱이 가능하지만 public은 그렇지 않다.
- open으로 클래스를 개방 접근 수준으로 명시하는 것은 그 클래스를 다른 모듈에서도 수퍼클래스로 사용하겠다는 의미→ 해당 클래스를 설계하고 만들었다는 것을 의미(다른 모듈에서 상속을 허용함)
- public은 주로 프레임워크에서 외부와 연결될 인터페이스를 구현하는데 많이 사용된다.
- internal
- 모든 요소에 암묵적으로 지정하는 디폴트 접근 제어자
- 소스 파일이 속해있는 모듈 어디에든 접근할 수 있지만 외부 모듈에서는 접근할 수 없다.
- fileprivate
- 소스 파일 내부에서만 접근할 수 있다.
- 서로 다른 클래스가 같은 하나의 소스 파일에 정의되어있고 fileprivate로 선언되어 있다면 두 클래스는 서로 접근할 수 있다.
- private
- 가장 제한적인 접근 제어자입니다.
- fileprivate과 달리 같은 파일 안에 있어도 서로 다른 클래스이고 private로 선언되었다면 두 요소는 서로 접근할 수 없다.
// open
open class Vehicle {
open func startEngine() {
print("Engine started")
}
}
open class Car: Vehicle {
open var carType: String = "Sedan"
}
// public
public struct Point {
public var x: Int
public var y: Int
public init(x: Int, y: Int) {
self.x = x
self.y = y
}
public mutating func moveByX(_ deltaX: Int, y deltaY: Int) {
self.x += deltaX
self.y += deltaY
}
}
// internal
internal class InternalClass {
internal var internalProperty: Int = 10
internal func doSomethingInternally() {
print("Internal operation performed")
}
}
internal let internalConstant = 20
// fileprivate
class OuterClass {
fileprivate var outerVariable = 30
fileprivate func outerFunction() {
print("Outer function called")
}
fileprivate class InnerClass {
fileprivate func innerFunction() {
print("Inner function called")
}
}
}
// private
class MyClass {
private var privateVariable = 40
private func privateFunction() {
print("Private function called")
}
}
'iOS 앱 개발자 프로젝트 > Swift 문법 정복하기' 카테고리의 다른 글
[Swift] protocol, associatedtype, typealias (0) | 2024.03.13 |
---|---|
[Swift] Exception (throws, throw, do-catch, try) (0) | 2024.03.13 |
[Swift] Inheritance, Initializer (0) | 2024.03.12 |
[Swift] Class, Struct, Enum (0) | 2024.03.11 |
[Swift] array, set, dictionary and OOP (1) | 2024.03.10 |