iOS Class 4

1. 함수(Function) 정의와 호출

1-1. 매개변수 레이블과 시그니처

Swift에서 함수는 함수명 + 매개변수 레이블을 합친 것이 실제 구분되는 이름입니다.

// 1. 기본 형태
func add(x: Int, y: Int) { ... }
add(x: 10, y: 20) // 외부 매개변수명 x, y

// 2. 외부 이름 지정
func add(first x: Int, second y: Int) { ... }
add(first: 10, second: 20) // 외부 매개변수명 first, second

// 3. 외부 이름 생략
func add(_ x: Int, _ y: Int) { ... }
add(10, 20) // 외부 매개변수명 없음 (_ 사용)

// 4. 혼합
func add(_ x: Int, with y: Int) { ... }
add(10, with: 20) // 첫 번째는 없음, 두 번째는 with

UIKit 예시

func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int
  • 함수명: tableView
  • 전체 시그니처: tableView(_:numberOfRowsInSection:)
  • 자료형: (UITableView, Int) -> Int
  • 포인트: Swift에서는 함수 이름 자체는 tableView지만, 매개변수 라벨까지 포함해 구분합니다.
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell
// 시그니처: tableView(_:cellForRowAt:)

1-2. 디폴트 아규먼트(Default Argument)

func greet(name: String = "Guest") {
    print("Hello, \(name)")
}

greet()           // Hello, Guest
greet(name: "Tom") // Hello, Tom
  • 개념: 매개변수에 미리 값을 지정 → 호출 시 값이 없으면 기본값 사용
  • 장점: 함수 호출 간소화, 오버로딩 감소

1-3. 가변 인자(Variadic Parameter)

func print(_ items: Any..., separator: String = " ", terminator: String = "\n")
  • _ items: Any... → 외부 이름 없음, 여러 값 가능
  • separator → 값 사이 구분자
  • terminator → 출력 마지막 붙는 문자

예시

print("A", "B", "C")               // A B C
print("A", "B", "C", separator: "-") // A-B-C
print("Hello", terminator: "!")     // Hello!
  • 실제 시그니처: print(_:separator:terminator:)

2. 함수와 클로저

Swift에서 함수는 **1급 객체(First-class Citizen)**입니다.

  • 변수에 저장 가능
  • 매개변수로 전달 가능
  • 반환값으로 사용 가능
func add(x: Int, y: Int) -> Int { return x + y }
print(add(x:10, y:20)) // 30

let add1 = { (x: Int, y: Int) -> Int in return x + y }
print(add1(10, 20))    // 30
print(type(of:add1))    // (Int, Int) -> Int

2-1. 고차 함수(Higher-order function)

func math(x: Int, y: Int, cal: (Int, Int) -> Int) -> Int {
    return cal(x, y)
}

// 일반 함수 전달
math(x: 10, y: 20, cal: add) // 30

// 클로저 직접 전달
math(x: 10, y: 20, cal: { (a: Int, b: Int) -> Int in return a + b })

// Trailing closure, 타입 생략, shorthand
math(x: 10, y: 20) { $0 + $1 }
  • 포인트: 함수를 값처럼 전달하여 동작 변경 가능
  • 문법 축약 과정
    1. 완전형 { (Int, Int) -> Int in return a + b }
    2. Trailing closure { a, b in a + b }
    3. Shorthand { $0 + $1 }

3. 클래스와 속성(Properties)

3-1. 저장 속성 vs 계산 속성

class Man {
    var age: Int = 1            // 저장 속성
    var weight: Double = 3.5    // 저장 속성
    var squaredWeight: Double { // 계산 속성
        return weight * weight
    }
}
  • 저장 속성: 값을 직접 저장
  • 계산 속성: 값을 계산 후 반환

3-2. 옵셔널(Optional)

var age: Int?      // nil로 초기화 가능
var weight: Double! // 암시적 옵셔널

3-3. 이니셜라이저(Initializer)

class Man {
    var age: Int
    var weight: Double
    init(age: Int, weight: Double) {  // Designated initializer
        self.age = age
        self.weight = weight
    }
}
  • Designated Initializer: 모든 속성을 초기화하는 주 생성자
  • 영어 발음: /ˈdezɪɡˌneɪtɪd ɪˈnɪʃəˌlaɪzər/
  • 초기화 완료 전에는 메서드 호출 불가 (self 미완성)

3-4. 클래스 상속과 Override

class Animal {
    func sound() { print("동물이 소리를 냅니다") }
}

class Dog: Animal {
    override func sound() { print("멍멍!") }
}
  • override 키워드 필수
  • 부모 메서드 동작 확장 가능: super.sound()

4. Extension과 계산 속성

extension Double {
    var squared: Double { return self * self }
}

let myValue: Double = 3.5
print(myValue.squared) // 12.25
print(3.5.squared)     // 12.25
print(myValue.isZero)  // false
  • extension: 기존 타입에 기능 추가
  • computed property: 저장 없이 계산해서 반환

5. Access Modifier (액세스 제어자)

5-1. Swift

접근 수준키워드범위패키지(Module) 관점설명
공개public어디서든 접근 가능다른 패키지에서도 접근 가능외부 API/타입 공개
모듈 내부internal같은 모듈 내패키지 내부만 접근 가능기본값
파일 내부fileprivate같은 파일 내패키지와 관계 없음파일 안에서만 접근 가능
선언 범위private클래스/구조체 등 내부패키지와 관계 없음선언 범위 내부에서만 접근 가능

예시

class Person {
    private var name: String = "Unknown"
    var age: Int = 0      // internal 기본
    public func display() { print("\(name), \(age)") }
}

5-2. 다른 언어 비교

