Chapter2

Done: No
DueDate: March 14, 2022

init은 미리 구현된 타입 메서드일까? → 함수!
INITIALIZERS ARE FUNCTIONS

Function

func 이름 (....) - > return type { }

  • (....) : parameter list

    • parameter name, colon, parameter Type
  • return의 역할

    • 함수를 중지한다
    • 값을 반환한다
    • 함수 body에 한 줄 밖에 없으면 그 code line은 반드시 return 라인이 된다
  • argument : 함수의 parameter에 넣는 값

  • 신발상자(변수선언)에 이름 안 붙이고 싶을 때

    • _
    • @discardableResult

Function signature

  • 함수의 parameter 리스트와 return type을 표기하는 것
    func signature(data : Int) -> Int {
    ///
    }
    
    //signature함수의 function signature는 
    //  (Int) -> Int
    • 함수가 값을 return 하지 않는 경우 () 혹은 Void 로 표시

Overloading

In Swift, function overloading is legal (and common).”

  1. 오버로딩이란?
  • 이름이 같지만 signature가 달라서(리턴타입, parameter type 등으로 구분됨) 구분되는 함수가 되는 것 .
  • external parameter name(argument label)으로 구분되는 것은 overloading의 경우가 아닌 그저 다른 함수(메서드)가 되는 것이다.
  • method overloading 예시
```swift
func test(_ forwhat: Int) {
}

func test(_ forwhat: String) {
}
```
  1. 오버로딩 메소드들을 쓸 땐 타입추론에 기대지말고 직접 타입을 명시하는게 좋다. 또 그래야만 쓸 수 있는 경우도 있다.

    func overloadingTestOne(_ data: String) {
        print(data)
    }
    
    func overloadingTestOne(_ data: String) -> String {
        return data
    }
    
    let test = overloadingTestOne(1)
    //Constant 'test' inferred to have type '()',
    // which may be unexpected
    
    • 타입추론이 가능한 경우
      func testOfTest(_ forTest: String) {
      }
      let testOne = test("1")
      let testOfTest = testOfTest(testOne)
      • 여기서 testOfTest는 string을 파라미터 타입으로 명시했기 때문에 컴파일러 입장에선 Int를 인자로 받는 test가 아님을 확신할 수 있음
  • 타입을 명시하는 방법
    1. assignment 뒤에 타입명시

    2. as 이용해 타입 캐스팅 하여 타입을 명시

      
      let test: String = overloadingTestOne(1)
      let test = overloadingTestOne("1") as String

Parameters

External Parameter Names == argument name

  • 왜 써야하나유?

    1. 각각의 argument의 목적을 분명히 한다
      1. argument name은 함수의 행동에 어떤 영향을 주는지에 대한관점에서 그 단서를 주는 방향으로 명명해야한다
    2. 같은 함수 이름을 가졌더라도 parameter name 이 다르면 구분된다.
    3. obj-c 나 Cocoa같은 것과 Swift가 interface 하는데 도움을 준다. 왜냐하면 obj-c, cocoa는 거의 항상 exeternal name이 있기 때문이다.
  • 어떻게 구분되나유?

    • 이름을 하나만 쓰면 자동으로 external parameter name(argument name)이자 internal name으로 정해짐
    • 만약 이름을 공백으로 나누어서 쓰면 가장 왼쪽의 이름이 argument, 그 다음의 이름이 parameter
    • 만약 argument 이름을 omit(_) 한다면 함수 호출 시 이름이 보이지 않는다.

Parameter value의 기본값

  • 함수 파라미터 타입 뒤에 assignment opertaor(=)이용해 기본값 선언
    func isMeGenius(answer: Bool = true) -> Bool {
    	//
    }

Variadic Parameter

  • 여러개의 파라미터를 입력할 수 있는 것
    func addALot(of fruits: Fruit ...) {
    //
    }
    
    let addingFruit = addALot(Apple, Banana, Grape)

Ignored Parameter

  • argument name 을 omit하는 것
    func ignore(_ data: Int) {
    //
    }

