휴,,, 다행이 오늘도 게을러지지 않게 TIL을 작성하려 합니다.. 아침에 무심코 늦잠을 잘 뻔 했지만 이겨냈습니다 ^_^ㅋㅋ
아침에 운동을 다녀온 후 집근처 카페에 들려 공부를 하니 집에서 에어컨도 안틀고 절약이 되는거 있죠..
절약은 절약이지만 와서 카야토스트와 말차라떼를 시킨 저는 다이어트는 안된다는점~~ 
이번에 산 책인 "객체지향의 사실과 오해"를 가지고 와서 읽고 싶었지만 아직 배송중.. ㅜㅜ
따라서 어제 하지못한 클로저의 뒷부분을 공부했습니다~~ 

(어제쓴 TIL한번 정독후~)

 

 

 

배운점

연산자 메서드

클래스와 구조체는 기존 연산자의 자체 구현을 제공할수 있습니다. -> 연산자 오버로딩
(오버라이딩(Overriding) : 서브 클래스는 슈퍼 클래스에서 상속할 메서드, 프로퍼티, 서브스크립트를 서브클래스에서 원하는대로 재정의 할 수 있다.

오버로딩(Overloading) : 함수 이름은 같으나 매개변수, 리턴타입 등을 다르게 하여 함수를 중복으로 선언할 수 있다.)
잠깐 오버라이딩과 오버로딩을 상기시키고~

struct Vector2D {
    var x = 0.0, y = 0.0
}

extension Vector2D {
    static func + (left: Vector2D, right: Vector2D) -> Vector2D {
        return Vector2D(x: left.x + right.x, y: left.y + right.y)
    }
}

위의 예제는 2차원 벡터에 대한 구조체를 정의 한 후 벡터 구조체의 인스턴스를 함께 더하기 위해 연산자 메서드의 정의를 합니다.

 

+와 일치하는 메서드의 이름을 가진 타입 메서드로 정의됩니다.

덧셈은 벡터의 필수 동작의 일부가 아니므로 메인 구조체에서의 선언이 아닌 확장에서 정의된다고 합니다!!

let vector = Vector2D(x: 3.0, y: 1.0)
let anotherVector = Vector2D(x: 2.0, y: 4.0)
let combinedVector = vector + anotherVector
// combinedVector는 값이(5.0, 5.0)인 Vector2D 인스턴스 입니다.

 

접두사와 접미사 연산자

위에서의 예제는 이진 중위 연산자의 사용자 정의 구현입니다. 클래스와 구조체는 표준 단항연산자의 구현도 제공합니다!

-a 와 같이 대상의 앞에오면 접두사(prefix)연산자, a!와 같이 뒤에오면 접미사(postfix)연산자 입니다.

 

함수의 마지막 인수로 함수에 클로저 표현식을 전달해야 하고 클로저 표현식이 긴 경우 후행 클로저로 작성하는것이 유용!

extension Vector2D {
    static prefix func - (vector: Vector2D) -> Vector2D {
        return Vector2D(x: -vector.x, y: -vector.y)
    }
}

let positive = Vector2D(x: 3.0, y: 4.0)
let negative = -positive
// negative는 (-3.0, -4.0)의 값을 가진 Vector2D의 인스턴스 입니다.
let alsoPositive = -negative
// alsoPositive (3.0, 4.0)의 값을 가진 Vector2D의 인스턴스 입니다.

단항 마이너스 연산자 -a 를 구현한 예제입니다. 결과값은 -를 +로 +를 -로 바꾸어서 반환합니다.

 

복합 할당 연산자

 

복합 할당 연산자는 다른 연산과 할당(=)을 결합합니다. Ex) (+=)

extension Vector2D {
    static func += (left: inout Vector2D, right: Vector2D) {
        left = left + right
    }
}

var original = Vector2D(x: 1.0, y: 2.0)
let vectorToAdd = Vector2D(x: 3.0, y: 4.0)
original += vectorToAdd
// original은 현재 (4.0, 6.0)의 값을 가지고 있습니다.

※ 기본 할당 연산자 (=)는 오버로드가 불가능 하다고 합니다! 복합 할당 연산자만 오버로드 될 수 있습니다. 삼항 조건 연산자(a ? b : c)또한 오버로드 할 수 없습니다.

 

등가 연산자

 

사용자 정의 클래스와 구조체는 (==)와 (!=)라고 하는 등가 연산자의 구현을 가지지 않습니다. 일반적으로 표준 라이브러리의 기본 구현을 사용합니다. (==), (!=) 연사자를 구현하는 경우 표준 라이브러리의 Equatable 프로토콜의 준수성을 추가합니다.

extension Vector2D: Equatable {
    static func == (left: Vector2D, right: Vector2D) -> Bool {
        return (left.x == right.x) && (left.y == right.y)
    }
}

let twoThree = Vector2D(x: 2.0, y: 3.0)
let anotherTwoThree = Vector2D(x: 2.0, y: 3.0)
if twoThree == anotherTwoThree {
    print("These two vectors are equivalent.")
}
// Prints "These two vectors are equivalent."

많은 간단한 경우에 합성 구현을 사용하여 프로토콜 채택 (Adopting a Protocol Using a Synthesized Implementation) 에서 설명 된대로 Swift에 등가 연산자의 합성 구현을 제공하도록 요청할 수 있습니다.


위의 글을 읽고 합성된 구현을 사용하여 프로토콜 채택  으로 가서 문서를 확인했습니다.

 

합성된 구현을 사용하여 프로토콜 채택 

 

struct Vector3D: Equatable {
    var x = 0.0, y = 0.0, z = 0.0
}

