본문 바로가기
아이폰 개발/Swift

swift 기초 - 옵셔널, 구조체, 클래스, 열거형, 값타입 참조타입

by 인생여희 2021. 3. 3.

[1]옵셔널

 

[1] 옵셔널 : nil이 들어올 수 있다.

옵셔널이 아닌 타입에는 nil을 할당할 수 없다.

 

 

[1-1] ! 암시적 추출 옵셔널

nil 할당가능 , 기존변수처럼 사용가능

잘못된 접근으로 인한 런타임 오류 발생 가능

 

var optionValue1 :Int! = nil;

switch optionValue1 {
case .none:
    print("nil!");
    //print(optionValue1 + 1)
case .some:
    print("not nil...");
    print(optionValue1 + 1)
}

 

 

[1-2] ? : 일반 옵셔널

기존 변수처럼 사용불가능

옵셔널과 일반값은 다른 타입이므로 연산불가.

파라미터가 옵셔널 타입이므로 nil이 들어갈 수 있다.

 

func some(someOptional : Int?){
    
}


//일반 옵셔널
var optionValue :Int? = nil;

switch optionValue {
case .none:
    print("nil!");
    //print(optionValue1 + 1)
case .some:
    print("not nil...");
    print(optionValue1 + 1)
}

 

 

[2]옵셔널 꺼내서 사용하기(값추출) - 2가지 방법

 

[2-1] 옵셔널 바인딩 : nil 체크 + 안전한방법

옵셔널은 변수값을 상자에 감싸놓은것과 같다. 즉 상자다.

상자에 노크해서 - 값이 있습니까?하고 값이 있으면 값을 꺼내온다. 없으면 지나친다.

 

var myName : String? = "kkkk"
var yourName : String? =  nil

//옵셔널을 꺼내는 방법 : 두 변수중 하나라도 nil이 들어가있으면 print 문은 실행안된다.
if let name = myName , let friend = yourName {
    print("\(name) and \(friend)")
}

yourName = "nnn"

if let name = myName , let friend = yourName {
    print("\(name) and \(friend)")
    //kkkk and nnn
}

 

[2-2]옵셔널 강제 추출

뒤에 느낌표를 붙이면 옵셔널의 값을 강제로 추출된다.

nil 값이면 강제추출할때 런타임 오류가 발생한다.

 

func parmName(_ name : String){
    
    print(name)
}

var myName2: String? = "hong"

print(myName2!) //hong

myName2 = nil
//print(myName2!) //강제추출시 값이 없으므로 런타임 오류 발생

var yourName2 : String! = nil;
//parmName(yourName2) //nil값이 전달되기 때문에 런타임 오류발생

 

 

[3]구조체 (값타입)

 

struct Sample{
    var mutableProperty: Int = 100      //가변프로퍼티
    let immutableProperty: Int = 100     //불변 프로퍼티
    
    static var typeProperty : Int = 100  //타입 프로퍼티
    
    //인스턴스 메소드
    func instanceMethod() {
        print("인스턴스 메소드")
    }

    //타입 메소드
    static func typeMethod(){
        print("타입 메소드")
    }
}

 

[3- 1] 구조체 사용

//[1] 가변 인스턴스
var mutableInstance : Sample = Sample()

mutableInstance.mutableProperty = 200
//mutableInstance.immutableProperty = 200 //불변 프로퍼티라서 변경 불가.

//[2] 불변 인스턴스
let immutable : Sample = Sample()

//immutable.mutableProperty = 200 //불변인스턴스를 통해서 구조체 변수 변경 불가.
//immutable.immutableProperty = 200

//타입 프로퍼티 및 타입 메소드
Sample.typeProperty = 300
Sample.typeMethod() //타입 메소드

//인스턴스가 스태틱 변수와 스태틱 메소드에 접근 불가능!
//mutableInstance.typeProperty = 300
//mutableInstance.typeMethod()

 

 

[4]클래스 (참조타입)

 

class SampleClass{
    var mutableProperty: Int = 100      //가변프로퍼티
    let immutableProperty: Int = 100     //불변 프로퍼티
    
    static var typeProperty : Int = 100  //타입 프로퍼티
    
    //인스턴스 메소드
    func instanceMethod() {
        print("클래스의 인스턴스 메소드")
    }

    //타입 메소드 - 재정의 불가 타입 메소드 - static
    static func typeMethod(){
        print("타입 메소드")
    }
    
    //재정의 가능 타입 메소드
    class func classMethod() {
        print("재정의 가능한 타입 메소드")
    }
}


//수정 가능 인스턴스
var mutableReference : SampleClass = SampleClass()

mutableReference.mutableProperty = 200
//mutableReference.immutableProperty = 200 //수정불가 프로퍼티 수정불가

//수정불가 인스턴스
let immutableReference : SampleClass = SampleClass()

immutableReference.mutableProperty = 200   //수정불가 인스턴스라도 프로퍼티에 접근해서 수정가능
//immutableReference.immutableProperty = 200 //변수 자체가 수정불가능한 변수라서 접근불가능


