Swift study - 문법들의 간단한 사용 예시(2)

rbw·2022년 2월 13일
0

swift-study

목록 보기
3/17
post-thumbnail

swift 문법 (2)

클로저

// String을 반환하는 클로저
let myName : String = {
  // myName 으로 들어간다
  return "정대리"
}()
print(myName) 

let myRealName : (String) -> String = { (name: String) -> String in 
    return "개발하는 \(name)"
}
myRealName("쩡대리") // 개발하는 쩡대리 print로 감싸야 출력함

let myRealNameLogic : (String) -> Void = { (name: String) in 
    print("개발하는 \(name)")
}

매개변수로서의 클로저

클로저는 메소드로 생각하면 편함

// completion 이라는 클로저를 매개변수로 가지는 메소드 정의
func sayHi(completion: () -> void) {
  print("sayHi() called")
  sleep(2) // 2초 가량 멈추기
  // completion 클로저 실행
  completion()
}

// 메소드 호출부 에서 이벤트 종료를 알 수 있다
sayHi(completion:{
  print("2초가 지났다. 1")
})
sayHi(){
  print("2초가 지났다. 2")
}
sayHi{
  print("2초가 지났다. 3")
} // called 2초 지났다, 2초간격으로 3번 출력

// 매개변수로서 데이터를 반환하는 클로저
// 매개변수를 2개 이상 추가하려면
// completion : (String, String) -> Void) {...} 로 사용하면 된다
func sayHiWithName(completion: (String) -> Void){
  print("sayHiWithName() called")
  sleep(2)
  // 클로저를 실행과 동시에 데이터 반환
  completion("hi guys~")
}

sayHiWithName(completion:{ (comment: String) in
  print("2초 뒤 그가 말했다 ! comment: ", comment)
})
// called, 2초뒤 그가 말했다 higuys~ 출력

sayHiWithName(completion:{ comment in
  print("2초 뒤 그가 말했다 ! comment: ", comment)
})
sayHiWithName{comment in
  print("2초 뒤 그가 말했다 ! comment: ", comment)
}
sayHiWithName{
  print("2초 뒤 그가 말했다 ! comment: ", $0)
} // 매개변수가 두개이면 $0, $1 로 작성

객체 생성자, 해체자

class MyFriend {
    
    var name : String
    init(_ name: String = "이름없음"){
        self.name = name
        print("init() MyFriend 가 메모리에 올라갔다. - ", self.name)
    }

    deinit{
        print("deinit() 메모리에서 사라짐 - ", self.name)
    }

var myFriend = MyFriend("쩡대리")
let aFriend = MyFriend()
weak var aFriendToBeDestoried = MyFriend("개발하는 정대리")

상속

class Friend {
    var name : String
    
