iOS프로그래밍기초(Smile Han)/복습

[iOS] 복습 07 - Swift 문법 5

wse46 2024. 10. 16. 16:50

클래스로부터 만들어진 객체를 인스턴스라고 한다.

 

Swift에서 Class 안의 변수 데이터를 프로퍼티(property)라고 한다.

 

객체지향 프로그래밍은 여러 현대 프로그래밍 언어에서 중요한 패러다임으로 클래스로부터 객체를 만드는 방법은 언어마다 조금씩 다르다.

## Swift

Swift에서는 다음과 같이 클래스를 정의하고 객체를 생성합니다:

class Car {
    var brand: String
    var model: String
    
    init(brand: String, model: String) {
        self.brand = brand
        self.model = model
    }
    
    func drive() {
        print("\(brand) \(model) is driving.")
    }
}

// 객체 생성
let myCar = Car(brand: "Tesla", model: "Model 3")
myCar.drive()



## Java

Java에서의 클래스 정의와 객체 생성:

public class Car {
    private String brand;
    private String model;
    
    public Car(String brand, String model) {
        this.brand = brand;
        this.model = model;
    }
    
    public void drive() {
        System.out.println(brand + " " + model + " is driving.");
    }
}

// 객체 생성
Car myCar = new Car("Toyota", "Corolla");
myCar.drive();



## C#

C#에서의 클래스 정의와 객체 생성:

public class Car
{
    public string Brand { get; set; }
    public string Model { get; set; }
    
    public Car(string brand, string model)
    {
        Brand = brand;
        Model = model;
    }
    
    public void Drive()
    {
        Console.WriteLine($"{Brand} {Model} is driving.");
    }
}

// 객체 생성
Car myCar = new Car("Ford", "Mustang");
myCar.Drive();


## JavaScript

JavaScript에서의 클래스 정의와 객체 생성:

class Car {
    constructor(brand, model) {
        this.brand = brand;
        this.model = model;
    }
    
    drive() {
        console.log(`${this.brand} ${this.model} is driving.`);
    }
}

// 객체 생성
const myCar = new Car("Honda", "Civic");
myCar.drive();



## Python


Python에서의 클래스 정의와 객체 생성:

class Car:
    def __init__(self, brand, model):
        self.brand = brand
        self.model = model
    
    def drive(self):
        print(f"{self.brand} {self.model} is driving.")

# 객체 생성
my_car = Car("Hyundai", "Sonata")
my_car.drive()



이러한 예시들은 각 언어에서 클래스를 정의하고 객체를 생성하는 기본적인 방법을 보여줍니다[1][2]. 모든 언어에서 클래스는 객체의 청사진 역할을 하며, 속성(프로퍼티)과 메서드를 포함합니다. 객체는 이 클래스의 인스턴스로 생성되며, 각 언어마다 약간의 문법적 차이가 있지만 기본 개념은 동일합니다.

 

 

 

객체지향 프로그래밍에서 상속은 중요한 개념입니다. 각 언어별로 상속을 구현하는 방법을 살펴보겠습니다.

## Swift

Swift에서는 콜론(`:`)을 사용하여 상속을 표현합니다:

class Vehicle {
    var currentSpeed = 0.0
    func makeNoise() {
        print("일반적인 소리")
    }
}

class Car: Vehicle {
    var gear = 1
    override func makeNoise() {
        super.makeNoise()
        print("부릉부릉")
    }
}

let myCar = Car()
myCar.currentSpeed = 50.0
myCar.makeNoise()

 



Swift에서는 `override` 키워드를 사용하여 메서드를 재정의하고, `super` 키워드로 부모 클래스의 메서드를 호출합니다[1].

## Java

Java에서도 `extends` 키워드를 사용하여 상속을 구현합니다:

class Vehicle {
    protected double currentSpeed = 0.0;
    public void makeNoise() {
        System.out.println("일반적인 소리");
    }
}

class Car extends Vehicle {
    private int gear = 1;
    @Override
    public void makeNoise() {
        super.makeNoise();
        System.out.println("부릉부릉");
    }
}

Car myCar = new Car();
myCar.currentSpeed = 50.0;
myCar.makeNoise();



Java에서는 `@Override` 어노테이션을 사용하여 메서드 재정의를 명시적으로 표시합니다.

## C#

C#에서는 콜론(`:`)을 사용하여 상속을 표현합니다:

class Vehicle
{
    public double CurrentSpeed { get; set; } = 0.0;
    public virtual void MakeNoise()
    {
        Console.WriteLine("일반적인 소리");
    }
}

