코딩공부

iOS 7주차

늠재 2025. 10. 13. 14:12

swift에는 stored Property

limits method가 보통 자주 쓰임

자동적으로 컴파일러가 만들어주는 생성자를 자기가 만들어준다.

  • 인스턴스 메서드 (Instance Method): self를 받아. 개별 객체(인스턴스)의 데이터에 접근하고 변경하는 데 사용돼. (가장 흔하게 쓰는 메서드)
  • 클래스 메서드 (Class Method): cls를 받아. 클래스 자체의 속성이나 클래스를 활용해 객체를 생성하는 데 사용돼. 상속받은 자식 클래스에서도 원래 부모 클래스의 클래스 메서드를 호출하면 그 자식 클래스의 cls가 전달되어서 유연하게 동작할 수 있다는 장점도 있어! 😼
  • 정적 메서드 (Static Method): self나 cls 둘 다 받지 않아! 그냥 클래스 안에 "툭"하고 넣어둔 일반 함수라고 보면 돼. 클래스나 인스턴스의 특정 데이터에 의존하지 않고, 해당 클래스(혹은 모듈)와 연관된 유틸리티 기능을 제공할 때 사용한단다.

생성자는 괄호 열면 자동으로 호출됨

var kim : Man = Man(yourAge:10, yourWeight:20.5) //일반적인 init()호출 방법

var kim1 : Man = Man.init(yourAge:1, yourWeight:3.5)//이렇게 작성해도 되지만 비추

 

 

self

-- 혀재 클래스 내 프로퍼티를 가리킬 때 씀

class Man{
var age : Int = 1
var weight : Double = 3.5
func display(){
print("나이=\(age), 몸무게=\(weight)")
}
init(age: Int, weight : Double){
self.age = age //self.을 만드시 써야 함
//매개변수 age값을 프로퍼티 age에 대입
self.weight = weight
}
}
var kim : Man = Man(age:10, weight:20.5)
kim.display()

 

 

프로그래밍 언어별 차이

 

 

파이썬

*   **특징**: 파이썬의 `self`는 **아주아주 명시적**이야! 모든 인스턴스 메서드의 첫 번째 매개변수로 반드시 `self`를 넣어줘야 해. 심지어 메서드를 호출할 때는 `self`를 직접 넘겨주지 않아도 파이썬이 알아서 현재 객체를 `self`로 넘겨준단다.
*   **왜 이렇게 할까?**: 파이썬 개발자들은 이렇게 명시적으로 `self`를 써야 코드를 읽는 사람이 "아, 이 메서드가 현재 객체의 데이터에 접근하고 있구나!" 하고 한눈에 알 수 있어서 좋다고 생각했어. 코드를 더 명확하게 만드는 거지!
*   **예시**:
    ```python
    class Cat:
        def __init__(self, name):
            self.name = name  # 여기서 self.name은 이 Cat 객체의 이름이야!

        def meow(self):
            print(f"{self.name} 야옹!") # self.name으로 내 이름을 불러!

    my_cat = Cat("나비")
    my_cat.meow() # 출력: 나비 야옹!
    ```

 

 

자바

*   **특징**: 자바의 `this`는 **대부분 묵시적**이야. 메서드 안에서 현재 객체의 멤버 변수나 메서드를 참조할 때 굳이 `this.`를 붙이지 않아도 대부분 잘 동작한단다. 하지만 지역 변수와 멤버 변수 이름이 같아서 혼동될 때는 `this.`를 붙여서 명확하게 구분해 줄 수 있어.
*   **주요 용도**:
    *   지역 변수와 멤버 변수 이름이 같을 때 멤버 변수를 지칭 (예: `this.name = name;`)
    *   다른 생성자를 호출할 때 (예: `this(other_args);`)
*   **예시**:
    ```java
    class Cat {
        String name;

        public Cat(String name) {
            this.name = name; // 여기서 this.name은 멤버 변수, name은 매개변수!
        }

        public void meow() {
            System.out.println(name + " 야옹!"); // this.name 안 해도 name은 멤버 변수!
        }
    }
    // ... (사용 예시는 파이썬과 유사)
    ```

 

overloading

생성자 오버로딩 : 같은 이름의 함수를 여러 개 정의

 