Modified Parameters

  1. Swift

    • 기본적으로 함수바디 내부에서 쓰이는 파라미터들은 상수
    1. Value Type(Struct)
    • struct타입의 value를 변경하고 싶다면 inout 키워드를 파라미터 타입 앞에 작성 ⇒ 함수 바디 내부로 파라미터 변수를 전달하지 않고 해당 파라미터(인자로 들어온 변수)의 address를 전달하기 위해 & 키워드를 함수 호출 시 인자 앞에 추가함

    b. reference Type(Class) - 클래스는 그 자체로 mutable 하다

    • 클래스인스턴스가 담긴 신발상자가 상수라고 하더라도 속성 변경가능(단 속성이 mutable해야함)

    c. 구조체의 경우 인자로 들어가는 값을 변경해주싶다면 변수여야하며, 클래스는 let 이어도 상관없다.

    • 구조체는(value타입) 인스턴스의 copy가 전달되는 것이지만 클래스(reference타입)는 그 자체가 전달되는 것이기 때문
  2. Calling Objective-C with Modifiable Parameters

  • inout 대신 UnsafeMutablePointer 사용
  • 언제쓰이나요?
    1. UIColor 의 호출 과정

      • UIColor 를 만드려면 4개의 값이 필요하다(빨,초,파, 알파)
      • Color를 만든는 메소드는 이 4개의 값을 한 꺼번에 받아서 하나의 결과로 나타내야한다. 이는 Object-C가 런타임에 해야하는 일 ⇒ 아래와 같이 Bool타입으로 반환하며 그 내부에서 각각의 변수들을 전달해 그 변수들의 값을 바꾼다.
        func getRed(_ red: UnsafeMutablePointer<CGFloat>,
            green: UnsafeMutablePointer<CGFloat>,
            blue: UnsafeMutablePointer<CGFloat>,
            alpha: UnsafeMutablePointer<CGFloat>) -> Bool {
        	// 빨간색으로 바꾸는 로직 구현 
        }
        
        let c = UIColor.purple
        var r : CGFloat = 0
        var g : CGFloat = 0
        var b : CGFloat = 0
        var a : CGFloat = 0
        c.getRed(&r, green: &g, blue: &b, alpha: &a)// now r, g, b, a are 0.5, 0.0, 0.5, 1.0
    2. Calling Objective-C with Modifiable Parameters

      이해안됨

함수 내부의 함수

  • 외부 함수(자신이 속한 함수)를 도와주는 역할로서 존재하는게 좋다.

Recursion, 재귀

  1. 재귀 : 함수 스스로가 자신을 호출하는 것
  2. 잘못쓰면 infinit loop를 만들 수 있지만 잘 쓰면 함수 자체로 stopper를 만들 수 있다.
func countDownFrom(_ ix:Int) {
    print(ix)
    if ix > 0 { // stopper
        countDownFrom(ix - 1) // recurse!
    }
}
countDownFrom(5) // 5, 4, 3, 2, 1, 0

Value로서의 function

“ In Swift, a function is a first-class citizen”

= 즉 함수를 Value로서 사용할 수 있다.

= paramter Value(argument) 로서도 사용 가능한 이유

  • 장점
    • 매게변수로 받으면 해당 함수가 뭔지 몰라도 호출할 수 있다.

      typealias VoidVoidFunction = () -> ()
      func dothis(_ f:VoidVoidFunction) {
          f()
      }
      
      // f()가 뭔지 모르지만 dothis함수를 이용해 호출 가능 
  • 어디서 많이 쓰이나요? - Cocoa API에서 많이 쓰임

    The Cocoa API is full of situations where you’ll pass a function to be called by the runtime in some special way or at some later time.

    • ex : selector

Define and call

  • call 하는 법 : 코드블럭 뒤에 () 붙임
  • 매게변수에 클로저가 들어가는 경우 아래와 같이 리팩토링을 진행하는게 좋다.
//출처 : iOS14 Programming Fundamentals with Swift Chapter2
let para = NSMutableParagraphStyle()
para.headIndent = 10
para.firstLineHeadIndent = 10

// ... more configuration of para ...
content.addAttribute( // content is an NSMutableAttributedString”
		.paragraphStyle, value:para, range:NSRange(location:0, length:1))

// refactoring
content.addAttribute(
    .paragraphStyle,
    **value: {
        let para = NSMutableParagraphStyle()
        para.headIndent = 10
        para.firstLineHeadIndent = 10
        // ... more configuration of para ...
        return para
    }()**,“range:NSRange(location:0, length:1))