let twoThreeFour = Vector3D(x: 2.0, y: 3.0, z: 4.0)
let anotherTwoThreeFour = Vector3D(x: 2.0, y: 3.0, z: 4.0)
if twoThreeFour == anotherTwoThreeFour {
    print("These two vectors are also equivalent.")
}
// Prints "These two vectors are also equivalent."

위의 예제를 보았을때 x, y, z 모두 Equatable 타입이므로 Vector3D는 등가 연산자의 합성된 구현을 받습니다. Vector3D는 등가 연산자의 기본 구현을 사용합니다.

등가 연산자의 합성된 구현...

  • Equatable 프로토콜을 준수하는 속성만 저장된 구조
  • Equatable 프로토콜을 준수하는 연관된 타입만 포함하는 열거형
  • 연관된 타입들이 없는 열거형

생각과 감정

문서를 볼때마다 새롭네요... 아직 많이 부족한것 같슴다..

클로저또한 완벽히 이해한것은 아니니 계속 보아야 할거같아요,,ㅎㅎ

 

 

 

 

앞으로의 계획선언

합성된 구현에 대한 이해가 아직 부족한것 같으니 관련 문서와 블로그를 보고 공부를 해야겠씀미다...

집에가면 책이 도착했겠죠..? ㅎㅎ 조금은 읽어보고 자야겠네요~~

 

 

 

 

 

 

https://kka7.tistory.com/132?category=919617 

 

Advanced Operators

[최종 수정일 : 2018.09.13]원문 : 애플 개발 문서 Swift 4.2 Language Guide - Advanced Operators고급 연산자(Advanced Operators)기본 연산자(Basic Operators)에서 설명된 연산자 외에, Swift는 더 복잡한 값을 다루기 위한

kka7.tistory.com

 

'TIL' 카테고리의 다른 글

TIL_20230921  (0) 2023.09.21
TIL_20230918  (0) 2023.09.18
TIL_20230914  (0) 2023.09.14
TIL_20230913  (0) 2023.09.13
TIL_20230911  (0) 2023.09.11

안녕하세요~ 오늘부터 배운것을 조금씩이나마 깔짝 해보겠습니다..ㅎㅎ
하루하루 계획없이 시간만 보내던중 되돌아보니 시간이 엄청 흘러있던거 있죠,,
되돌아보니 이런 아이가 커서 뭐가될지 참...

백수가 운동을 하면 하루를 알차게 보냈다는 착각을 하게돼서 하루의 의미있는 시간은 운동밖에 없다죠..
지금 제가 그렇습니다.. 운동을 하고와서 얼마나 의미없게 보냈는지.. 
여러분도 착각에 빠지지 마시길....
(참 저 백수아님.. 대학 막학기지만 온라인 강의로인해 집에만 있습니다 ^^)

 

배운점

오늘은 Swift Document를 읽었습니다. 원문은 단어를 계속찾아야 하고... 번역기의 노예가 된 느낌이니 한글 번역 문서를 읽었습니다 ㅎ

https://bbiguduk.gitbook.io/swift/language-guide-1/closures

 

클로저 (Closures) - Swift

다음은 Int 값의 배열을 String 값의 배열로 변환하기 위해 후행 클로저와 map(_:) 메서드를 어떻게 사용하는지 나타냅니다. 배열 [16, 58, 510] 은 새로운 배열 ["OneSix", "FiveEight", "FiveOneZero"] 을 생성하는

bbiguduk.gitbook.io

 

바로 클로저에 대해 알아봤습니다!!
문서에서의 클로저 정의는 명명된 함수 생성없이 실행되는 코드 그룹입니다. 라고 하더군요
이는 함수 또한 클로저를 의미합니다. 

클로저는 코드에서 주변에 전달과 사용할 수 있는 자체 포함된 기능 블럭입니다. 한마디로 코드의 블럭이군요!

이렇게 클로저를 알기 전에 알고있어야하는 내용이 있었어요. 바로 일급 객체(일급 시민)입니다.
일급 객체란 다른 객체들에 일반적으로 적용 가능한 연산을 모두 지원하는 객체를 가르킵니다.

일급 객체는 아래의 조건을 만족 시켜야 합니다.

  • 함수의 실질적인 매개변수가 될 수 있다.
  • 함수의 반환값이 될 수 있다.
  • 할당의 대상이 될 수 있다.
  • 비교연산을 적용할 수 있다. (Swift는 권장하지 않습니다.)

 

클로저 표현식

중첩 함수 (Nested Functions) 에서 소개된 중첩 함수는 더 큰 함수에 부분으로 자체 포함된 코드 블럭의 이름을 지정하고 정의하기 편리한 수단입니다. 그러나 완전한 선언과 이름없이 함수와 유사한 구조의 짧은 버전을 작성하는 것이 때때로 유용합니다. 함수를 하나 이상의 인수로 사용하는 함수 또는 메서드로 작업할 때 특히 그렇습니다.

위의 글을 읽다 중첩 함수 부분을 다시 읽었습니다,,,

func chooseStepFunction(backward: Bool) -> (Int) -> Int {
    func stepForward(input: Int) -> Int { return input + 1 }
    func stepBackward(input: Int) -> Int { return input - 1 }
    return backward ? stepBackward : stepForward
}
var currentValue = -4
let moveNearerToZero = chooseStepFunction(backward: currentValue > 0)
// moveNearerToZero now refers to the nested stepForward() function
while currentValue != 0 {
    print("\(currentValue)... ")
    currentValue = moveNearerToZero(currentValue)
}
print("zero!")
// -4...
// -3...
// -2...
// -1...
// zero!

chooseStepFunction의 함수내부의 stopForward, stopForward의 중첩함수를 정의하여 호출을 하고있습니다!
Bool값으로 앞으로갈지 뒤로갈지 리턴하고 있습니다.