swift 생성자 오버로딩 예시

class Man{
var age : Int
var weight : Double
func display(){
print("나이=\(age), 몸무게=\(weight)")
}
init(age: Int, weight : Double){ //1
self.age = age
self.weight = weight
} //designated initializer
init(){ //2
self.age = 1
self.weight = 3.5
}
}
var kim : Man = Man(age:10, weight:20.5) //1
var kim1 : Man = Man() //2
//var kim2 : Man = Man()
//init가 없다면 인스턴스 만드는 방법
kim.display()
kim1.display()

자바의 생성자 오버로딩

// Java 예시
class Cat {
    String name;
    int age;
    String furColor;

    // 1. 이름만 받는 생성자 (기본 생성자)
    public Cat(String name) {
        this(name, 0); // "나야 나!" 이 클래스의 다른 생성자를 호출! (이름, 나이)
        this.furColor = "갈색"; // 추가로 털 색상 기본값 설정
    }

    // 2. 이름과 나이를 받는 생성자
    public Cat(String name, int age) {
        this.name = name; // 여기서 this는 현재 Cat 객체 자신을 가리켜!
        this.age = age;
        this.furColor = "알 수 없음"; // 기본값 설정
    }

    // 3. 이름, 나이, 털 색깔까지 받는 생성자
    public Cat(String name, int age, String furColor) {
        this.name = name;
        this.age = age;
        this.furColor = furColor;
    }

    public void displayInfo() {
        System.out.println(name + "은(는) " + age + "살이고, 털색은 " + furColor + "이다냥!");
    }
}

// 사용 예시
public class Main {
    public static void main(String[] args) {
        Cat cat1 = new Cat("나비");                  // 이름만으로 생성! (나이 0, 갈색)
        Cat cat2 = new Cat("야옹이", 3);             // 이름과 나이로 생성! (털색 알 수 없음)
        Cat cat3 = new Cat("레오", 5, "치즈");      // 모든 정보로 생성!

        cat1.displayInfo(); // 나비은(는) 0살이고, 털색은 갈색이다냥!
        cat2.displayInfo(); // 야옹이은(는) 3살이고, 털색은 알 수 없음이다냥!
        cat3.displayInfo(); // 레오은(는) 5살이고, 털색은 치즈이다냥!
    }
}

 

🌟 failable initializer가 뭐냐옹?

일반적인 초기화(생성)는 객체를 만들 때 항상 성공하잖아? 그런데 failable initializer는 말 그대로 "실패할 수도 있는" 초기화를 의미해! 😮 즉, 객체를 만들려다가 조건이 맞지 않거나 뭔가 문제가 생기면 nil (객체 없음!)을 반환할 수 있는 생성자란다.

표현은 init? (물음표가 붙어!) 이렇게 생겼어. 마치 "이거 만들 수 있을까? 없을 수도 있어!" 하고 물어보는 것 같지?


🧐 왜 필요한 거냐옹? (쓰는 이유)

생각해 봐! 우리가 어떤 고양이 객체(Cat 인스턴스)를 만들려고 하는데, 이런 상황들이 생길 수 있잖아?

  1. 유효하지 않은 입력 값으로 객체를 만들 때 (데이터 검증)
    • "고양이 이름이 없어요!" 😵 (빈 문자열)
    • "고양이 나이가 마이너스 5살이라고요?" 😱 (음수)
    • "고양이 등급을 '최상급'으로 만들어야 하는데, '최악급'이라고 넣었어요!" (잘못된 데이터)
    ➡️ 이런 경우에 "에러 메시지를 띄우고 앱이 멈추게 하는 것"보다는 **"그냥 객체 생성을 실패시키고 nil을 반환"**하는 게 훨씬 깔끔하고 안전한 방법이야! 엉터리 객체가 만들어져서 나중에 문제를 일으키는 것을 막을 수 있지!
  2. 외부 리소스 로딩에 실패했을 때 (파일, 네트워크)
    • "내 앱 폴더에 'cuteCat.png'라는 이미지가 있어야 하는데, 파일이 없대요!" 😿
    • "저 URL로 고양이 사진을 받아오려 했는데, 인터넷이 안 되거나 주소가 잘못됐대요!" 😾
    ➡️ 이런 상황들은 앱의 잘못이라기보다는 외부 환경적인 요인이잖아? 이럴 때도 객체 생성을 실패시키면, 코드가 훨씬 안전해지고 유연하게 대응할 수 있어!
  3. 데이터를 변환하거나 형식을 맞출 때 (타입 캐스팅, 원시값 초기화)
    • "숫자 텍스트 '123'을 Int로 바꾸려고 했는데, 'ABC'가 들어와서 변환할 수 없대요!" 🤯
    • 열거형(Enum)을 만들 때 특정 '원시값' (raw value)으로 만들려고 했는데, 해당 원시값에 해당하는 케이스가 없을 때 실패해야겠지?
    ➡️ Swift의 Int("123") 이나 URL(string: "...") 같은 것들이 대표적인 failable initializer들이야! UIImage(named: "myCatImage")도 마찬가지고! (이전에 설명했지?)

 