Anonymous Funtions = Closure

  1. 클로저 = 바디에서 사용하는 변수 등의 reference를 캡쳐한다.

    = "see” variables and functions declared in a surrounding scope

    = a function is a closure and that it captures external variables referred to in its body

  • 코드 및 사용 예시
    //출처 : iOS14 Programming Fundamentals with Swift Chapter2
    func imageOfSize(_ size: CGSize, _ whatToDraw: () -> ()) -> UIImage {
        UIGraphicsBeginImageContextWithOptions(size, false, 0)
        whatToDraw()
        let result = UIGraphicsGetImageFromCurrentImageContext()!
        UIGraphicsEndImageContext()
        return result
    }
    
    let image = imageOfSize(CGSize(width: 45, height: 20)) {
        let p = UIBezierPath(roundedRect: CGRect(x: 0, y: 0, width: 45, height: 20), cornerRadius: 8)
        p.stroke()
    }
    
    //MARK:- firstRefactor
    let sz = CGSize(width: 45, height: 20)
    let firstRefactoredImage = imageOfSize(sz) {
        let p = UIBezierPath(roundedRect: CGRect(origin: CGPoint.zero, size: sz), cornerRadius: 8)
        p.stroke()
    }
    
    //MARK:- SecondRefactor
    func makeRoundedRectangle(_ sz: CGSize) -> UIImage {
        let image = imageOfSize(sz) {
            let p = UIBezierPath(roundedRect: CGRect(origin: CGPoint.zero, size: sz), cornerRadius: 8)
            p.stroke()
        }
        return image
    }
    
    //MARK:- Usage
    UIImageView(image: UIImage(named: "cat")).image 
    = makeRoundedRectangle(CGSize(width: 45, height: 45))
  • 함수를 반환하는 함수를 클로저를 이용해 compact 하게 만들기
    //출처 : iOS14 Programming Fundamentals with Swift Chapter2
    //MARK:- Functions returing funtion
    
    func makeRoudnedRectangleMaker(_ sz: CGSize) -> () -> UIImage {
        func maker() -> UIImage {
            let image = imageOfSize(sz) {
                let path = UIBezierPath(roundedRect: CGRect(origin: CGPoint.zero, size: sz), cornerRadius: 8)
                path.stroke()
            }
            return image
        }
        return maker
    }
    
    let maker = makeRoudnedRectangleMaker(CGSize(width:45, height: 20))
    
    UIImageView(image: UIImage(named: "cat")).image = maker()
    
    //“makeRoundedRectangleMaker is a factory for creating a whole family of functions similar to maker, each of which produces an image of one particular size. That’s a dramatic illustration of the power of closures.”
    
    //MARK: 클로저 캡쳐 이용해 리팩토링
    funcmakeRoundedRectangleMakerTwo(_ sz: CGSize) -> () -> UIImage {
        func any() -> UIImage {
            return imageOfSize(sz) {
                let p = UIBezierPath(roundedRect: CGRect(origin: CGPoint.zero, size: sz), cornerRadius: 8)
                p.stroke()
            }
        }
        return any
    }
    
    funcmakeRoundedRectangleMakerThree(_ sz: CGSize) -> () -> UIImage {
        func any() -> UIImage {
            return {
                return imageOfSize(sz) {
                    let p = UIBezierPath(roundedRect: CGRect(origin: CGPoint.zero, size: sz), cornerRadius: 8)
                    p.stroke()
                }
            }
        }
    }

Closure Setting a Captured Varable and Environment

클로저는 주변 환경을 캡쳐할 수 있다.

  • 코드
    //출처 : iOS14 Programming Fundamentals with Swift Chapter2
    func countAdder(_ f: @escaping () -> ()) -> () -> () {
        var ct = 0
        return {
            ct = ct + 1
            print("count is \(ct)")
            f()
        }
    }
    
    func greet() {
        print("하이")
    }
    
    let countedGreet = countAdder(greet)
    countedGreet()
    countedGreet()

Escaping Closures

  1. 함수의 종료 이후 클로저가 호출 될 때 해당 키워드 추가
  2. 클로저 내부에 self를 캡쳐하는 경우 명시적으로 선언하도록 한다. 왜냐하면 컴파일러가 self 캡쳐 여부를 원하기 때문

Secondary feature of escaping closures is that, when you refer to a property or method of self within the function body, the compiler may insist that you say self explicitly. That’s because such a reference captures self, and the compiler wants you to acknowledge this fact by saying self:

Curried Functions

  • 함수가 함수를 리턴할 때 그걸 curried function이라고 한다.
//출처 : iOS14 Programming Fundamentals with Swift Chapter2
func makeRoundedRectangleMakerThree(_ sz: CGSize) -> ((CGFloat) -> UIImage) {
    return { r in
        imageOfSize(sz) {
            let p = UIBezierPath(roundedRect: CGRect(origin: CGPoint.zero, size: sz), cornerRadius: r)
            p.stroke()
        }
    }
}

//일반적으로 쓸 때
let maker2 = makeRoundedRectangleMakerThree(CGSize(width:45, height:20))
maker2(8)

//curreid Function
makeRoundedRectangleMakerThree(CGSize(width:45, height:20))(8)

Functions References and Seletors

함수를 호출하지 않고 reference만 가지고 오는게 가능하다

func test() {
    print("test")
}