언어접근 제어 키워드특징
C++private, protected, publicOOP 중심, 상속 고려
C#private, protected, internal, protected internal, publicOOP 중심, 어셈블리 단위 접근 가능
Rust기본 private, pub, pub(crate)모듈 단위 중심, protected 없음

6. 요약

  • 함수: 이름 + 매개변수 라벨이 구분 단위, 디폴트값/가변 인자/클로저 가능
  • 클로저: 이름 없는 함수, trailing closure, shorthand 사용 가능
  • 클래스: 저장 속성, 계산 속성, 옵셔널, init, designated initializer
  • 상속/Override: 부모 메서드 재정의 가능, super 사용 가능
  • Extension: 기존 타입 확장, 계산 속성 추가
  • Access Modifier: 캡슐화와 안전성, Swift/타 언어 비교
  • 패키지 관점: internal/public 기준으로 모듈 간 접근 제어

func add(x: Int, y: Int) -> Int { return x+y
}
print(add(x:10, y:20))
{(매개변수 이름: 매개변수 타입, ... ) -> 반환 타입 in // 클로저 표현식 코드
}
let add1 = { (x: Int, y: Int) -> Int in return x+y
}
print(add1(x:10, y:20))
//주의 error: extraneous(관련 없는) argument labels 'x:y:' in call print(add1(10, 20)) //OK
print(type(of:add1)) //?

class Man{
var age : Int = 1 //직접 지정
var weight : Double = 3.5
}
class Man{
var age : Int? //옵셔널 변수는 nil로 자동 초기화
var weight : Double! //옵셔널 변수는 nil로 자동 초기화
}
class Man{
var age : Int
var weight : Double
init(){ //initializer로 초기화
age = 1
weight = 3.5
}
}

좋아요! 스위프트(Swift)에서 **접근자(access modifier)**를 실무에서 쓰는 방법을, 실제 개발 상황 중심으로 정리해 드릴게요. 단순 문법보다 **“왜 이렇게 쓰는지, 어디서 쓰는지”**를 이해하는 게 핵심이에요.


1️⃣ 기본 원칙

스위프트 접근자는 코드 캡슐화안정성을 위해 쓰입니다.
즉, 외부에서 함부로 속성이나 메서드에 접근하지 못하게 하고, 내부 구현을 숨기는 목적이 있어요.

  • 기본값: internal → 같은 모듈(앱 또는 프레임워크) 안에서 접근 가능
  • 외부 프레임워크 공개: public
  • 파일 단위 제한: fileprivate → 같은 파일 안에서만 사용
  • 클래스/구조체 범위 제한: private → 선언 범위 내부에서만 사용

2️⃣ 실무에서 자주 쓰는 패턴

🔹 1. private : 클래스 내부 구현 숨기기

class BankAccount {
    private var balance: Double = 0.0

    func deposit(amount: Double) {
        balance += amount
    }

    func withdraw(amount: Double) -> Bool {
        if amount <= balance {
            balance -= amount
            return true
        }
        return false
    }

    func getBalance() -> Double {
        return balance
    }
}
  • balanceprivate으로 숨겨서 직접 변경하지 못하게 함
  • 항상 deposit() / withdraw() 같은 메서드를 통해 안전하게 접근
  • 실무에서는 데이터 무결성 보장 목적

🔹 2. fileprivate : 같은 파일 내에서만 접근

fileprivate func helperFunction() {
    print("같은 파일에서만 사용")
}
  • 보통 테스트용 함수파일 내부만 쓰는 유틸 함수에 사용
  • 다른 파일에서 접근하면 컴파일 오류 발생

🔹 3. internal : 기본값

var name: String = "Guest"  // internal
  • 같은 모듈 안에서는 자유롭게 접근 가능
  • 앱 개발에서는 보통 별도 지정 없이 internal이 기본
  • 프레임워크 만들 때, 외부 공개 여부에 따라 public/internal 결정

🔹 4. public / open : 프레임워크용

public class APIClient {
    public init() {}

    public func fetchData() {
        // 외부 모듈에서도 사용 가능
    }
}
  • 프레임워크를 배포할 때 외부에서 접근할 API에 붙임
  • open상속까지 허용
  • 예: UIKit에서 UIViewController 클래스는 open이라 상속 가능

🔹 5. 계산 속성(computed property) + 접근자 조합

class Circle {
    private var radius: Double = 1.0

    public var diameter: Double {
        get {
            return radius * 2
        }
        set {
            radius = newValue / 2
        }
    }
}
  • 내부 저장소 radius는 private
  • 외부에서는 diameter를 통해 간접 접근 가능
  • 실무에서 API 형태와 내부 구현을 분리할 때 자주 사용

🔹 6. 테스트 환경에서 internal + @testable

@testable import MyApp
  • 테스트용 코드에서 internal 접근 허용
  • 실제 앱 코드에서는 외부에서 안 보이지만, 테스트에서 접근 가능
  • 실무에서 단위 테스트 작성 시 거의 필수 패턴

3️⃣ 실무에서 기억할 포인트

  1. 속성은 가능한 private으로 숨기고, 필요하면 메서드/계산 속성을 통해 공개
  2. 파일 범위 helper는 fileprivate로 제한
  3. 프레임워크 외부 공개 API는 public/open
  4. internal은 기본값 → 특별히 외부 공개 필요 없으면 지정 안 해도 됨
  5. 테스트에서 internal 접근 필요하면 @testable import 활용

💡 실무 요약 한 줄

“내부 구현은 최대한 숨기고(private/fileprivate), 외부에서 필요한 기능만 public/open으로 노출 → 안정성 + 유지보수 + 테스트 용이성 확보”