만드는 방법

1, init 다음에 ? 붙이기

2. 언래핑 하기 위해 정상적인 수치면 수치 그대로 넣어주고
     정상 범주가 아니면 nil을 리턴하도록 작성

3. 옵셔널 바인딩으로 풀어준다.

 

class Man{
    var age : Int
    var weight : Double
    func display(){
        print("나이=\(age), 몸무게=\(weight)")
    }
    init?(age: Int, weight : Double){
        if age <= 0 || weight <= 0.0{
            return nil
        }
        else{
            self.age = age
            self.weight = weight
        }
    }
} // failable initializer
var kim : Man? = Man(age:1, weight:3.5)

//1-1.옵셔널 형으로 선언
if let kim1 = kim { //1-2.옵셔널 바인딩  if let kim = kim, if let kim 으로도 됨
    kim1.display()
}

//2.인스턴스 생성과 동시에 옵셔널 바인딩
if let kim2 = Man(age:2, weight:5.5) { // 저 옵셔널 선언 부분이 필요없어짐
    kim2.display()
}

//3.인스턴스 생성하면서 바로 강제 언래핑
var kim3 : Man = Man(age:3, weight:7.5)!
kim3.display()

//4.옵셔널 인스턴스를 사용시 강제 언래핑
var kim4 : Man? = Man(age:4, weight:10.5)
kim4!.display()

//3,4 번째는 크래시(crash) 우려가 있어 사용하지 않는다.

 

부모는 하나만 있거나 없을 수 있지만

프로토콜은 여러 개 있을 수 있다(java의 interface)

 

 

class Man{

    var age : Int = 1

    var weight : Double = 3.5

    func display(){

        print("나이=\(age), 몸무게=\(weight)")

    }

    init(age: Int, weight : Double){

        self.age = age

        self.weight = weight

    }

}



class Student : Man{

    

}

var kim = Man(age:10, weight:20.5)

kim.display()

var kim1 = Student(age:20, weight:60.5)

kim1.display()

Man에서 상속을 받아 Man의 변수들을 다 가져다 사용할 수 있게 된다.

부모 클래스의 메소드가 마음에 안들어서 

새로 만들 수도 있다. 다른 이름으로 만들어도 되고

같은 이름으로 오버라이딩 해도 되는데

오버라이딩 할 때는 func 구문 앞에 override를 붙여준다.

 

class Man{
    var age : Int
    var weight : Double
    func display(){
        print("나이=\(age), 몸무게=\(weight)")
    }
    init(age: Int, weight : Double){
        self.age = age
        self.weight = weight
    }
}

class Student : Man {
    var name : String
    override func display() {
        print("이름=\(name), 나이=\(age), 몸무게=\(weight)")
    }
    init(age : Int, weight : Double, name : String){
        self.name = name //자식의 프로퍼티 먼저 초기화, 그렇게 안하면 에러남.
        super.init(age:age, weight:weight) //init으로 불러서 초기값을 지정해주어야한다.
    }
}
var lee : Student = Student(age:20,weight:65.2,name:"홍길동")
lee.display()

오버로딩 (Overloading): 같은 이름, 다른 능력 (유연한 사용)