중첩함수도 편리한 수단이지만, 완전한 선언과 이름없이 함수와 유사한 구조의 짧은 버전을 작성하는 것이 때때로 유용합니다!! 

클로저 표현식은 명확성이나 의도를 잃지 않고, 짧은 형태로 클로저를 작성하기위해 구문최적화를 제공한다고 합니다.
여기서 sorted(by: )라는 예제를 보여주고 있습니다.

sorted(by: )는 첫번째 값이 두번째 값의 앞또는 뒤에 표시되어야 하는지 여부를 Bool값으로 반환합니다.
첫번째값이 더 큰경우 True, 두번째값이 더 큰경우 False

let names = ["Chris", "Alex", "Ewa", "Barry", "Daniella"]
func backward(_ s1: String, _ s2: String) -> Bool {
    return s1 > s2
}
var reversedNames = names.sorted(by: backward)
// reversedNames is equal to ["Ewa", "Daniella", "Chris", "Barry", "Alex"]

backward를 통해 첫번째값이 두번째값보다 커야한다고 알려주고있군요.


클로저 표현구

{ (<#parameters#>) -> <#return type#> in
   <#statements#>
}

클로저 표현구의 파라미터는 in-out 파라미터 일 수 있지만 기본값을 가질수 없다 합니다!

//backward(_:_:)의 클로저 표현
reversedNames = names.sorted(by: { (s1: String, s2: String) -> Bool in
    return s1 > s2
})

클로저로 표현한다면 더욱 짧게 표현할 수 있습니다!

 

컨텍스트 타입 추론

sorted(by:)의 메서드는 (String, String) -> Bool 타입의 함수여야 합니다. 따라서 (String, String) 과 Bool 타입은 타입추론으로 인해 작성할 필요가 없습니다! 따라서 ->와 파라미터 이름을 둘러싼 소괄호를 생략할 수 있습니다.

reversedNames = names.sorted(by: { s1, s2 in return s1 > s2 } )


단일 표현 클로저의 암시적 변환

단일 표현 클로저는 return키워드를 생략 할 수 있습니다. (마지막 줄을 return으로 인지)

reversedNames = names.sorted(by: { s1, s2 in s1 > s2 } )

 

짧은 인수 이름

swift는 클로저에 $0, $1, $2... 등 클로저의 인수값으로 참조할 수 있습니다!
클로저의 표현식이 본문으로 전체가 구성되기 때문에 in 키워드를 생략 할 수 있습니다!

reversedNames = names.sorted(by: { $0 > $1 } )

$0과 $1은 클로저의 첫번째와 두번째 String인수를 참조합니다.


연산자 메서드

Swift의 String타입은 (>)의 문자열 별 구현을 String타입의 파라미터 2개가 있는 메서드로 정의하고, Bool을 반환합니다.
(흠 더욱 짧게 표현 할 수 있겠군요.)

reversedNames = names.sorted(by: >)

 

오늘은 여기까지...

 

 

생각, 감정

swift문법을 찍먹후 다시공부하는 저에겐 클로저는 새롭더라구요. $0 $1 , 코드줄이기 대충생각하고 아~ 그런가보다~ 하고 넘어갔지만 깊게 파고들수록 열심히 해야겠다는 생각이 드네요.. 

 

앞으로의 계획

클로저의 연산자 메서드에서 새로운 파도타기를 추천해줘 연산자 메서드 (Operator Method)도 공부해봐야 겠군요! 그리고 남은 뒷부분(후행클로저)을 열심히....하도록~~~~~~~~ 하자!
(내일 알라딘에서 산 책이 오니 조금씩 읽어보자 ㅎㅎ 아 애플아카데미 붙고 싶은것.....ㅜㅜ)

다시는 게을러 지지 않는 내가 되도록 하자,, 화이팅~~






 

1급 객체(first-class object)란?

일급 객체라는 말이 요새는 많이 보편화가 된 것 같습니다. 제가 이 말을 처음 들었던 건 자바스크립트에서였는데, 단순히 자바스크립트에 국한된 개념은 아니더군요. 프로그래밍 언어론 수업

jcsoohwancho.github.io

 

 

[Swift] 일급 객체(First-class citizen)

안녕하세요~ 차니에요! 오늘은 1급 객체에 대해 알아볼게요. Swift의 함수가 일급 객체이므로 Swift로 진행하겠습니다! 1. 일급 객체란? 컴퓨터 프로그래밍 언어 디자인에서, 일급 객체(영어: first-cla

dvlpr-chan.tistory.com

 

 

 

 

Swift의 Closure | Hohyeon Moon

Swift의 핵심 개념인 Closure에 대해 알아봅니다

www.hohyeonmoon.com

 

 

일급 객체(First Class Object)란?

자바스크립트를 공부하다보면 라는 말을 한 번쯤은 들어보았을 것이다. 이번에는 그 가 무엇인지에 대해서 정리해보고자 한다. 일급 객체(1급객체, First Class Object) 일급객체에 대한 정의이다. >

velog.io

 

'TIL' 카테고리의 다른 글

TIL_20230921  (0) 2023.09.21
TIL_20230918  (0) 2023.09.18
TIL_20230914  (0) 2023.09.14
TIL_20230913  (0) 2023.09.13
TIL_20230912  (0) 2023.09.12

 

문제 설명

수웅이는 매달 주어진 음식을 빨리 먹는 푸드 파이트 대회를 개최합니다. 대회에서 선수들은 1 1 대결하며, 대결마다 음식의 종류와 양이 바뀝니다. 대결은 준비된 음식들을 일렬로 배치한 , 선수는 제일 왼쪽에 있는 음식부터 오른쪽으로, 다른 선수는 제일 오른쪽에 있는 음식부터 왼쪽으로 순서대로 먹는 방식으로 진행됩니다. 중앙에는 물을 배치하고, 물을 먼저 먹는 선수가 승리하게 됩니다.

이때, 대회의 공정성을 위해 선수가 먹는 음식의 종류와 양이 같아야 하며, 음식을 먹는 순서도 같아야 합니다. 또한, 이번 대회부터는 칼로리가 낮은 음식을 먼저 먹을 있게 배치하여 선수들이 음식을 먹을 있게 하려고 합니다. 이번 대회를 위해 수웅이는 음식을 주문했는데, 대회의 조건을 고려하지 않고 음식을 주문하여 개의 음식은 대회에 사용하지 못하게 되었습니다.

예를 들어, 3가지의 음식이 준비되어 있으며, 칼로리가 적은 순서대로 1 음식을 3, 2 음식을 4, 3 음식을 6 준비했으며, 물을 편의상 0 음식이라고 칭한다면, 선수는 1 음식 1, 2 음식 2, 3 음식 3개씩을 먹게 되므로 음식의 배치는 "1223330333221" 됩니다. 따라서 1 음식 1개는 대회에 사용하지 못합니다.

수웅이가 준비한 음식의 양을 칼로리가 적은 순서대로 나타내는 정수 배열 food 주어졌을 , 대회를 위한 음식의 배치를 나타내는 문자열을 return 하는 solution 함수를 완성해주세요.

 

제한사항

  • 2 ≤ food 길이 ≤ 9
  • 1 ≤ food 원소 ≤ 1,000
  • food에는 칼로리가 적은 순서대로 음식의 양이 담겨 있습니다.
  • food[i] i 음식의 수입니다.
  • food[0] 수웅이가 준비한 물의 양이며, 항상 1입니다.
  • 정답의 길이가 3 이상인 경우만 입력으로 주어집니다.

 

입출력

food result
[1, 3, 4, 6] "1223330333221"
[1, 7, 1, 2] "111303111"

 

입출력 설명

입출력 #1

  • 문제 예시와 같습니다.

입출력 #2

  • 선수는 1 음식 3, 3 음식 1개를 먹게 되므로 음식의 배치는 "111303111"입니다.

 

import Foundation

func solution(_ food:[Int]) -> String {

    //정답 배열
    var res = ""
    
    //오른쪽 배열
    var half = ""
    
    //food접근
    for i in food.indices {
        //갯수만큼 문자열 생성,추가
        res += String(repeating: String(i), count: food[i] / 2)
    }
    //왼쪽 배열 뒤집어서 오른쪽배열만들기
    half = String(res.reversed())
    //물인 "0"추가
    res.append("0")
    
    //정답 배열에 오른쪽 배열 추가
    res.append(half)
    
    return res
}

 

https://github.com/kodecocodes/swift-algorithm-club

 

GitHub - kodecocodes/swift-algorithm-club: Algorithms and data structures in Swift, with explanations!

Algorithms and data structures in Swift, with explanations! - GitHub - kodecocodes/swift-algorithm-club: Algorithms and data structures in Swift, with explanations!

github.com

 

정의 

구조체와 클래스의 정의 구문은 비슷합니다. struct키워드로 구조를 소개하고 키워드로 클래스를 소개합니다 class. 둘 다 한 쌍의 중괄호 안에 전체 정의를 배치합니다.

struct SomeStructure {
    // structure definition goes here
}
class SomeClass {
    // class definition goes here
}

 

EX)

struct Resolution {
    var width = 0
    var height = 0
}
class VideoMode {
    var resolution = Resolution()
    var interlaced = false
    var frameRate = 0.0
    var name: String?
}

 

인스턴스를 생성하는 구문은 구조체와 클래스 모두 매우 유사합니다.

let someResolution = Resolution()
let someVideoMode = VideoMode()

 

 

모든 구조체에는 자동으로 생성된 멤버 단위 초기화가 있으며 이를 사용하여 새 구조체 인스턴스의 멤버 속성을 초기화할 수 있습니다. 새 인스턴스의 속성에 대한 초기 값은 이름으로 멤버별 이니셜라이저에 전달될 수 있습니다.

let vga = Resolution(width: 640, height: 480)

구조체와 달리 클래스 인스턴스는 기본 멤버 단위 초기화를 받지 않습니다. (초기화 initialization)

 

 

클래스는 참조 유형입니다.

값 유형과 달리 참조 유형은 변수나 상수에 할당되거나 함수에 전달될 때 복사되지 않습니다 . 복사본이 아니라 동일한 기존 인스턴스에 대한 참조가 사용됩니다.

let tenEighty = VideoMode()
tenEighty.resolution = hd
tenEighty.interlaced = true
tenEighty.name = "1080i"
tenEighty.frameRate = 25.0

frameRate를 30으로 바꿈

let alsoTenEighty = tenEighty
alsoTenEighty.frameRate = 30.0

 

 

 

 

 

 

 

 

import UIKit

struct Town{
    //프로퍼티
    let name = "Angelaland"
    var citizens = ["Angela", "Jack Bauer"]
    var resources = ["Gain" : 100, "Ore" :42, "Wool" : 75]
    
    //구조체 안의 리턴값 없는 메서드(구조체나 클래스 안에서의 정의)
    //독립실행형이고 어디선가 자유롭게 사용된다면 함수라고 정의 따라서 fortify는 메서드
    func fortify(){
        print ("Defences increased!")
    }
}


var myTown = Town() //Town개체를 myTown으로 사용

print(myTown.name) // 구조체의 name 출력
print("\(myTown.name) has \(myTown.resources["Gain"]!) bags of grain.")

myTown.citizens.append("Smith") //moTown의 citizens에 Smith추가
print(myTown.citizens.count) //citizens의 개수 출력

myTown.fortify()



//init사용
struct Town_initCase{
    //프로퍼티
    let name : String
    var citizens : [String]
    var resources : [String : Int]
    
    init(name : String, citizens : [String], resources : [String:Int]){
        self.name = name
        self.citizens = citizens
        self.resources = resources
    }
    
    //구조체 안의 리턴값 없는 메서드(구조체나 클래스 안에서의 정의)
    //독립실행형이고 어디선가 자유롭게 사용된다면 함수라고 정의 따라서 fortify는 메서드
    func fortify(){
        print ("Defences increased!")
    }
}

//init으로 Town_initCase 구조체 초기화
var anotherTown = Town_initCase(name: "Nameless Island", citizens: ["Tom"], resources: ["Coconuts" : 100])
anotherTown.citizens.append("Wilson")
print(anotherTown.citizens)

 

 

 

https://docs.swift.org/swift-book/documentation/the-swift-programming-language/classesandstructures#Definition-Syntax

 

Documentation

 

docs.swift.org

 

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

Swift 알고리즘 클럽  (0) 2023.06.30
Swift 기본문법) 특수문자 출력  (0) 2023.06.17
Swift 기본문법) 컬렉션 타입  (0) 2022.09.05
Swift 기본문법) 기본 데이터 타입  (1) 2022.08.31
Swift 기본문법) 상수와 변수  (1) 2022.08.30

 