class Car : Vehicle
{
    public int Gear { get; set; } = 1;
    public override void MakeNoise()
    {
        base.MakeNoise();
        Console.WriteLine("부릉부릉");
    }
}

Car myCar = new Car();
myCar.CurrentSpeed = 50.0;
myCar.MakeNoise();


C#에서는 `virtual` 키워드로 재정의 가능한 메서드를 선언하고, `override` 키워드로 메서드를 재정의합니다.

## JavaScript

JavaScript에서는 `extends` 키워드를 사용하여 클래스 상속을 구현합니다:

class Vehicle {
    constructor() {
        this.currentSpeed = 0.0;
    }
    makeNoise() {
        console.log("일반적인 소리");
    }
}

class Car extends Vehicle {
    constructor() {
        super();
        this.gear = 1;
    }
    makeNoise() {
        super.makeNoise();
        console.log("부릉부릉");
    }
}

const myCar = new Car();
myCar.currentSpeed = 50.0;
myCar.makeNoise();


JavaScript에서는 `super()` 를 사용하여 부모 클래스의 생성자를 호출하고, `super.methodName()`으로 부모 클래스의 메서드를 호출합니다.

## Python

Python에서는 괄호 안에 부모 클래스를 명시하여 상속을 구현합니다:

class Vehicle:
    def __init__(self):
        self.current_speed = 0.0
    
    def make_noise(self):
        print("일반적인 소리")

class Car(Vehicle):
    def __init__(self):
        super().__init__()
        self.gear = 1
    
    def make_noise(self):
        super().make_noise()
        print("부릉부릉")

my_car = Car()
my_car.current_speed = 50.0
my_car.make_noise()



Python에서는 `super().__init__()`을 사용하여 부모 클래스의 생성자를 호출하고, `super().method_name()`으로 부모 클래스의 메서드를 호출합니다.

이러한 예시들은 각 언어에서 상속을 구현하는 기본적인 방법을 보여줍니다. 모든 언어에서 상속의 개념은 유사하지만, 문법과 세부 구현 방식에서 차이가 있습니다. 상속을 통해 코드 재사용성을 높이고 계층적인 구조를 만들 수 있습니다[2].

 


메소드는 인스턴스만 사용하는 메소드(인스턴스 메소드)와 클래스만 사용하는 메소드(클래스/타입 메서드) 2가지 메소드가 있다.

 

 

1. 초기값 지정

age는 Stored property로 초기값 없이 실행되지 않음.

클래스 안에 있는 Stored property는 반드시 초기값이 있어야 함.

 

 

2. 옵셔널 변수로 선언 (자동으로 nil로 초기화)

 

 

 

 

p.34 실습

: Man 생략 가능

 

 

# command + A

ctrl + i 전제 정렬

 

 

class 에서 만들어진 클래스메서드(CM) 은 자식 클래스에서 override 가능

 

 designated initializer

모든 프로퍼티(age, weight)를 다 초기화시키는 생성자

일반적으로 .init 생략 가능

 

여러 프로그래밍 언어에서 현재 객체를 참조하는 키워드로 'this'와 'self'를 사용하는 방식

키워드언어

this Java, C++, JavaScript, PHP, C#, D
self Python, Ruby, Rust, Objective-C, Swift
Me Visual Basic
선택 가능 Perl (관례상 'self' 사용)


주요 특징:

- C++ 계열 언어들(Java, C# 등)은 주로 'this'를 사용합니다[1].
- Smalltalk에서 파생된 언어들은 주로 'self'를 사용하는 경향이 있습니다[1].
- Python에서는 'self'가 관례이지만, 실제로는 첫 번째 매개변수의 이름을 자유롭게 지정할 수 있습니다[1].
- 일부 언어(예: Perl)에서는 프로그래머가 선택할 수 있지만, 관례상 'self'를 사용합니다[2].
- Visual Basic은 독특하게 'Me'를 사용합니다[1].

이러한 차이는 주로 각 언어의 설계 철학과 역사적 배경에서 비롯됩니다. 예를 들어, 'self'를 사용하는 언어들은 객체가 "자기 자신"을 참조한다는 개념을 강조하는 반면, 'this'를 사용하는 언어들은 "이 객체"를 가리키는 의미를 강조합니다.

 

 

p20~23 시험 범위 제외

 


method overloading : 생성자 중첩

생성자 오버로딩;

매개변수의 개수와 자료형이 다른 같은 이름의 함수를 여러 개 정의

매개변수가 다른 두 생성자를 통해 두가지 방법으로 인스턴스를 만들 수 있음

 

apple 에서 만든 많은 이니셜라이징

 

Swift에서 UIImage 클래스를 사용하는 가장 일반적인 방법들 (순서대로)

1. 이미지 에셋에서 이미지 로드:

let image = UIImage(named: "imageName")

 

이 방법은 Assets.xcassets에 있는 이미지를 로드하며, 자동으로 캐싱됩니다.

2. 파일 경로에서 이미지 로드:

if let path = Bundle.main.path(forResource: "imageName", ofType: "png"),
   let image = UIImage(contentsOfFile: path) {
    // 이미지 사용
}

 

이 방법은 캐싱을 하지 않아 메모리 사용이 적습니다.

3. UIImageView에 이미지 설정:

let imageView = UIImageView(image: UIImage(named: "imageName"))



4. 시스템 이미지 사용:

let image = UIImage(systemName: "star.fill")



5. 데이터에서 이미지 생성:

if let data = Data(contentsOf: url),
   let image = UIImage(data: data) {
    // 이미지 사용
}



6. 이미지 리사이징:

let resizedImage = image.withRenderingMode(.alwaysOriginal).resizableImage(withCapInsets: .zero, resizingMode: .stretch)



7. 애니메이션 이미지 생성:

let images = [UIImage(named: "frame1"), UIImage(named: "frame2"), UIImage(named: "frame3")]
let animatedImage = UIImage.animatedImage(with: images, duration: 1.0)

 

 

클래스(class) 상속

 

내가 만든 클래스 이름 : 부모 클래스 이름

단일 상속 (single inheritance)으로, Swift에서 하위 클래스는 단 하나의 부모 클래스만 상속받을 수 있음

 

 

override : 부모와 자식에 같은 메서드가 있으면 자식 우선

Overriding 발생; 부모와 자식에 display()라는 메서드가 있어서 Student클래스는 display() 메서드가 두 개임

Student클래스의 인스턴스 lee가 display()를 호출할 때, 자식클래스가 새로 만든 display() 메서드가 우선적으로 호출되려면 func 앞에 override키워드 씀

 

 

값이 nil / optional 형으로 티턴됨

 

 

4 옵셔널 인스턴스를 사용시 강제 언래핑

실습 ㄱㄱ

1단계 init다음에 “?”

값을 리턴하기 전 옵셔널 인스턴스 강제 언래핑

nil 값을 언래핑 시도하면 에러

 

1-2 옵셔널 바인딩

 

2 인스턴스 생성과 동시에 옵셔널 바인딩

 

#wrtn

class Man {
    var age: Int // 나이 프로퍼티
    var weight: Double // 몸무게 프로퍼티
    
    // 나이와 몸무게를 출력하는 메서드
    func display() {
        print("나이=\(age), 몸무게=\(weight)")
    }
    
    // 초기화 메서드, 나이가 0 이하일 경우 nil 반환
    init?(age: Int, weight: Double) {
        if age <= 0 {
            return nil // 나이가 0 이하일 경우 인스턴스를 생성하지 않음
        } else {
            self.age = age // 유효한 나이일 경우 프로퍼티에 값 할당
        }
        self.weight = weight // 몸무게 프로퍼티에 값 할당
    }
}

// 1-1. 옵셔널 형으로 선언하여 Man 인스턴스 생성
var kim: Man? = Man(age: 1, weight: 3.5)

// 1-2. 옵셔널 바인딩을 사용하여 인스턴스가 nil이 아닐 경우 메서드 호출
if let kim1 = kim { 
    kim1.display() // kim1이 nil이 아니면 display() 호출
}

// 2. 인스턴스 생성과 동시에 옵셔널 바인딩
if let kim2 = Man(age: 2, weight: 5.5) {
    kim2.display() // 인스턴스가 nil이 아닐 경우 display() 호출
}

// 3. 인스턴스 생성하면서 바로 강제 언래핑
var kim3: Man = Man(age: 3, weight: 7.5)! // nil이 아닐 것을 보장하고 강제 언래핑
kim3.display() // kim3의 display() 호출

// 4. 옵셔널 인스턴스를 사용시 강제 언래핑
var kim4: Man? = Man(age: 4, weight: 10.5) // 옵셔널로 Man 인스턴스 생성
kim4!.display() // kim4가 nil이 아니라고 가정하고 강제 언래핑하여 display() 호출

 

#perplexity

// Man 클래스 정의
class Man {
    var age : Int  // 나이를 저장하는 변수
    var weight : Double  // 몸무게를 저장하는 변수
    
    // 나이와 몸무게를 출력하는 메서드
    func display() {
        print("나이=\(age), 몸무게=\(weight)")
    }
    
    // 실패 가능한 이니셜라이저 (age가 0 이하일 경우 nil 반환)
    init?(age: Int, weight : Double) {
        if age <= 0 {
            return nil  // age가 0 이하면 초기화 실패
        }
        else {
            self.age = age  // age가 양수면 초기화 진행
        }
        self.weight = weight  // weight 초기화
    }
}

// kim 변수를 Man? 타입으로 선언하고 초기화 (옵셔널 타입)
var kim : Man? = Man(age:1, weight:3.5)

// 1-1. kim은 옵셔널 형으로 선언되었음
// 1-2. 옵셔널 바인딩을 통해 kim의 값이 있는지 확인
if let kim1 = kim {
    kim1.display()  // kim이 nil이 아니면 display() 메서드 호출
}

// 2. 인스턴스 생성과 동시에 옵셔널 바인딩
if let kim2 = Man(age:2, weight:5.5) {
    kim2.display()  // 생성된 인스턴스가 nil이 아니면 display() 메서드 호출
}

// 3. 인스턴스 생성하면서 바로 강제 언래핑
// 주의: 강제 언래핑은 값이 nil일 경우 런타임 에러를 발생시킬 수 있음
var kim3 : Man = Man(age:3, weight:7.5)!
kim3.display()  // kim3는 non-optional 타입이므로 바로 메서드 호출 가능

// 4. 옵셔널 인스턴스를 사용시 강제 언래핑
// 주의: 강제 언래핑은 값이 nil일 경우 런타임 에러를 발생시킬 수 있음
var kim4 : Man? = Man(age:4, weight:10.5)
kim4!.display()  // kim4가 nil이 아님을 확신할 때 강제 언래핑 사용

 

상속, 오버라이딩, failable initializer를 포함한 예제 코드

class Vehicle {
    let numberOfWheels: Int
    
    init?(numberOfWheels: Int) {
        if numberOfWheels < 1 {
            return nil
        }
        self.numberOfWheels = numberOfWheels
    }
    
    func describe() {
        print("This vehicle has \(numberOfWheels) wheels.")
    }
}

class Car: Vehicle {
    let brand: String
    
    init?(brand: String, numberOfWheels: Int) {
        if brand.isEmpty {
            return nil
        }
        self.brand = brand
        super.init(numberOfWheels: numberOfWheels)
    }
    
    override func describe() {
        print("This \(brand) car has \(numberOfWheels) wheels.")
    }
}

class ElectricCar: Car {
    let batteryCapacity: Int
    
    init(brand: String, batteryCapacity: Int) {
        self.batteryCapacity = batteryCapacity
        super.init(brand: brand, numberOfWheels: 4)!
    }
    
    override func describe() {
        super.describe()
        print("It has a battery capacity of \(batteryCapacity) kWh.")
    }
}

이 코드에 대한 설명:

  1. Vehicle 클래스:
    • failable initializer init?(numberOfWheels: Int)를 가집니다.
    • 바퀴 수가 1 미만이면 초기화에 실패합니다.
  2. Car 클래스:
    • Vehicle을 상속받습니다.
    • failable initializer init?(brand: String, numberOfWheels: Int)를 가집니다.
    • 브랜드가 비어있으면 초기화에 실패합니다.
    • describe() 메서드를 오버라이드합니다.
  3. ElectricCar 클래스:
    • Car를 상속받습니다.
    • non-failable initializer init(brand: String, batteryCapacity: Int)를 가집니다.
    • 부모 클래스의 failable initializer를 강제 언래핑하여 호출합니다.
    • describe() 메서드를 다시 오버라이드합니다.
if let bicycle = Vehicle(numberOfWheels: 2) {
    bicycle.describe()  // 출력: This vehicle has 2 wheels.
}

if let sedan = Car(brand: "Toyota", numberOfWheels: 4) {
    sedan.describe()  // 출력: This Toyota car has 4 wheels.
}

let teslaModel3 = ElectricCar(brand: "Tesla", batteryCapacity: 75)
teslaModel3.describe()
// 출력:
// This Tesla car has 4 wheels.
// It has a battery capacity of 75 kWh.

let invalidCar = Car(brand: "", numberOfWheels: 4)  // nil
let invalidVehicle = Vehicle(numberOfWheels: 0)  // nil