오버로딩은 하나의 클래스 안에서 여러 개의 메서드(또는 함수, 생성자)가 동일한 이름을 가지지만, **매개변수의 목록(개수 또는 타입)**이 다른 경우를 의미합니다.

  • 정의: 동일한 이름을 가진 메서드가 여러 개 존재하며, 각 메서드는 인자의 개수나 자료형이 다릅니다.
  • 목적: 동일한 기능(예: 더하기 기능)을 수행하지만, 입력되는 데이터의 형태가 다를 때(예: 정수 덧셈, 실수 덧셈, 여러 숫자 덧셈 등) 사용자에게 유연하고 직관적인 호출 방법을 제공합니다.
  • 발생 시점: 주로 컴파일 시점에 어떤 오버로딩된 메서드를 호출할지 결정됩니다.
  • 상속 관계: 상속과는 무관하게 동일 클래스 내에서 발생합니다.

오버라이딩 (Overriding): 부모의 기능, 자식의 방식으로 (동작 재정의)

오버라이딩은 **상속 관계에 있는 두 클래스(부모 클래스와 자식 클래스)**에서 자식 클래스가 부모 클래스에 정의된 메서드와 동일한 시그니처(이름, 매개변수 목록, 반환 타입)를 가진 메서드를 재정의하는 것을 의미합니다.

  • 정의: 자식 클래스가 부모 클래스로부터 상속받은 메서드를 자신의 특정 상황에 맞게 새로운 동작으로 구현하는 것입니다.
  • 목적: 부모 클래스의 일반적인 동작을 자식 클래스에서 특수한 동작으로 변경/구체화하여 다형성(Polymorphism)을 구현합니다.
  • 발생 시점: 주로 런타임 시점에 어떤 메서드를 호출할지 결정됩니다 (동적 바인딩).
  • 상속 관계: 반드시 상속 관계에서만 발생합니다.

 

 

 

import UIKit
class ViewController: UIViewController {
    @IBOutlet var lblHello: UILabel!
    @IBOutlet var txtName: UITextField!
    //Interface Builder Outlet(변수, 프로퍼티)
    override func viewDidLoad() {
        super.viewDidLoad()
    }
    @IBAction func btnSend(_ sender: UIButton) {
        //Interface Builder Action(함수, 메서드)
        lblHello.text = "Hello, " + txtName.text!
    }
}
import UIKit
  1. import UIKit:
    • 설명: 이 줄은 UIKit 프레임워크를 현재 파일로 가져옵니다. UIKit은 Apple의 iOS 앱 개발을 위한 핵심 프레임워크로, 사용자 인터페이스(UI)를 구성하는 데 필요한 다양한 클래스, 함수, 구조체 등을 포함하고 있습니다. 예를 들어 UIViewController, UILabel, UITextField, UIButton 등이 모두 UIKit에 정의되어 있습니다. 이 줄이 없으면 UI 요소를 사용할 수 없습니다.

swift