특수한 문자열을 출력하려면 양끝에 #을 붙여주면된다.

import Foundation

print(#"!@#$%^&*(\'"<>?:;"#)
//!@#$%^&*(\'"<>?:; 그대로 출력됨.

 

 

 

 

어찌저찌 우리의 졸업작품 프로젝트가 끝났다... 

2022.07 ~ 2023.05까지 약 1년간 진행했다...

여름 방학때부터 매주 모여 아이디어를 회의하고 사실 지금의 아이디어는 찐찐찐찐막 의 아이디어를 선정 하였다 ㅋㅌㅋ

그렇게 아이디어중 하나를 선택하고 서로의 공부 방향을 정했다.

처음에 라즈베리파이로 서버를 열려 하였지만... ip가 유동ip여서 정말 불편했다는... 그리고 모델 연산을 하게되면 엄청난 부하로 인해 라즈베리파이 cpu로 고기도 구워먹을 수 있었다.

따라서 우리는 AWS를 사용하게 되고 뭐 이런저런 과정을 겪게 됐다 ㅋㅋ

아마 전공 4년동안 가장 인상적인 팀 프로젝트가 아닌가 싶다. 사실 이전의 것들은 하난도 인상적이지 않아 기억이 없다 ㅋㅌ

 

내가 앱 제작을 맡아 앱 개발 과정을 이 블로그에 올리려 하였지만 여러가지 문제로 인해 올리지 못했다. (주로 귀차니즘) 돌아보니 간단히 우리의 기능을 보여주려 하여다보니 앱 이라고 말하기 조금 꼬름해졌다 ㅋㅋ 

계획은 거창하였으나 구현을 못한것에 대해 아쉽다.. 로그인 뭐 회원관 등등의 기본적인 기능들도 넣었어야 하는 아쉬움.... 이 있다!!

그래도 이 이후에 계속 고쳐나갈 예정이니까 git주소 올릴태니 모두들 많관부 ㅎ 이쁘게 봐주세요 여루분  혹여나 간단한 프로젝트를 우리와 비슷하게 하려는 분들 참고하시구 인사팀 또는 관련 사장님들 우리 써주세요 ㅎㅎ 일 열심히해요 ㅎㅎ ㅋㅌㅋ 

 

 

 

 

 

 

 

 

(고생많았구 다들 음 팀장직은 이만 내려놓겠다. ㅋㅌㅋ )

 

 

https://github.com/NewP1/Golden_Time

 

GitHub - NewP1/Golden_Time

Contribute to NewP1/Golden_Time development by creating an account on GitHub.

github.com

 

 

CUDA와 CUDNN설치후 openCV를 수동으로 설치후 weights파일이 적용된 아니면 적용이 안된 python파일을 실행시켜도

nvidia-smi의 running processes에 추가가 되지 않는 모습이었다... CUDA/CUDNN/openCV의 버전 호환도 맞췄고, gpu에 따른 

CUDA_ARCH_BIN도 맞췄는데 왜안될까... 라고해서 CUDA/CUDNN/openCV 설치만 주구장창 10일 가량 했던거 같다..

물론 나의 잘못이지만 python에서 import cv2를 한 후 버전확인 한 버전과 pkg-config --modversion opencv4의 버전이 다르다는것은 내가 CUDA를 적용한 openCV를 파이썬이 적용을 하지 못한것... 따라서 openCV설치 전의 AMI이미지를 저장해뒀기에 처음부터 다시 시작하기로 하였다..(사실 심볼릭 링크가 깨져서 이것저것 지우고 다시깔다가 apt가 안돼서 다시함 ㅎㅎ 비밀~~)

(재설치 10회 이상하여 멘탈이 안좋아 사진은 없습니다....)

 

CUDA11.4,NDIVIA-DRIVER-470 설치

 

계속 nvidia-driver를 

sudo apt-get install nvidia-driver-470

이렇게 설치한 후 CUDA버전을 맞춰서 설치를 진행 하였지만.

nvidia-driver를 설치한후 CUDA를 설치하지않았는데 nvidia-smi로 확인을 해 보면 CUDA의 버전이 지멋대로 적용이 돼있었다..

 

CUDA를 설치할때 같이 설치되는 ndivia-driver로 설치 하였다.

https://developer.nvidia.com/cuda-11-4-0-download-archive?target_os=Linux&target_arch=x86_64&Distribution=Ubuntu&target_version=18.04&target_type=runfile_local 

이 링크에서 .run파일을 다운받고 설치를 진행해 준다.

 

중간에 체크박스 화면이 보이겠지만 example 등과같은건 설치하지말고 ndivia-driver와 그밑의 cuda tool만 설치를 진행하면 된다.

설치가 완료가 된다면

 

sudo vi ~/.bashrc

export PATH=$PATH:/usr/local/cuda-11.4/bin
export CUDADIR=/usr/local/cuda-11.4
export LD_LIBRARY_PATH=$LD_LIBRARY_PATH:/usr/local/cuda-11.4/lib64

:wq!로 저장을 한뒤

source ~/.bashrc 로 저장을 하고 재부팅을 하면된다.

 

이후

nvcc -V

nvidia-smi

로 cuda버전이 일치함을 볼 수 있을것이다.

 

 

CUDNN8.2.4설치

https://developer.nvidia.com/rdp/cudnn-download#a-collapse714-92

여기서 간단한 회원 가입후 자신이 원하는 버전의 cudnn을 다운받아 ubuntu환경으로 옮겨주면 된다.

(저는 노트북에 설치후 scp를 이용하여 옮겼습니다)

 

scp -i "키페어 위치" "옮길 파일의 위치" "ec2접속주소":/home/ubuntu

이후 deb는 dpkf or tgz는 tar -xvf 등으로 파일을 풀게 된다면 cuda의 폴더가 생성될것이다.

cd cuda/ 파일의 위치로 옮긴 후

sudo cp include/cudnn* /usr/local/cuda-11.4/include
sudo cp -P lib64/libcudnn* /usr/local/cuda-11.4/lib64/
sudo chmod a+r /usr/local/cuda-11.4/lib64/libcudnn*

//-P를 같이 해줘야 심볼릭 링크가 깨지지 않고 복사해줌.

하면 cudnn설치 완료

 

CUDNN버전 확인

cat /usr/local/cuda/include/cudnn_version.h | grep CUDNN_MAJOR -A 2

 

nvidia-smi를 확인하면 GPU가 OFF로 되어 있을 수 있다.

https://docs.aws.amazon.com/ko_kr/AWSEC2/latest/UserGuide/optimize_gpu.html

이 가이드를 참고하여 자신이 사용하는 인스턴스에 맞게 GPU를 활성화 해주면 된다.

 

여기까지 하면 openCV 설치를 위한 CUDA CUDNN설치가 완료됐다.~~ 칭찬해~~

 

 

 

openCV설치

 

혹시 설치된 openCV가 있는지 확인,삭제

pkg-config --modversion opencv4 //확인

//만약 openCV가 설치되어있다면


sudo apt-get purge libopencv* python-opencv4

//확인하기
sudo find /usr/local/ -name "*opencv*" -exec rm {} \;

//만약 남아 있다면 아래로 지우기
sudo rm -rf '경로'

//다시 확인하기
sudo find /usr/local/ -name "*opencv*" -exec rm {} \;

//다시 opencv 확인
pkg-config --modversion opencv4

 

git clone하여 opencv, opencv_contrib다운받기

git clone https://github.com/opencv/opencv.git
git clone https://github.com/opencv/opencv_contrib.git

 

CUDA_ARCH_BIN을 위해 자신의 gpu capability확인해서 넣어주세용

https://en.wikipedia.org/wiki/CUDA

cd ./opencv
mkdir build && cd build

//cmake해주기
cmake -D CMAKE_BUILD_TYPE=RELEASE \
-D CMAKE_INSTALL_PREFIX=/usr/local \
-D WITH_TBB=OFF \
-D WITH_IPP=OFF \
-D WITH_1394=OFF \
-D BUILD_WITH_DEBUG_INFO=OFF \
-D BUILD_DOCS=OFF \
-D BUILD_EXAMPLES=OFF \
-D BUILD_TESTS=OFF \
-D BUILD_PERF_TESTS=OFF \
-D WITH_CUDA=ON \
-D WITH_CUDNN=ON \
-D OPENCV_CUDA_ENABLED=ON \
-D CUDA_FAST_MATH=ON \
-D CUDA_ARCH_BIN=7.5\  #자신의 gpu에 맞는 capability확인하기
-D CUDA_ARCH_PTX=7.5 \
-D WITH_CUBLAS=ON \
-D WITH_CUFFT=ON \
-D WITH_QT=ON \
-D WITH_GTK=OFF \
-D WITH_OPENGL=ON \
-D WITH_V4L=ON \
-D WITH_FFMPEG=ON \
-D WITH_XINE=ON \
-D BUILD_NEW_PYTHON_SUPPORT=ON \
-D INSTALL_C_EXAMPLES=OFF \
-D INSTALL_PYTHON_EXAMPLES=ON \
-D OPENCV_GENERATE_PKGCONFIG=ON \
-D OPENCV_EXTRA_MODULES_PATH=/home/ubuntu/opencv_contrib/modules \
-D OPENCV_ENABLE_NONFREE=ON \
-D OPENCV_GENERATE_PKGCONFIG=ON \
-D OPENCV_PYTHON3_INSTALL_PATH=/usr/local/lib/python3.6/dist-packages \
-D PYTHON3_EXCUTABLE=/usr/bin/python3 \
-D PYTHON3_INCLUDE_DIR=/usr/include/python3.6 \
-D PYTHON3_NUMPY_INCLUDE_DIRS=**/usr/lib/python3/dist-packages/numpy/core/include** \
-D PYTHON3_PACKAGES_PATH=/usr/lib/python3/dist-packages \
-D PYTHON3_LIBRARY=/usr/lib/x86_64-linux-gnu/libpython3.6.so \

// 자신 시스템의 코어 수 확인
nproc 

// 빌드 (modify the core number '12' after option -j accordingly)
make -j12 # 자신 시스템 코어 수에 맞게 -j 다음의 숫자를 변경

// install
sudo make install

// 설치된 opencv확인 저 같은경우는 4.7.0
pkg-config --modversion opencv4

 

-D PYTHON3_EXCUTABLE=/usr/bin/python3 \
-D PYTHON3_INCLUDE_DIR=/usr/include/python3.6 \
-D PYTHON3_NUMPY_INCLUDE_DIRS=**/usr/lib/python3/dist-packages/numpy/core/include** \
-D PYTHON3_PACKAGES_PATH=/usr/lib/python3/dist-packages \
-D PYTHON3_LIBRARY=/usr/lib/x86_64-linux-gnu/libpython3.6.so \

이 경로를 잘 파악해서 넣어주는것이 중요합니다!!!

 

 

 

이후 python3 ~~.py를 실행하고 nvidia-smi를 확인하니 프로세스에 잘 넣어지고 gpu사용량도 쭉쭉 올라가는것이 보입니다..

여러분들도 혹시 저랑 같은 문제를 직면한다면 다지우고 처음부...ㅌ..읍읍.. ㅋㅋ 조금이나마 도움이 됐으면 좋겠습니다!!

 

 

 

 

 

 

 

많은도움주신 분

https://darkpgmr.tistory.com/184

 

OpenCV + CUDA 직접 빌드하기 (Windows/Linux 종합)

최근에 opencv에 있는 dnn을 한번 써보려고 직접 소스를 받아서 빌드(build)해 보았다. 역시나 엄청난 삽질의 연속이고 할 때마다 이것 저것 해결책을 검색하느라 많은 시간을 소모한다 (삽질은 누구

darkpgmr.tistory.com

라즈베리파이에서 Firebase Storage에 동영상을 올렸다는 가정하에 그 영상을 앱으로 가져와서

 

탭뷰로 나열한뒤, 실행까지 시키는 연습을 해볼것이다!!.

 

 

테이블뷰로 뷰를 꽉채운후 테이블뷰 cell과 cell안의 라벨을 추가해 준다.

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

셀의 Identifre를 cell로 설정을 한다.

 

 

 

 

 

 

그 다음은 파이어 베이스를 설정해보자~

 

파이어베이스 콘솔에서 프로젝트를 만들어주고

 

 

 

 

iOS로 시작한다~

 

 

 

 

 

 

 

 

 

 

 

 

 

번들ID를 입력을 해야하는데

이건 Xcode로 돌아가서

 

 

 

 

 

 

 

 

 

 

프로젝트 설정에서 빨간 박스를 복붙 하면 된다~

 

 

 

 

 

 

 

 

 

 

 

 

 

이 파일을  xcode에 옮길것인데

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

이렇게 끌어 오면 된다.

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

Firebase SDK를 설치합니다.

 

 

 

 

 

 

 

 

 

 

Xcode로 돌아와 프로젝트 설정에서 Package Dependencies에 들어가 +버튼을 눌러줍니다.

 

 

 

 

 

 

 

 

 

오른쪽 위의 검색창에 아까 복사한 Firebase SDK 저장소 URL을 붙여넣어 주시고.

Add Fackage를 한 후

조금 기다리면?

 

 

 

 

 

 

 

 

 

이 화면이 뜨게됩니다~

여기서

FirebaseDatabase

FirebaseDatabaseSwift

FirebaseFirestore

FirebaseFirestoreSwift

FirebaseMessaging

FirebaseStorage를 체크하고 AddPackage

 

 

 

 

import UIKit
import FirebaseCore

@main
class AppDelegate: UIResponder, UIApplicationDelegate {



    func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool {
        // Override point for customization after application launch.
        FirebaseApp.configure()
        return true
    }

이렇게 AppDelegate.swift 파일은 변경해 준다. 

(혹시 오류가 생기면 Firebase SDK 설치가 끝나면 오류가 사라질거에요~)

그후 파이어베이스의 RealTImeDatabase를 들어간다.

 

 

 

 

 

 

데이터 베이스를 만들고

 

 

 

 

 

 

 

 

 

 

 

 

경로를 이렇게 설정해 준다,

 

 

 

 

 

 

 

 

 

 

 

 

이후  FirebaseStorage에 짧은 영상을 2개 업로드 해 준다.(여러개도 상관없음)

 

 

 

 

 

 

 

 

이렇게 영상 2개를 업로드 한후

영상을 클릭하면

 

 

 

 

 

 

 

 

 

 

오른쪽에 이 화면이 나올것이다.

그리고 파일위치의 엑세스 토큰을 복사해 준다.

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

복사한 url을 Firebase RealTimeDatabase 경로에 추가해 준다.

나머지 1개도 해준다.

 

 

그럼 이렇게 경로 설정이 완료가 된다~

 

다시 Xcode로 돌아와서

 

command + N을 눌러 Swift파일을 추가해 준다. 이름은 Videos로

 

Videos.Swift

import Foundation

class Videos {
    
    var Title: String?
    var link: String?
    
    init(Title: String?, link: String?) {
        self.Title = Title;
        self.link = link;
    }
}

 

다음은 뷰컨트롤러의 아울렛 설정을 할 것이다.

command + N 을 눌러 CocoaTouch Class를 만들어 준다.

 

 

 

 

 

 

 

이후 메인의 테이블뷰 안에 있는 cell을 클릭 한 후 

 

만든 코코아터치 클래스로 설정을 해 준다

 

 

 

 

 

 

 

TableViewCell.swift

import UIKit

class TableViewCell: UITableViewCell {

    @IBOutlet weak var titleLabel : UILabel!
    
    override func awakeFromNib() {
        super.awakeFromNib()
        // Initialization code
    }

    override func setSelected(_ selected: Bool, animated: Bool) {
        super.setSelected(selected, animated: animated)

        // Configure the view for the selected state
    }

}

라벨의 아웃렛을 설정해준다.

 

그리고 뷰컨트롤러로 돌아와서

 

테이블뷰의 아웃렛을 설정한후~

 

ViewController.swift

import UIKit
import Foundation
import Firebase
import FirebaseDatabase
import AVKit


class ViewController: UIViewController, UITableViewDelegate, UITableViewDataSource{
   
    
    
    var table = [Videos]()
    var ref: DatabaseReference!

    @IBOutlet weak var TableView: UITableView!
    override func viewDidLoad() {
        super.viewDidLoad()
        
        ref = Database.database().reference().child("videos")
        
        ref.observe(DataEventType.value, with: { [self](snapshot) in
            if snapshot.childrenCount > 0 {
                self.table.removeAll()
                
                for video in snapshot.children.allObjects as! [DataSnapshot] {
                    
                    let Object = video.value as? [String : String]
                    let Title = Object?["Title"]
                    let videolink = Object?["link"]
                    
                    print(Title)
                    print(videolink)
                    
                    
                    let video = Videos(Title: Title as? String, link: videolink as? String)
                    self.table.append(video)
                    
                    TableView.delegate = self
                    TableView.dataSource = self
                    self.TableView.reloadData()
                    
                }
            }
            
        })
        
        
    }
    
    func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
        return table.count
    }
    
    func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
        let cell = TableView.dequeueReusableCell(withIdentifier: "cell") as! TableViewCell
        
        let video: Videos
        
        video = table[indexPath.row]
        cell.titleLabel.text = video.Title
        
        return cell
        
        
    }
    
    func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
        
        guard let videoURL = URL(string: table[indexPath.row].link!) else {
            return
        }
        
        let player = AVPlayer(url: videoURL)
        
        let controller = AVPlayerViewController()
        controller.player = player
        
        present(controller, animated: true) {
            player.play()
        }
        print("tabVIew clicked")
    }
    
    
    
    override func didReceiveMemoryWarning() {
        super.didReceiveMemoryWarning()
        // Dispose of any resources that can be recreated.
    }


}

 