//클래스 이름으로, static 으로 정의된 타입 프로퍼티 및 타입 메소드에 접근 가능
SampleClass.typeProperty = 300
SampleClass.typeMethod()

//인스턴스에서 타입 메소드, 타입 변수 접근 불가능
//mutableReference.typeProperty = 300
//mutableReference.typeMethod()

 

 

[5]열거형

 

enum은 타입이므로 대문자 카멜 케이스를 사용하여 이름을 정의 한다.

 각 case는 소문자 카멜 케이스로 정의한다.

 각 case는 그 자체가 고유의 값이다.

 

 enum 이름 {

    case 이름1

    case 이름2

 }

 

 

//[5-1] 예제 1

//열거 타입
enum Weekday {
    case mon
    case tue
    case wed
    case thu, fri, sat, sun
}

var day : Weekday = Weekday.mon
day = .tue

print(day) //tue

switch day{
case .mon , .tue , .wed , .thu:
    print("평일입니다."); //평일입니다.
case Weekday.fri:
    print("불금파티!!");

case .sat , .sun:
    print("신나는 주말.");
}


/*
 원시 값 : C언어의 enum 처럼 정수값을 가질 수도 있다.
 rawValue를 사용하면된다.
 case 별로 각각 다른값을 가져야한다.
 */

//정수 타입의 enum
enum Fruit:Int {
    case apple = 0
    case grape = 1
    case peach //2
}

print("Fruit.peach.rawValue == \(Fruit.peach.rawValue)") //2

//문자열 타입의 enum
enum School : String {
    case ele = "초등"
    case mid = "중등"
    case hig = "고등"
    case uni
}

print("School.uni.rawValue == \(School.uni.rawValue)") //uni


/*
 원시값을 통한 초기화
 rawValue를 통해 초기화 할 수 있다.
 rawValue가 case에 해당하지 않을 수 있으므로
 rawValue를 통해 초기화 한 인스턴스는 옵셔널 타입이다.
 */

let apple : Fruit? = Fruit(rawValue: 0)

if let orange:Fruit = Fruit(rawValue: 5){
    print("rawValue 5에 해당하는 케이스는 \(orange) 입니다.")
}else{
    print("rawValue 5에 해당하는 케이스가 없습니다.")
}

/* enum에 메소드가 있는 경우 */
enum Month {
    case dec, jan, feb
    case mar, apr, may
    
    func printMessage(){
        switch self {
        case .mar , .apr, .may:
        print("따스한 봄~")
            
        case .dec , .jan, .feb:
        print("추운 겨울 입니다.~")
            
        }
    }
}


Month.mar.printMessage() //따스한 봄~

 

 

[6]값 타입, 참조타입

 

Class : 참조타입(메모리 주소를 전달) , 단일 상속 가능 , 확장가능, 타입캐스팅 가능, 소멸자로 할당된 자원 해제 가능, 참조카운트

 

구조체 : 상속 불가 , 값타입(값을 복사해서 전달.), 확장가능

 

열거형 : 상속 불가, 값타입, 열거형하나하나의 case가 값이다., 확장가능

 

 구조체는 언제 사용하나?

 1.참조가 아닌 복사를 할때.(전달이나 할당될때),

 2.상속받을 필요가 없을때

 3.연관된 값을 모아서 하나의 데이터 타입으로 사용하고 싶을때.

 

참고

Swift에서는 String, Array, Dictionary 같은 기본 데이터 타입이 구조체로 구현 돼 있습니다.

그렇다는 의미는 이 값을 다른 상수나 변수에 할당하거나 함수나 메소드에 인자로 넘길 때 이 값이 복사 된다는 것입니다.

 

반면 Foundation의 NSString, NSArray, NSDictionary는 클래스로 구현 돼 있습니다.

그래서 이 데이터들은 항상 할당 되거나 전달될 때 복사 되지 않고 참조가 사용됩니다.

 

NOTE

위에서 String, Array, Dictionary 는 할당되거나 전달될 때 복사된다고 설명했습니다.

하지만 실제로 Swift에서는 최적의 성능을 위해 실제 필요할 때만 데이터가 복사됩니다.

 

식별 연산자(===)는 비교 연산자(==)와 같지 않습니다. 식별 연산자는 참조를 비교하는 것이고, 비교 연산자는 값을 비교합니다.

 

 

//구조체
struct ValueType1 {
    var property = 1
}

//클래스
class ReferencyType1{
    var property = 1
}

//구조체 인스턴스
let firstStrcutInstance = ValueType1()
var secondStructInstance = firstStrcutInstance
secondStructInstance.property = 2

print("firstStrcutInstance.property : \(firstStrcutInstance.property)")   //1
print("secondStructInstance.property : \(secondStructInstance.property)")  //2


//클래스 인스턴스
let firstRef = ReferencyType1()
var secondRef = firstRef
secondRef.property = 2

print("firstRef.property : \(firstRef.property)")   //2
print("secondRef.property : \(secondRef.property)")  //2

 

 

 

test2.zip
0.02MB

 

 

 

참조블로그

jusung.gitbook.io/the-swift-language-guide/language-guide/09-classes-and-structures

blog.yagom.net/