class ViewController: UIViewController {
  1. class ViewController: UIViewController {:
    • 설명: ViewController라는 이름의 새로운 클래스를 정의하는 코드입니다.
      • ViewController: 이 클래스의 이름입니다. 개발자가 임의로 지정할 수 있지만, 일반적으로 화면을 제어하는 클래스에 ViewController라는 접미사를 붙입니다.
      • 콜론은 ViewController 클래스가 다른 클래스를 **상속받는다(inherit)**는 것을 의미합니다.
      • UIViewController: ViewController가 상속받는 부모 클래스입니다. UIViewController는 UIKit에 정의된 클래스로, iOS 앱의 단일 화면을 관리하고 해당 화면의 뷰(View)를 조작하는 데 필요한 기본적인 기능을 제공합니다. 모든 화면 컨트롤러는 UIViewController를 상속받아야 합니다.

swift

@IBOutlet var lblHello: UILabel!
  1. @IBOutlet var lblHello: UILabel!:
    • 설명: lblHello라는 이름의 UILabel 타입 인스턴스 변수(프로퍼티)를 선언하는 코드입니다.
      • @IBOutlet: 이 어노테이션은 이 프로퍼티가 **인터페이스 빌더(Interface Builder)**와 연결될 것임을 나타냅니다. 인터페이스 빌더(Xcode의 스토리보드 또는 XIB 파일)에서 디자인한 UI 요소(여기서는 UILabel)를 코드에서 참조할 수 있도록 연결하는 역할을 합니다.
      • var: lblHello가 변수(값을 변경할 수 있는 프로퍼티)임을 나타냅니다.
      • lblHello: 이 변수의 이름입니다. 일반적으로 UI 요소의 종류를 나타내는 접두사(lbl은 Label을 의미)와 기능을 나타내는 단어(Hello)를 조합하여 명명합니다.
      • :: 변수의 타입(UILabel)을 명시합니다.
      • UILabel: 이 변수가 UIKit의 UILabel 클래스 타입임을 의미합니다. UILabel은 화면에 텍스트를 표시하는 데 사용되는 UI 요소입니다.
      • !: 이것은 **암시적 언래핑 옵셔널(Implicitly Unwrapped Optional)**을 의미합니다. UILabel은 인터페이스 빌더에서 연결되기 전까지는 nil일 수 있지만, 연결된 후에는 항상 값이 있을 것이라고 가정하며, 개발자가 매번 옵셔널을 언래핑하는 번거로움 없이 바로 사용할 수 있게 해줍니다. 하지만 만약 연결에 실패하여 nil 상태로 접근하면 런타임 오류(크래시)가 발생할 수 있으므로 주의해야 합니다.

swift

@IBOutlet var txtName: UITextField!
  1. @IBOutlet var txtName: UITextField!:
    • 설명: txtName이라는 이름의 UITextField 타입 인스턴스 변수를 선언하는 코드입니다.
      • @IBOutlet: 이 프로퍼티가 인터페이스 빌더와 연결될 것임을 나타내는 어노테이션입니다.
      • var: 변수(값을 변경할 수 있는 프로퍼티)임을 나타냅니다.
      • txtName: 이 변수의 이름입니다. txt는 TextField를 의미합니다.
      • UITextField: UIKit의 UITextField 클래스 타입입니다. UITextField는 사용자로부터 한 줄의 텍스트 입력을 받는 데 사용되는 UI 요소입니다.
      • !: lblHello와 마찬가지로 암시적 언래핑 옵셔널입니다.

swift

//Interface Builder Outlet(변수, 프로퍼티)
  1. //Interface Builder Outlet(변수, 프로퍼티):
    • 설명: 코드에 대한 설명(주석)입니다. // 뒤에 나오는 내용은 컴파일러에 의해 무시되며, 코드를 읽는 개발자가 해당 코드의 목적이나 기능 등을 이해하는 데 도움을 줍니다. 이 주석은 위에 선언된 @IBOutlet 변수들이 인터페이스 빌더와 연결된 UI 요소임을 설명하고 있습니다.

swift

override func viewDidLoad() {
  1. override func viewDidLoad() {:
    • 설명: viewDidLoad()라는 메서드(함수)를 정의하는 코드입니다.
      • override: 이 키워드는 현재 클래스(ViewController)가 부모 클래스(UIViewController)에 이미 존재하는 viewDidLoad() 메서드를 **재정의(override)**한다는 것을 명시합니다. UIViewController에는 viewDidLoad()라는 기본 구현이 있으며, 자식 클래스에서 이 메서드를 호출하면 자신의 구현으로 덮어쓰는 것입니다.
      • func: Swift에서 함수를 정의할 때 사용하는 키워드입니다.
      • viewDidLoad(): 이 메서드의 이름입니다. viewDidLoad()는 뷰 컨트롤러의 뷰(View) 계층 구조가 메모리에 로드된 후, 그리고 화면에 나타나기 직전에 단 한 번 호출되는 UIViewController의 생명 주기(lifecycle) 메서드입니다. 일반적으로 UI 요소의 초기 설정이나 데이터 로딩 등 초기화 작업을 이곳에서 수행합니다.
      • ( ): 매개변수가 없음을 나타냅니다.
      • {: 메서드 정의의 시작을 알리는 중괄호입니다.

swift

super.viewDidLoad()
  1. super.viewDidLoad():
    • 설명: 부모 클래스(UIViewController)의 viewDidLoad() 메서드를 호출하는 코드입니다. override 키워드를 사용하여 부모 메서드를 재정의할 경우, 일반적으로 부모의 기본 동작을 유지하기 위해 재정의된 메서드 내에서 super 키워드를 사용하여 부모 클래스의 동일한 메서드를 호출합니다. super.viewDidLoad()를 호출하지 않으면 UIViewController가 뷰를 로드하고 준비하는 데 필요한 중요한 내부 동작들이 누락될 수 있습니다.

swift

}
  1. }:
    • 설명: viewDidLoad() 메서드 정의의 끝을 알리는 중괄호입니다.

swift

@IBAction func btnSend(_ sender: UIButton) {
  1. @IBAction func btnSend(_ sender: UIButton) {:
    • 설명: btnSend()라는 메서드(함수)를 정의하는 코드입니다.
      • @IBAction: 이 어노테이션은 이 메서드가 **인터페이스 빌더(Interface Builder)**에서 UI 요소(예: 버튼)의 특정 이벤트(예: 탭)에 연결될 것임을 나타냅니다. 사용자의 액션(이벤트)에 반응하는 코드를 작성할 때 사용합니다.
      • func: 함수 정의 키워드입니다.
      • btnSend: 이 메서드의 이름입니다. 일반적으로 btn (버튼)과 그 버튼이 수행하는 동작(Send)을 조합하여 명명합니다.
      • ( ): 이 메서드의 매개변수를 정의합니다.
      • _: sender 매개변수에 대한 외부 매개변수 이름을 생략합니다. 즉, 이 메서드를 호출할 때 sender:라는 레이블 없이 바로 UIButton 인스턴스를 넘겨주면 됩니다.
      • sender: UIButton: 이 메서드의 매개변수입니다.
        • sender: 이 매개변수의 이름입니다. 일반적으로 액션을 발생시킨 UI 요소를 나타내며, UIButton 타입으로 정의되어 있습니다.
        • UIButton: 액션을 발생시킨 객체가 UIButton 클래스 타입임을 의미합니다. 이 sender 객체를 통해 어떤 버튼이 눌렸는지 등의 추가 정보를 얻을 수 있습니다.
      • {: 메서드 정의의 시작을 알리는 중괄호입니다.

swift

//Interface Builder Action(함수, 메서드)
  1. //Interface Builder Action(함수, 메서드):
    • 설명: 이 주석은 위에 선언된 @IBAction 메서드가 인터페이스 빌더와 연결된 사용자 액션임을 설명합니다.

swift

lblHello.text = "Hello, " + txtName.text!
  1. lblHello.text = "Hello, " + txtName.text!:
    • 설명: lblHello (레이블)의 텍스트 내용을 변경하는 코드입니다.
      • lblHello.text: lblHello 인스턴스의 text 프로퍼티에 접근합니다. UILabel의 text 프로퍼티는 레이블에 표시되는 문자열을 관리합니다.
      • +: 문자열을 연결(concatenate)하는 연산자입니다.
      • txtName.text!: txtName (텍스트 필드)의 text 프로퍼티에 접근합니다. UITextField의 text 프로퍼티는 사용자가 입력한 문자열을 담고 있습니다. 여기에 !가 붙은 것은 text 프로퍼티가 옵셔널 타입(String?)이기 때문에, 개발자가 이 시점에는 반드시 값이 있을 것이라고 확신하고 **강제로 언래핑(force unwrap)**하여 String 타입으로 만들었음을 의미합니다. 만약 txtName.text가 nil인 상태에서 강제 언래핑을 시도하면 런타임 오류(크래시)가 발생할 수 있으므로, 실제 앱에서는 nil 가능성을 안전하게 처리(옵셔널 바인딩 등)하는 것이 좋습니다.
      • 종합적으로, 이 줄은 "Hello, "라는 문자열에 txtName 텍스트 필드의 현재 입력 값을 붙여서 lblHello 레이블에 표시하도록 합니다.

'코딩공부' 카테고리의 다른 글

iOS 10주차  (0) 2025.11.03
iOS 9주차  (0) 2025.10.27
iOS 3주차  (0) 2025.09.15
iOS 1주차  (0) 2025.09.01
가상 메모리  (0) 2025.05.13