시뮬레이터를 실행시키면

 

 

 

 

이렇게 셀들이 올라오고 터치하면 동영상이 플레이어로 나오게 됩니다!.

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

코드

https://github.com/jjwon2149/iOS---SwiftPractice/tree/main/AVkitWithTableViewTest

 

GitHub - jjwon2149/iOS---SwiftPractice

Contribute to jjwon2149/iOS---SwiftPractice development by creating an account on GitHub.

github.com

 

지난번에 만들어놓은 코드에 url을 라즈베리파이에서 서버로 띄운 웹의 주소를 넣었더니...

 

 

 

 

 

이게웬걸 ?박스가 뜨면서 카메라의 실시간 스트리밍 화면이 보이지 않았다..

 

지금 현제 뷰는 3개가 있는데

1. RecentVideoView

2.RealTimeVideoView

3.SettingView

 

2번의 뷰에서 하면 ?박스가 뜨고 1번의 뷰에 똑같이 넣으면 보이는? 현상이 발생했다,,,

이유는 모르겠다만 구글링을 해서 wkwebView 이전의 webView의 코드들을 가져다 써 보았고.

그 코드는 정상 작동하여 웹페이지에서 띄운 실시간 스트리밍 화면이 잘 보이게 됐다.

 

 

 

 

 

 

 

 

 

 

 

 

 

 