    init(_ name: String){
        self.name = name
    }
    func sayHi(){
        print("안녕?! 난 \(self.name) 라고 해")
    }
}

class BestFriend : Friend {
    // override로 부모의 메소드를 가져왔다.
    override init(_ name: String) {
        // super 로 부모의 메소드 사용
        super.init("베프 " + name)
    }
    override func sayHi() {
        super.sayHi()
    }
    func sayGoodBye(){
        print("sayGoodBye() called")
    }
}

let myFriend = Friend("쩡대리")
myFriend.sayHi()

let myBestFriend = BestFriend("영희")
// 난 베프 영희 라고 해 출력 자식에도 var name 이 있다고 생각하면 된다
myBestFriend.sayHi()

myBestFriend.name

딕셔너리

// 키 값으로 한쌍인 딕셔너리 - 사물함과 비슷
// 키 : 값
var myFriends = ["bestFriend" : "쩡대리",
                 "highschool" : "영희"
                ]
// bestFriend 라는 열쇠로 내용물을 꺼냄
let myBestFriend = myFriends["bestFriend"]
let highSchoolFriend = myFriends["highschool"]

// 해당 키로 값을 꺼낼때 저장된 값이 없다면 default 로 기본 값을 가져오도록 설정 가능
let youtubeFriend = myFriends["youtube" , default: "친구없음"]
// 값 추가 및 변경 가능
myFriends["bestFriend"] = "개발하는 정대리"

let myBF = myFriends["bestFriend"]
myFriends["newFriend"] = "철수"
let newFriend = myFriends["newFriend"]

// updateValue 메소드로 값 추가 및 변경 가능
myFriends.updateValue("수잔", forKey: "girlFriend")
let girlFriend = myFriends["girlFriend"]
myFriends.updateValue("짹슨", forKey: "bestFriend")
let myBestFriend2 = myFriends["bestFriend"]

// 빈 딕셔너리를 만드는 방법 들
let emptyDictionary1 : [String : Int] = [:]
let emptyDictionary2 = [String : Int]()

let myEmptyDictionary : [String : Int] = Dictionary<String, Int>()

myFriends.count
for item in myFriends {
    print("item : ",item)
}

파이널 클래스

// 상속이 불가능한 class
final class Friend {
    var name : String
    init(name: String){
        self.name = name
    }
}

// error
class BestFriend : Friend {
    override init(name: String){
        super.init(name: "베프 " + name)
    }
}

에러

// 자료형이 Error 인 enum
enum MismatchError: Error {
    case nameMismatch
    case numberMismatch
}

// throw 를 통해 에러를 밖으로 던진다
// 에러를 밖으로 보낸다고 메소드 반환 부분에 throws 표시
func guessMyName(name input: String) throws {
    print("guessMyName() called")
    if input != "쩡대리" {
        print("틀렸다")
        throw MismatchError.nameMismatch
    }
    print("맞췄다")
}

// - Parameter input: 사용자 숫자 입력
// - Returns: bool 맞췄는지 여부
func guessMyNumber(number input: Int) throws -> Bool {
    print("guessMyNumber() called")
    if input != 10 {
        print("틀렸다")
        throw MismatchError.numberMismatch
    }
    print("맞췄다")
    return true
}

// 해당 메소드에서 에러가 던져져도 에러 처리를 안할려면 try에 ?를 붙여준다
// try? guessMyName(name: "이대리")

// do catch 를 이용해 외부로 던져진 에러를 잡아서 에러 처리가 가능
do {
   try guessMyName(name: "이대리")
} catch {
    print("잡은 에러: \(error)")
}

// 에러를 던지는 반환형이 있는 메소드의 경우에는
// 에러가 던져졌을때 데이터를 반환하지 않고 바로 catch블럭으로 들어가는 것을 볼 수 있다.
do {
   let receivedValue = try guessMyNumber(number: 9)
} catch {
    print("잡은 에러: \(error)")
}

스트럭트 mutating

class Friend {
    var name : String
    func changeName(newName: String){
        self.name = newName
    }
    init(_ name: String){
        self.name = name
    }
}

var myFriend = Friend("쩡대리")
myFriend.changeName(newName: "개발하는 쩡대리")
myFriend.name

// 구조체의 경우
// struct 는 참조(메모리 주소)인 클래스와 다르기 때문에
// struct 구조의 멤버 변수 값을 변경(mutate) 하기 위해서는 mutating 키워드가 필요
struct BestFriend {
    var name : String
    mutating func changeName(newName: String){
        self.name = newName
        print("newName: ", newName)
    }
}
var myBestFriend = BestFriend(name: "쩡대리")
myBestFriend.changeName(newName: "호롤롤로!")

Set

var myNumberSet : Set<Int> = Set<Int>()

// 1,2,3 3개만 들어간다
myNumberSet.insert(1)
myNumberSet.insert(2)
myNumberSet.insert(2)
myNumberSet.insert(3)

// set 내부 값은 고유 해야한다
// 배열과 다르게 순서가 정해져있지 않다.
// 매번 출력되는 값들의 순서가 다르다
for aNumber in myNumberSet {
    print("aNumber: ", aNumber)
}

var myBestFriends : [String] = ["철수", "영희", "수지"]
myBestFriends.contains("수지")

var myFriends : Set<String> = ["철수", "영희", "수지"]
// 콜렉션 [배열, 셋, 딕셔너리, 튜플] 등이 가지고 있는 기본 메소드 들을 제공한다.
myFriends.contains("쩡대리")

// 수지의 Set 인덱스를 가져온다.
// 인덱스가 없을수 있으므로 옵셔널을 언래핑하는 if let을 사용한다
// 인덱스가 없다면 nil을 반환하기 때문
if let indexToRemove = myFriends.firstIndex(of: "수지") {
    print("indexToRemove: ", indexToRemove)
    myFriends.remove(at: indexToRemove)
}

프로토콜

// 약속 이라고 생각하면 된다
/// **delegate, **able, **ing 이름을 붙이는 방식들
protocol Naming {
    // 우리는 이런 변수를 가지고 있을겁니다 라고 약속
    var name : String { get set }
    // 우리는 이런 메소드를 가지고 있을겁니다 라고 약속
    func getName() -> String
}
// 프로토콜에 맞지 않은 선언은 error 를 뜨게 해줌
struct Friend : Naming{
    var name: String
    func getName() -> String {
        return "내 친구: " + self.name
    }
}
var myFriend = Friend(name: "쩡대리")
myFriend.getName() 


// 프로토콜 상속
protocol Naming {
    var name: String { get set }
    func getName() -> String
}
protocol Aging {
    var age: Int { get set }
}

// 이름, 나이 프로토콜을 상속하는 사용자 프로토콜
protocol UserNotifiable : Naming, Aging {
    
}

// 두 프로토콜을 동시에 준수해야한다.
// 프로토콜 = 약속
class MyBestFriend: UserNotifiable {
    var name: String = ""
    var age: Int
    func getName() -> String {
        return self.name
    }
    init(_ name: String, _ age: Int){
        self.name = name
        self.age = age
    }
}
struct MyFriend : UserNotifiable {
    var name: String
    func getName() -> String {
        return self.name
    }
    var age: Int   
}


// 프로토콜 확장
// 프로토콜에는 선언만 하고 로직을 담을 수 없다.
protocol Naming {
    var lastname: String { get set }
    var firstname: String { get set }
    func getName() -> String
}
// 프로토콜을 확장 익스텐션하면 메소드의 로직을 담을 수 있다.
extension Naming {
    func getFullname() -> String{
        return self.lastname + " " + self.firstname
    }
}
struct Friend : Naming {
    var lastname: String
    var firstname: String
    func getName() -> String {
        return self.lastname
    }
}
let myFriend = Friend(lastname: "쩡", firstname: "대리")
myFriend.getName()
myFriend.getFullname()


// 프로토콜 associatedType
protocol PetHaving {
    // associatedtype 을 통해 제네릭으로 어떠한 자료형이든 가질수 있다.
    associatedtype T
    var pets: [T] { get set }
    // mutating 을 통해 스트럭트 에서 맴버 변수 값 변경
    mutating func gotNewPet(_ newPet: T)
}
extension PetHaving {
    mutating func gotNewPet(_ newPet: T){
        self.pets.append(newPet)
    }
}
enum Animal {
    case cat, dog, bird
}
struct Friend : PetHaving {
    // 애완동물이 Animal 내부 case들만 입력이 가능
    var pets: [Animal] = []
}

struct Family : PetHaving {
    // 애완동물이 String
    var pets: [String] = []
}

typealias 별칭

// typealias 별칭으로 내가 원하는 것을 다르게 부를수 있다.
protocol Naming {
    func getName() -> String
}
protocol Aging {
    func getAge() -> Int
}

// 프로토콜 별칭 설정
// Naming이랑 Aging을 같이 쓴다는 의미
typealias Friendable = Naming & Aging
typealias FullNaming = Naming

struct Friend : Friendable {
    var name : String
    var age : Int
    func getName() -> String {
        return self.name
    }
    func getAge() -> Int {
        return self.age
    }
}

// 자료형, 클래스, 스트럭트, 클로저 등 모두 별칭 설정이 가능하다
typealias FriendName = String
var friendName : FriendName = "정대리"

typealias Friends = [Friend]
var myFriendsArray : Friends = []

// 클로저를 StringBlock 이라는 별칭으로 설정하였다.
typealias StringBlock = (String) -> Void
func sayHi(completion : StringBlock){
    print("안녕하세요?")
    completion("오늘도 빡코딩 하고 계신가요?")
}

sayHi(completion: { saying in
    print("여기서 받음 : ", saying)
})

typealias MyType = MyClass.MY_TYPE
class MyClass {
    enum MY_TYPE {
        case DOG
        case CAT
        case BIRD
    }
    var myType = MyType.DOG
}
var myClass = MyClass()
myClass.myType = MyType.DOG
print("myClass.myType:", myClass.myType)

lazy 키워드

struct Pet {
    init(){
        print("Pet 이 생성되었다.")
    }
}
struct Friend {
    var name: String
    // lazy 키워드로 바로 메모리에 올리지 않고 사용하는 시점에 메모리에 올려서 사용합니다.
    lazy var pet : Pet = Pet()
    init(_ name: String){
        self.name = name
        print("Friend 가 생성됨")
    }
}
var myFriend = Friend("쩡대리")
// 접근했을때 Pet이 생성되서 메모리에 올라가는 걸 볼 수 있습니다.
myFriend.pet

고차함수 (Higher Order Functions)

매개변수로 클로저를 받고 클로저를 반환하는 함수

func getName(_ name: String) -> String {
    return "내 이름은 \(name)"
}
var getNameClosure : (String) -> String
func sayHello( getName: (String) -> String, name: String) -> String {
    return getName(name)
}

let resultOne = sayHello(getName: getName(_:), name: "호롤롤로")
// 1. sayHello의 매개변수로 getName을 받고
// 2. return의 getName에서 name으로 "호롤롤로"를 받아서 return

let numbers = [3, 7, 4, -2, 9, -6, 10, 1]
let stringNumbers : [String] = numbers.map { (aNumber: Int) -> String in
    return "\(aNumber) 입니다."
}
let evenNumbers : [Int] = numbers.filter { aNumber in
    return aNumber % 2 == 0
} // [4, -2, -6, 10]
let oddNumbers : [Int] = numbers.filter { aNumber in
    return aNumber % 2 != 0
} // [3, 7, 9, 1]

고차함수 - sort, sorted(정렬)

var myArray = [3, 4, 88, 99, 5, 6, 7 , 8, 10, 20, 100]
var ascendingArray = myArray.sorted()

// sort는 원본이 바뀐다
myArray.sort()
var descendingArray = myArray.sorted(by: >)

myArray.sort(by: >)
profile
hi there 👋

0개의 댓글