let a = test // () -> () 타입의 객체가 됨 

method overloaded일 땐 signature 를 명시해라

//출처 : iOS14 Programming Fundamentals with Swift Chapter2
class Dog {
    func bark() { }
    func bark(_ loudly: Bool) { }
    func bark(_ times: Int) { }
    func test() {
        let barkFunction = bark(_:) **as (Int) -> ()**
    }
}

Function Reference Scope

  1. 함수의 Reference만 가지고 오고 싶을 땐 인스턴스로 안가져와도 됨.

    //출처 : iOS14 Programming Fundamentals with Swift Chapter2
    class Dog {
        func bark() { }
    }
    
    class Cat {
        func purr() {}
        
        func test() {
           let c = Dog.bark
        }
    }
  2. Curried function

    • 인스턴스 메서드의 curried static/class version 를 명확히 해야한다. “If you use the type with dot-notation and you need to disambiguate the function reference by giving its signature, the signature must describe the curried static/class version of the instance method (see “The Secret Life of Instance Methods”):”
    //2.
    class Cat {
        func purr() {
        }
        func purr(_ loudly:Bool) {
        }
    }
    class Dog {
        func test() {
            let purrFunction = Cat.purr as (Cat) -> () -> Void
        }
    }
    

Selectors

  • obj-c에서는 selector가 메소드의 reference 처럼 사용됨
    • addTarget 같은 메소드 보면 selector 추가하는게 있는 걸 볼 수 있음
  • selctor 나 action 등으로 불림
  • 왜 이게 필요했냐면 run tiem에 메소드의 target을 제공하기 위해서였는데 이 targer 을 문자, 즉 string으로 주어야하기 때문에 많이 위험했음(오타 하나만 나도 실행안됨, 메소드 레퍼런스하는 방법 모르는 경우도 생길 수 있음) 즉. "unrecognized crash" 발생 위험 높음

⇒ 그래서 제공하는게 #selector 키워드

  • #selector 사용하면 어떻게 달라지는지에 대한 예시
    //출처 : iOS14 Programming Fundamentals with Swift Chapter2
    class ViewController : UIViewController {
        @IBOutlet var button : UIButton!
        func viewDidLoad() {
            super.viewDidLoad()
            self.button.addTarget(
                // #selector 문법 사용
                self, action: #selector(buttonPressed), for: .touchUpInside)
            // #selector 문법 사용하지 않는 경우
            self.button.addTarget(self, action: buttonPressed:, for: .touchUpInside)
        }
        @objc func buttonPressed(_ sender: Any) {
            // ...
        }
    }

♦️addTarget( 해당객체가선언된객체, action: 무엇을실행할것인가 , for: 어떤이벤트가발생했을때인가 )

함수를 매게변수로 받을 수 있다. 더 나아가 함수를 만드는 함수를 만들 수 있다. 클로저 잘 이용해 보자

  • 함수를 반환하는 함수를 클로저를 이용해 compact 하게 만들기
    //출처 : iOS14 Programming Fundamentals with Swift Chapter2
    //MARK:- Functions returing funtion
    
    func makeRoudnedRectangleMaker(_ sz: CGSize) -> () -> UIImage {
        func maker() -> UIImage {
            let image = imageOfSize(sz) {
                let path = UIBezierPath(roundedRect: CGRect(origin: CGPoint.zero, size: sz), cornerRadius: 8)
                path.stroke()
            }
            return image
        }
        return maker
    }
    
    let maker = makeRoudnedRectangleMaker(CGSize(width:45, height: 20))
    
    UIImageView(image: UIImage(named: "cat")).image = maker()
    
    //“makeRoundedRectangleMaker is a factory for creating a whole family of functions similar to maker, each of which produces an image of one particular size. That’s a dramatic illustration of the power of closures.”
    
    //MARK: 클로저 캡쳐 이용해 리팩토링
    funcmakeRoundedRectangleMakerTwo(_ sz: CGSize) -> () -> UIImage {
        func any() -> UIImage {
            return imageOfSize(sz) {
                let p = UIBezierPath(roundedRect: CGRect(origin: CGPoint.zero, size: sz), cornerRadius: 8)
                p.stroke()
            }
        }
        return any
    }
    
    funcmakeRoundedRectangleMakerThree(_ sz: CGSize) -> () -> UIImage {
        func any() -> UIImage {
            return {
                return imageOfSize(sz) {
                    let p = UIBezierPath(roundedRect: CGRect(origin: CGPoint.zero, size: sz), cornerRadius: 8)
                    p.stroke()
                }
            }
        }
    }
profile
to be iOS Developer

0개의 댓글