정상적으로 화면이 보이는 모습이다.

아직 포트포워딩을 하지 않아 같은 네트워크가 아니면 외부에서 스트리밍 사이트를 보지 못한다.

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

코드  (다시해보니 이 코드도 되고 전 글의 주소만 바꿔도 되네...)

import UIKit
import WebKit

class RealTImeVideoViewController: UIViewController, WKUIDelegate {

    @IBOutlet weak var webView: WKWebView!
    
    override func viewDidLoad() {
        super.viewDidLoad()

        //탭바 순서
        self.tabBarController?.selectedIndex = 1
        
        self.tabBarController?.tabBar.isHidden = false
        
//        loadWebPage("http://192.168.35.11:2204/")
        
        let stream_url = "http://192.168.35.11:2204/"
        webView.uiDelegate = self
        webView.load(NSURLRequest(url: NSURL(string: stream_url )! as URL) as URLRequest)
    }
}

 

다음은 웹뷰에 사용자 터치를 설정해 보겠다.

https://shylog.com/settings-for-a-more-complete-webview/

 

보다 완벽한 webview를 위한 세팅들 | 수줍은 동그래 블로그

해당 글은 2019년 3월에 작성된 글 입니다. 🙏 보다 완벽한 webview를 위한 세팅들 App에서 webview를 이용해서 앱을 웹으로 대체하는 경우가 꽤 있다. 퍼포먼스 면에서는 아직 웹뷰가 네이티브뷰를 따

shylog.com

 

 

혹시..그냥 주소만 바꿔도 나오시는분 있을까요..?ㅎㅎ

 

 

 

 

참고했습니다.

https://github.com/matzpersson/swift-raspberry-video-streaming

 

GitHub - matzpersson/swift-raspberry-video-streaming: Streaming video from Raspberry Pi Camera directly to web browser or to ios

Streaming video from Raspberry Pi Camera directly to web browser or to ios mobile device. - GitHub - matzpersson/swift-raspberry-video-streaming: Streaming video from Raspberry Pi Camera directly t...

github.com

 

 

 

 

 

 

 

 

 

https://github.com/jjwon2149/iOS---SwiftPractice/tree/main/GraduationApp

 

GitHub - jjwon2149/iOS---SwiftPractice

Contribute to jjwon2149/iOS---SwiftPractice development by creating an account on GitHub.

github.com

 

+ Recent posts