ABOUT ME

-

Today
-
Yesterday
-
Total
-
  • Swift 문법 기초
    iOS/Swift 기초문법 2022. 3. 25. 00:26
    <상수와 변수>
    // 상수 - 변하지 않는 값. 변경 불가
    let a: Int = 100
    let a = 100
    a = 200 // 불가
    
    // 변수
    var b: Int = 200
    b = 300
    
    // 전체공간 : 변하지 않는 값이므로 let으로 - 개발자가 실수로 값을 변경했을때 컴파일에러
    // 사용가능 공간 : 변하는 값이므로 var로 선언
    // Character는 문자 만 저장 가능, 문자열 저장 불가
    var someCharacter: Character = "안"
    var someString: String = "안녕하세요"
    
    // 타입 추론 - 자료형을 명시하지 않아도 컴파일시에 알아서 지정
    var number = 10

     

    <컬렉션 타입>
    // 빈 array 생성 
    var numbers: Array<Int> = Array<Int>()
    numbers.append(1)
    numbers.append(2)
    numbers.append(3)
    
    // 인덱싱
    numbers[0]
    numbers[1
    
    // 삽입
    numbers.insert(4, at:2)
    // 제거
    numbers.removew(at:0)
    
    // array 생성 방법 2 - 문자열 array
    var names = [String]()
    var names: [String] = []
    
    // dictionary 생성 - <key자료형, value자료형>
    var dic: Dictionary<String, Int> = Dictionary<String, Int>()
    var dic: [String:Int] = ["양지현":1]
    dic["김철수"] = 3
    dic["김민지"] = 5
    
    dic.removeValue(forKey: "김민지")
    
    // set생성 - 축약형 코드가 없음
    var set: Set = Set<Int>()

     

    <함수>
    func sum(a: Int, b: Int) -> Int {
        return a + b
    }
    
    sum(a: 5, b: 3)
    
    func hello() -> String {
        return "hello"
    }
    
    hello()
    
    // 반환값이 없는 함수
    func printName() -> Void {
        
    }
    
    
    // 매개변수에 기본값이 할당된 함수
    func greeting(friend: String, me: String = "gunter") -> T {
        print("Hello, \(friend)! I'm \(me)")
    }
    greeting(friend: "Albert") // print("Hello, Albert! I'm gunter")
    
    // 전달인자 레이블 사용
    func sendMessage(from myName: String, to name: String) -> String{
        return "Hello \(name)! I'm \(myName)"
    }
    
    // 함수 호출시 전달인자 레이블을 사용하면 매개변수의 역할을 명확히 표현 가능 -> 코드 가독성이 좋아짐
    sendMessage(from: "Gunter", to: "Json")
    
    // 와일드카드 식별자 - 매개변수 이름을 안적고 값만 넘겨줄 수 있음
    func sendMessage(_ name: String) -> String {
        return "Hello \(name)"
    }
    sendMessage("권태완")
    
    // 매개변수에 여러개의 값
    func send(me: String, friends: String...) -> String {
        return "hello \(friends)! I'm \(me)"
    }
    // 배열로 넘겨받음
    send(me: "Gunter", friends:"Json","Alberto","Stella")
    // "hello["Json","Alberto","Stella]! I'm "Gunter"
    
    // 스위프트의 함수는 일급객체 - 함수를 변수, 상수에 할당 가능 & 매개변수에도 할당 가능

     

    <조건문>
    let temp = 30
    
    switch temp {
    case -20...9:
        print("가을입니다")
    case 10...14:
        print("겨울입니다")
    case 15...20:
        print("봄입니다")
    default:
        print("이상 기후 입니다")
    }
    
    
    // 반복문
    // repeat-while문은 조건이 안맞아도 반드시 한번은 실행
    var x = 6
    repeat{
        x+=2
    }while x<5 // x<5일때 실행
    
    print(x) // 8

     

    < 옵셔널 >

    - 값이 있을수도 있고 없을수도 있는 변수

    - nil을 지정하려면 type뒤에 ? (물음표)

    - 초기값을 지정하지 않으면 기본적으로 nil이 할당됨

    - 일반적인 프로그래밍 언어에서는 null값 변수에 접근하면 런타임에러 발생, 프로그램 종료 (obj-c의 null point error)

      스위프트는 안정성이 장점인 언어이므로 옵셔널 기능 존재

     

    var name: String? // 기본값이 nil
    var opitionalName: String? = "Gunter"
    print(optionalName) // "Optional("Gunter") 
    // Optional로 감싸져 있음 -> 일반타입 변수와 결합될 수 없음 -> 옵셔널 바인딩으로 벗겨내기
    
    var num: Int? = 3
    print(number)
    print(number!)
    // 1. 옵셔널 강제 해제 
    
    // 옵셔널 강제 해제는 위험한 방법 - 값이 nil인 옵셔널을 강제해제 하면 프로그램 종료됨
    // 2. 안전하게 옵셔널을 해제하려면 비강제 옵셔널 바인딩
    if let result = number{ // 옵셔널을 추출해서 result에 할당
        print(result)
    } else{ // 추출이 실패할 경우
    }
    
    func test() {
        let number: Int? = 5
        guard let result = number else { return } // 함수 종료
        print(result)
    }
    // if문으로 옵셔널 바인딩을 하면 옵셔널이 추출된 변수, 상수를 if문 내에서만 사용 가능
    // guard를 사용해 추출하면 함수 전체 구문에서 사용 가능
    
    
    // 3. 컴파일러에 의한 자동 해제
    // 옵셔널 값을 비교연산을 이용해서 다른 깂과 비교하면 자동 해제됨
    let value: Int? = 6
    if value == 6{
        print("value가 6")  // 이걸로 출력됨
    } else{
        print("value가 6이 아님")
    }
    
    // 4. 묵시적 해제
    let string = "12"
    var stringToInt: Int! = Int(string)
    // 사용할 때 묵시적으로 해제됨

     

    <구조체>

    - 클래스 vs 구조체 : 구조체의 인스턴스는 값 / 클래스의 인스턴스는 참조 타입

    - 구조체와 클래스는 대문자로 시작 (파스칼표기법)

     

    - 구조체를 사용하기 위해서는 객체(인스턴스)를 생성해야 함 (메모리에 로드). --> 바이트패딩 개념

    - 구조체 안에 따로 생성자를 선언해주지 않아도 기본적으로 생성자가 만들어짐 - 프로퍼티 이름으로 자동 선언

    - 구조체 안에는 메소드도 구현 가능

    struct User{
        var nickname: String
        var age: Int
    }
    
    var user = User(nickname:"jh", age:23)
    user.nickname
    user.nickname = "ablert"
    
    // 구조체 내에 함수 선언
    struct User{
        var nickname: String
        var age: Int
    
        func inform() {
            print("\(nickname)은 \(name)살")
        }
    }
    
    var user = User(nickname:"jh", age:23)
    user.nickname
    user.nickname = "ablert"
    
    user.inform()

     

    <클래스>
    class Dog {
        var name: String = ""
        var age = Int = 0
        // 클래스에서는 클래스 인스턴스를 생성하기 위한 생성자를 선언해주어야함 
        init() {
        }
    
        // 메소드(함수)
        func introduce(){
            print("name \(name) age \(age)"
        }
    
    var dog = Dog() // 클래스 인스턴스 생성
    dog.name = "CoCo"
    dog.age = 3
    cob.introduce()  // name CoCo age 3

     

    <초기화 구문>

    - 클래스 구조체 또는 열거형의 인스턴스를 사용하기 위한 준비 과정

    - 매개변수로 전달받지 않고 생성자 내에서 프로퍼티 값을 대입해 초기화도 가능

    class User{
        var nickname:String
        var age:Int
        
        // 초기화 구문
        init(nickname:String, age:Int){
            self.nickname = nickname
            self.age = age
        }
        
        init(age:Int){
            self.nickname = "ablert"
            self.age = age
        }
    }
    
    var user = User(nickname: "gunter", age: 20)
    user.nickname  // gunter
    user.age       // 20
    
    var user2 = User(age: 27)
    user2.nickname  // ablert
    user2.age       // 27

    - deinit : 클래스 인스턴스 에만 구현 가능

    class User{
        var nickname:String
        var age:Int
        
        init(age:Int){
            self.nickname = "ablert"
            self.age = age
        }
        
        deinit {
            print("deinit user")
        }
    }
    
    var use3: User? = User(age: 23)
    user3 = nil  // deinit user가 출력됨
    // 인스턴스가 더이상 필요하지 않다고 판단되면 옵셔널로 선언, nil 할당

     

    <프로퍼티>

    1) 저장프로퍼티 - 인스턴스의 변수(var), 상수(let)를 의미

    2) 연산프로퍼티 - 값을 저장하는 것이 아니라 특정 연산을 실행하는 결과값

    3) 타입프로퍼티 - 특정 인스턴스에서 사용되는 것이 아닌 특정 타입에서 사용되는 프로퍼티

     

     

    1) 저장프로퍼티

    struct Dog {
        // 1) 저장프로퍼티: 구조체 내에 선언된 변수,상수
        var name: String
        let gender: String
    }
    
    var dog = Dog(name: "gunter", gender: "Male")
    print(dog)
    dog.name = "양지현"
    dog.gender = "Female" // 에러: 상수로 선언된 프로퍼티는 값 변경 불가
    
    let dog2 = Dog(name:"gunter", gender: "male") // 구조체를 상수로 선언
    dog2.name = "양지현" // 에러: 구조체 프로퍼티가 var로 선언됐을지라도 구조체가 상수로 선언되면 변경 불가

    구조체 인스턴스는 value 타입이기 때문에 상수(let)로 선언하면 구조체 내부 프로퍼티도 상수(let)가 되어 변경할수 없음

    (var로 선언된 프로퍼티일지라도 구조체 인스턴스가 let으로 선언되면 변경 불가)

     

    클래스는 참조타입이라 구조체와 다른 결과가 나옴

    클래스 인스턴스는 상수(let)로 선언해도 변수(var)로 선언한 프로퍼티 값을 바꿀수 있음 (상수로 선언한 프로퍼티는 당연히 변경 불가)

    class Cat{
        var name: String
        let gender: String
        
        init(name:String, gender:String){
            self.name = name
            self.gender = gender
        }
    }
    
    let cat = Cat(name: "json", gender: "male")
    cat.name = "gunter"
    print(cat.name)  // gunter로 변경됨

     

    2) 연산프로퍼티

    저장프로퍼티는 구조체, 클래스에서만 사용가능한 반면

    연산프로퍼티는 구조체, 클래스, 열거형에서 사용 가능

    - 값을 직접적으로 저장하지 않는 대신 getter/setter를 사용해서 값을 연산하고 프로퍼티에 전달해줌

    struct Stock{
        var avgPrice: Int
        var quantity: Int
        
        // 계산프로퍼티 - Stock인스턴스 생성 후 purchasePrice값을 접근하면 계산된 값을 가져옴
        var purchasePrice : Int {
            get {
                return avgPrice * quantity
            }
            // 새로운 값이 설정되면 매개변수 newPrice로 전달됨. purchasePrice값이 변경되면 newPrice도 변경됨
            set(newPrice) {
                avgPrice = newPrice / quantity
            }
            // 매개변수 이름을 설정하지 않으면 newValue 디폴트 네임 사용
            set{
                avgPrice = newValue / quantity
            }
        }
    }
    
    
    var stock = Stock(avgPrice: 2300, quantity: 3)
    stock.purchasePrice  // 2300*3 출력
    stock.purchasePrice = 3000
    stock.avgPrice       //  3000/3 출력

     

    3) 프로퍼티 옵저버

    - 프로퍼티 값의 변화를 관찰하고 반영

    - 프로퍼티가 set될때마다 호출됨

    - 세 가지 경우에만 사용 가능

    1.  저장프로퍼티

    2. 오버라이딩 된 저장, 계산프로퍼티

     

    willSet옵저버

    : 값이 저장되기 직전에 호출,  새로 저장될 프로퍼티 값을 상수매개체로 전달 - 이름을 지정하지 않으면 기본값 newValue

    didSet옵저버

    : 값이 저장된 직후에 호출,  프로퍼티 기본값이 상수 매개변수로 저장 - 이름을 지정하지 않으면 기본값 oldValue

    class Account {
        var credit: Int = 0 {
            willSet { // 값이 저장되기 직전에 호출
                print("잔액이 \(credit)원에서 \(newValue)원으로 변경될 예정입니다.")
            }
            didSet { // 값이 저장된 직후에 호출
                print("잔액이 \(oldValue)에서 \(credit)원으로 변경되었습니다.")
            }
        }
    }
    
    var account = Account()
    account.credit = 1000
    // 잔액이 0원에서 1000원으로 변경될 예정입니다.
    // 잔액이 0원에서 1000원으로 변경되었습니다.

    4) 타입 프로퍼티

    - 인스턴스 생성 없이 객체 내의 프로퍼티에 접근 가능케 함

    - 프로퍼티 타입 자체와 연결

    - 저장, 연산 프로퍼티에서만 사용 가능

    - static키워드로 정의

    // static : 인스턴스를 생성하지 않고도 프로퍼티 활용 가능
    struct staticStruct {
        static var storedTypeProperty = "some value"
        static var computedTypeProperty: Int{
            return 1
        }
    }
    staticStruct.storedTypeProperty = "hello"
    staticStruct.computedTypeProperty

     

    <클래스와 구조체의 차이>

    • 공통점

    1) 값을 저장할 프로퍼티 선언 가능

    2) 함수적 기능을 하는 메소드 선언 가능

    3) 내부값에 .을 사용하여 접근 가능

    4) 생성자를 사용해 초기상태 설정 가능

    5) extension을 사용해 기능 확장 가능

    6) protocol을 재택하여 기능 설정 가능

     

    •  차이점

    1. 클래스

    1) 참조타입

    -> 메모리 스택 영역에는 포인터(인스턴스 메모리 주소)만 할당 / 실질 데이터는 힙 영역에 할당

    -> ARC로 메모리 관리

    2) 같은 클래스의 인스턴스를 여러개의 변수를 할당하면 하나의 인스턴스만 가리키는 메모리 주소만 복사됨 

    -> 같은 클래스의 인스턴스를 여러개의 변수를 할당한 뒤, 값을 변경시키면 모든 변수에 영향을 줌 

     

    2. 구조체

    1) 값 타입

    - 변수를 할당하면 스택영역에 값 저장 -> ARC로 메모리 관리 불가

    - 구조체 변수를 새로운 변수에 할당할 때마다 새로운 구조체가 할당됨 

    -> 같은 구조체를 여러 개의 변수에 할당한 뒤 값을 변경하더라도 다른 변수에 영향X

    class SomeClass{
        var count: Int = 0
    }
    
    struct SomeStruct{
        var count: Int = 0
    }
    
    var class1 = SomeClass()
    var class2 = class1
    var class3 = class1
    
    class3.count = 2
    class1.count // 2로 변경됨
    // 같은 클래스 인스턴스를 할당한 변수의 값을 변경하면 참조된 인스턴스의 값이 변경됨
    // 변수를 복사하더라도 하나의 인스턴스 주소값을 가리키고 있기 때문에 복사본,원본이 모두 같은 값을 갖게 됨
    
    
    // 구조체는 여러곳에 복사해도 값 자체가 복사되기 때문에 다른 구조체의 값에 영향X
    // 매 번 새로운 메모리가 할당됨
    var struct1 = SomeStruct()
    var struct2 = struct1
    var struct3 = struct1
    
    struct2.count = 2
    struct3.count = 4
    
    struct1.count // 0
    struct2.count // 2
    struct3.count // 4

     

    <상속>
    class Vehicle {
        var currentSpeed = 0.0
        var description: String {
            return "traveling at \(currentSpeed) miles per hour"
        }
        func makeNoise(){
            
        }
    }
    
    class Bicycle : Vehicle {
        var hasBasket = false
    }
    
    // 부모클래스인 Vehicle에서 정의한 프라퍼티를 활용 가능
    var bicycle = Bicycle()
    bicycle.currentSpeed // 0.0
    bicycle.currentSpeed = 15.0
    bicycle.currentSpeed // 15로 변경
    
    // overriding
    class Train : Vehicle {
        override func makeNoise() {
            print("choo choo")
        }
    }
    let train = Train()
    train.makeNoise() // choo choo 출력

    -  서브클래스에서 슈퍼클래스의 메소드를 재정의 했을 때, 

    슈퍼클래스의 특성을 서브클래스에서 사용하고 싶으면 super 키워드 사용

    class Vehicle {
        var currentSpeed = 0.0
        var description: String {
            return "traveling at \(currentSpeed) miles per hour"
        }
        func makeNoise(){
            print("speaker on")
        }
    }
    
    // overriding
    class Train : Vehicle {
        override func makeNoise() {
            super.makeNoise()
            print("choo choo")
        }
    }
    
    let train = Train()
    train.makeNoise()
    // speaker on 출력
    // choo choo  출력

    - 프로퍼티 overiding

    class Vehicle {
        var currentSpeed = 0.0
        var description: String {
            return "traveling at \(currentSpeed) miles per hour"
        }
        func makeNoise(){
            print("speaker on")
        }
    }
    
    class Car : Vehicle {
        var gear = 1
        override var description: String {
            return super.description + " in gear \(gear)"
        }
    }
    
    let car = Car()
    car.currentSpeed = 30
    car.gear = 2
    print(car.description)
    // traveling at 30 miles per hour in gear 2
    class Vehicle {
        var currentSpeed = 0.0
        var description: String {
            return "traveling at \(currentSpeed) miles per hour"
        }
        func makeNoise(){
            print("speaker on")
        }
    }
    
    class Car : Vehicle {
        var gear = 1
        override var description: String {
            return super.description + " in gear \(gear)"
        }
    }
    
    let car = Car()
    car.currentSpeed = 30
    car.gear = 2
    print(car.description)
    // traveling at 30 miles per hour in gear 2
    
    class AutomaticCar : Car {
        override var currentSpeed: Double {
            didSet {
                gear = Int(currentSpeed / 10) + 1
            }
        }
    }
    
    let automatic = AutomaticCar()
    automatic.currentSpeed = 35.0  // gear는 4로 변경
    print("AutomaticCar: \(automatic.description)")
    // AutomaticCar: traveling at 35.0 miles per hour in gear 4

    - final키워드를 붙이면 오버라이드 할 수 없음

    class Vehicle {
        final var currentSpeed = 0.0
        var description: String {
            return "traveling at \(currentSpeed) miles per hour"
        }
        func makeNoise(){
            print("speaker on")
        }
    }
    
    class AutomaticCar : Car {
        override var currentSpeed: Double {  // 컴파일 에러 발생
            didSet {
                gear = Int(currentSpeed / 10) + 1
            }
        }
    }

    'iOS > Swift 기초문법' 카테고리의 다른 글

    Swift 문법 기초 2편  (0) 2022.03.27
    Swift - 일급객체  (0) 2022.03.25

    댓글

Designed by Tistory.