final 과 const 변수 이해하기

변수

void main(){
    int age;
    age = 20;
}

변수 age의 역할은 20이라는 숫자가 저장된 위치를 포인트 즉, 가리키고 있는 것

20이 저장된 위치의 주소를 저장하고 있는 것이 변수

제어자

final과 const 같은 keyword를 modifier 제어자 라고 함

제어자는 클래스, 변수, 함수를 정의할때 함께 쓰여서 이것들을 사용하기 위한 옵션을 정의해주는 역할

void main(){
    final int myFinal = 30;
    const int myConst = 70;
}

접근제어자

final,const : 변수 값이 한번 초기화되면 바꿀 수 없게 하는 것

void main(){
    final int myFinal = 30;
    const int myConst = 70;

    myFinal = 20; // 에러
    myConst = 50; // 에러
}

final 변수 초기화

  1. 선언할때 초기화
void main(){
    final int myFinal = 30;
}
  1. 객체 생성시에 외부데이터를 받아 생성자를 통해 초기화
class Person {
    final int age;
    String name;

    Person(this.age, this.name);
}

void main(){
    Person p1 = new Person(21, 'Tom'); // 생성자를 통해 할당 -> 이후로 age는 변경 불가
    print(p1.age); // 21
} 

2번이 가능한 이유

final은 초기화되는 시점이 앱이 실행되는 시점이기 때문 ⇒ run time constant

response 변수는 컴파일시에 초기화 되지 않고 앱이 실행된 후 웹 상에서 데이터가 전송될 때까지 기다렸다가 그 후에 값이 저장

Const

compile-time constant는 컴파일 시에 상수가 됨

const 변수는 선언과 동시에 초기화

void main(){
    const time = DateTime.now() // 에러
}

현재 시간은 매번 호출될 때마다 그 값이 변경되기 때문에 런타임시에 값이 지정되어야하므로 const 키워드는 오류 발생

정리

  1. const 변수는 컴파일 시에 상수화
  2. final 변수는 런타임 시에 상수화
  3. Compile-time constant = Run-time constant
    컴파일 시에 상수화는 런타임에도 상수화가 유지됨을 의미
  4. final 변수는 rebuild 될 수 있음

참고

https://www.youtube.com/watch?v=akc51-j84os&list=PLQt_pzi-LLfoOpp3b-pnnLXgYpiFEftLB&index=6

 

728x90

'Study > Dart' 카테고리의 다른 글

[Dart] factory 패턴  (0) 2021.06.24
[Dart] future, async, await, 실행순서  (0) 2021.06.15
[Dart] Future, Isolate  (0) 2021.06.08
[Dart] 상속을 쓰는 이유  (0) 2021.06.07
[Dart] 상속  (0) 2021.06.07

Rendering 조건

stateful widget에서 rebuild를 초래하는 것은 state 클래스이며 이를 통해 스크린을 랜더링함

  • child 위젯의 생성자를 통해서 새로운 데이터가 전달될때
  • internal state가 바뀔때

왜 StatefulWidget 클래스는 두개로 이루어져 있을까?

class MyApp extends StatefulWidget {
  const MyApp({Key? key}) : super(key: key);

  @override
  _MyAppState createState() => _MyAppState();
}

class _MyAppState extends State<MyApp> {
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'Charactor card',
      home: MyPage(),
    );
  }
}

먼저, StatefulWidget클래스는 아래와 같이 widget 클래스를 상속하고 있습니다.

abstract class StatefulWidget extends Widget {
  const StatefulWidget({ Key? key }) : super(key: key);
  @override
  StatefulElement createElement() => StatefulElement(this);
  @protected
  @factory
  State createState(); // ignore: no_logic_in_create_state, this is the original sin
}

widget클래스는 기본적으로 immutable 즉, 한번 생성되면 state가 변하지 않습니다.

그렇기에 StatefulWidget은 StatelessWiget처럼 immutable한 위젯입니다.

그러나 반드시 statefulwidget은 state의 변화를 반영해야 합니다.

이런 문제를 해결하기 위해 두개의 클래스로 나눠 StatefulWidget인 MyApp 위젯은 immutable한 특징을 유지하고 _MyAppState 클래스는 mutable한 속성을 대신하게 만든 것입니다.

어떻게 두개의 클래스를 연결시킬 수 있을까 - 1

먼저, state 클래스의 코드입니다.

@optionalTypeArgs
abstract class State<T extends StatefulWidget> with Diagnosticable {
  ...
}

State클래스는 제네릭 타입으로 어떤 타입이 오든지 StatefulWidget을 상속받게 하고 있습니다.

_MyAppState 클래스는 state클래스를 상속 받았고 이제, _MyAppState 클래스는 state 타입이 되었습니다.

class _MyAppState extends State<MyApp>{...}

그리고 상속 받은 state클래스의 제네릭 타입을 MyApp 클래스로 지정해준다면 이 state 클래스는 오직 MyApp타입만을 가질 수 있게 됩니다.

이를 통해 _MyAppState 클래스가 StatefulWidget인 MyApp 위젯에 연결된다고 flutter에 알려줄수 있습니다.

왜 state 클래스는 제네릭 타입을 가지게 되었을까

제네릭 타입의 장점인 코드의 재사용성, 안정성 때문입니다.

check box

Checkbox(value: false, onChanged: (value){})

check box source code

// StatefulWidget을 상속 받고 있음
class Checkbox extends StatefulWidget {
    const Checkbox({...})
    ...
}
// 상속받은 state의 제네릭 타입은 checkbox 타입으로 되어있음
class _CheckboxState extends State<Checkbox> with TickerProviderStateMixin, ToggleableStateMixin {...}

즉, state클래스를 제네릭 타입으로 만들어서 필요한 타입을 편하게 지정하고 그 외의 타입을 전달 받지 못하도록 안전장치를 해둔 것

어떻게 두개의 클래스를 연결시킬 수 있을까 - 2

이제, _MyAppState 클래스를 createState Method를 통해 MyApp 위젯 내에서 불러와야합니다.

class MyApp extends StatefulWidget {
  const MyApp({Key? key}) : super(key: key);

  @override
  State<StatefulWidget> createState() {
    return _MyAppState();
  }
}
class _MyAppState extends State<MyApp> {
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'Charactor card',
      home: MyPage(),
    );
  }
}

createState Method는 state 타입으로 지정되어 있고 제네릭 타입으로 StatefulWidget이 지정되어 있습니다.

즉, createState Method는 반드시 state 타입의 객체를 리턴해야 하는데 결국, StatefulWidget타입만이 올 수있는 객체여야 하며 이 객체는 _MyAppState 클래스에 근거하여 만들어진 것입니다.

StatefulWidget과 StatelessWidget 차이

createState Method는 StatefulWidget이 생성될 때 마다 호출되는 메서드입니다.

StatelessWidget은 build method에서 생성한 객체를 바로 반환하지만 StatefulWidget은 이 createState Method에서 생성한 객체를 반환합니다.

이제 어떻게 State를 변화시켜 랜더링 시킬까

state를 변화시킬려면 반드시 build 메서드를 호출해서 위젯을 rebuild하는 방법 뿐입니다.

단순히 버튼을 누른다고 build메서드를 호출할 수는 없습니다.

TextButton(
  onPressed: () {
      counter++;
  },
  child: Icon(Icons.add),
)

그래서, flutter는 우리 대신 build 메서드를 호출해 줄 수 있는 setState 메서드를 가지고 있습니다.

setState의 역할은 두가지 입니다.

  • 매개 변수로 전달된 함수 호출
  • build 메서드 호출
TextButton(
  onPressed: () {
    setState((){
      counter++;
    })
  },
  child: Icon(Icons.add),
)

정리

flutter는 widget tree를 기반으로 element tree를 생성합니다.

MyApp Stateful widget을 만나면 관련된 MyApp Stateful element를 추가하지만 createState 메서드를 호출해서 MyApp Stateful element와 연결된 MyAppState 객체도 생성합니다.

그런데 이 객체는 MyApp Stateful widget과 간접적으로만 연결되어 있습니다.

여기서 특이점은 MyApp Stateful element는 위젯 관련 중요 정보들을 가지고 있지만, 메모리 상에서 어디에도 종속되지 않는 독립된 객체로서 MyAppState 객체에 대한 정보도 가지게 되는 것입니다.

이제 setState메서드가 호출되고 build 메서드로 인해서 state객체가 rebuild 되면서 새로운 state를 반영한 새로운 MyApp Stateful widget이 rebuild 됩니다. 그러면 MyApp Stateful element에 연결되어 있는 MyAppState객체에 새로운 state가 저장이 되고 이제 MyAppState객체는 새롭게 rebuild된 MyAppStateful widget을 가리키게 됩니다.

왜 MyAppState 객체는 MyAppStateful widget처럼 widget tree상에서 매번 rebuild되지 않는 것일까?

비용문제

state가 변한 state 객체를 비용이 싼 Stateful widget으로 만들어서 계속 rebuild하고 MyAppState 객체는 element tree에서 mutable하게 존재하면서 필요할때마다 새로운 state를 저장함과 동시에 새롭게 rebuild된 Stateful Widget과의 링크만을 업데이트 해줍니다.

참고

https://www.youtube.com/watch?v=OvWrOKMqSG0&list=PLQt_pzi-LLfoOpp3b-pnnLXgYpiFEftLB&index=2 

 

728x90

'Study > Flutter' 카테고리의 다른 글

[Flutter] Factory 패턴  (0) 2021.07.13
[Flutter] Provider pattern (bloc -> provider )  (0) 2021.06.16
[UI초급] Container, materialApp, Scaffold  (0) 2021.06.07

Future, Isolate

다트에서 이벤트 루프에 등록되는 애들이 있음

보통은 다 바로 시작이 되는데 이벤트 루프에 등록되는 애들은 특징이 main()이 종료되어야지 실행

  1. network
  2. I/O (file 읽기 쓰기)
  3. Future(약속)

Future

import 'dart:io';

void download(){
    sleep(Duration(seconds:5));
    print('다운완료');
}

main(){
    download();
    print("메인종료");
}

----
다운완료
메인종료
----
  • 실행 순서
import 'dart:io';

void download(){
Future.delayed(Duration(seconds:5),(){
        print('이벤트 실행됨'): // 3
    });
    print('다운완료'); // 1
}

main(){
    download();
    print("메인종료"); // 2
}

----
다운완료
메인종료
이벤트 실행됨
----
  • Future return
import 'dart:io';

Future<String> download(){
// Future는 빈박스이다.
// 먼저 value는 빈박스(null)를 만들고 5초뒤 return을 받음
Future<String> value = Future.delayed(Duration(seconds:5),(){
        return '사과'; // 5초뒤에 return을 받음
    });
return value; // Futurn data는 Future로 받아야함
}

// 첫번째 방법
// await를 사용하기 때문에 async 키워드 필요
main() async {
    //빈박스를 먼저 받아오기 때문에 Future로 받아야함
    //그렇기에 기다릴 필요가 있음 -> await 사용 -> async 키워드 필요
    // Future<String> value = download();
    // 기다렸다 받기 때문에 Future로 받을 필요없음
    String value = await download();
    print(value);
}

// 두번째 방법
main(){
    download().then((value){
        // 빈박스가 채워져야 실행됨
        print(value);
    });
//메인 종료 후 download return 받음
}

Isolate

  • 동시에 실행
void download(var msg){
    Future((){
        for(int i = 0; i < 5; i++){
            sleep(Duration(seconds:1));
            print("download함수 : ${i}");
        }
    });
}

main(){
    // 독립적인 스레드
    Isolate.spawn(download,'msg');
    for(int i = 0; i < 5; i++){
        sleep(Duration(seconds:1));
        print("main함수 : ${i}");
    }
}

isolate는 다른 언어의 스레드와 똑같음

다른 점은 heap을 공유하지 않음. 독립적인 heap이 있음

int num = 10;

void change(var value){
    num = value;
    print("change : ${num}");
}

void check(var value){
    print("check : ${num}");
}

void main(){
    Isolate.spawn(change,20);
    Isolate.spawn(check,"");
}

---
check : 10
change : 20
---

어떤 이벤트가 들어왔을때 이 이벤트 때문에 오래 기다려야할 땐 Future

동시 실행 하고 싶을땐 isolate

728x90

'Study > Dart' 카테고리의 다른 글

[Dart] future, async, await, 실행순서  (0) 2021.06.15
[Dart] const 와 final 이해하기  (0) 2021.06.15
[Dart] 상속을 쓰는 이유  (0) 2021.06.07
[Dart] 상속  (0) 2021.06.07
[Dart] 접근지정자, 생성자, getter, setter  (0) 2021.06.04

동기 / 비동기

호출되는 함수의 작업 완료 여부를 누가 신경쓰느냐가 관심사

호출되는 함수에게 callback을 전달해서 호출되는 함수의 작업이 완료되면호출되는 함수가 전달받은 callback을 실행하고, 호출한 함수는 작업 완료 여부를 신경쓰지 않는다면 비동기이다.

호출하는 함수가 호출되는 함수의 작업 완료 후 return을 기다리거나호출되는 함수로부터 바로 return 받더라도 작업 완료 여부를 호출한 함수 스스로 확인하며 신경 쓴다면 동기이다.

동기

  • 두 개 이상의 작업의 시작시간, 종료시간이 같거나 시작과 동시에 종료할 것
  • Thread1이 작업을 시작 시키고, Task1이 끝날때까지 기다렸다 Task2를 시작한다.
  • 작업 요청을 했을 때 요청의 결과값(return)을 직접 받는 것이다.
  • 요청의 결과값이 return값과 동일하다.
  • 호출한 함수가 작업 완료를 신경 쓴다.

비동기

  • 비동기 프로그래밍이란 어떤 특정 작업을 할 때 기다리지 않고 다음 작업을 동시에 실행하는 것을 의미
  • 다른 작업과 시작, 종료 시간을 맞추지 말 것
  • Thread1이 작업을 시작 시키고, 완료를 기다리지 않고, Thread1은 다른 일을 처리할 수 있다.
  • 작업 요청을 했을 때 요청의 결과값(return)을 간접적으로 받는 것이다.
  • 요청의 결과값이 return값과 다를 수 있다.
  • 해당 요청 작업은 별도의 스레드에서 실행하게 된다.
  • 콜백을 통한 처리가 비동기 처리라고 할 수 있다.
  • 호출된 함수(callback 함수)가 작업 완료를 신경 쓴다.

블로킹/논블로킹

호출되는 함수가 바로 return하느냐 마느냐가 관심사

주로 IO의 읽기, 쓰기에서 사용

호출된 함수가 바로 return해서 호출한 함수에게 제어권을 넘겨주고호출한 함수가 다른 일을 할 수 있는 기회를 줄 수 있으면 non-blocking이다.

호출된 함수가 자신의 작업을 모두 마칠 때까지호출한 함수에게 제어권을 넘겨주지 않고 대기하게 만든다면 blocking이다.

블로킹

  • 자신의 작업을 하다가 다른 작업 주체가 하는 작업의 시작부터 끝까지 기다렸다가 다시 자신의 작업을 시작
  • 다른 작업을 하는 동안 자신의 작업을 일시정지할 것
  • 다른 작업의 주체가 작업하는동안 기다릴 것
  • 요청한 작업을 마칠 때까지 계속 대기한다.
  • 즉시 return한다.
  • return 값을 받아야 끝난다.
  • Thread 관점으로 본다면, 요청한 작업을 마칠 때까지 계속 대기하며 return 값을 받을 때까지 한 Thread를 계속 사용/대기 한다.

논블로킹

  • 다른 주체의 작업과 관계없이 자신의 작업을 계속
  • 다른 작업의 주체가 작업하는동안 기다리지 말 것
  • 요청한 작업을 즉시 마칠 수 없다면 즉시 return한다.
  • 즉시 리턴하지 않는다. (일을 못하게 막는다.)
  • Thread 관점으로 본다면, 하나의 Thread가 여러 개의 IO를 처리 가능하다.

동기/비동기와 블로킹/논블로킹의 조합

동기, 블로킹 조합

  • JDBC를 이용해 DB에 쿼리 질의를 날린다
  • 메서드에서 다른 메서드를 호출하여 결과값을 즉시 받아온다
  • 결과가 처리되어 나올때까지 기다렸다가 return 값으로 결과를 전달한다.

비동기, 블로킹 조합

블로킹 작업을 실행하였지만 자기도 모르게 블로킹 작업을 실행했을 때 이러한 결과

  • 비동기, 논블로킹 작업을 호출하고 자신의 작업을 하던 도중 호출한 작업의 결과 값을 조회하려고 했을 때(블로킹 메서드 실행)

동기, 논블로킹 조합

논블로킹으로 자신의 작업을 계속하고 있지만 다른 작업과의 동기를 위해 계속해서 다른 작업이 끝났는지 조회합니다.

  • Polling

비동기, 논블로킹 조합

자신의 작업이 멈추지도 않고, 다른 주체가 하는 작업의 결과가 나왔을 때 콜백을 설정하기도 합니다. 다른 주체에게 작업을 맡겨놓고 자신이 하던 일을 계속할 수 있기 때문에 해야 할 작업이 대규모이고, 동기가 필요하지 않을 때 효과적입니다.

작업 요청을 받아서 별도의 프로세서에서 진행하게 하고 바로 return(작업 끝)한다.

결과는 별도의 작업 후 간접적으로 전달(callback)한다.

  • 대규모 사용자에게 푸시메세지 전송
  • 다양한 외부 API를 한번에 호출할 때

참고

https://deveric.tistory.com/99

https://velog.io/@wonhee010/동기vs비동기-feat.-blocking-vs-non-blocking

728x90

메모리, heap, stack

위의 그림처럼

먼저, RAM 메모리에 적재 되어야 CPU에서 연산이 가능합니다.

메모리에는 Heap과 Stack 영역이 존재하는데 Dart에서 Heap은 class를 제외한 상단에서 선언한 모든 메소드 및 변수 그리고 main 클래스가 가장 먼저 적재됩니다. 

그러나 메서드의 중괄호 { } 안의 영역은 stack 영역으로써, 호출될 때 stack 영역이 만들어지며 중괄호 { }안의 메서드 및 변수가 적재되고 중괄호의 종료와 함께 없어집니다.

main 클래스 밖에서 선언된 class는 객체를 Animal a = Animal(); 와 같이 생성할때 heap영역에 class안의 변수와 메서드가 적재됩니다.

위와 같이 메서드가 호출될때 stack 영역 또한 생성됩니다.

상속 특징 1. 다형성

Dog 객체가 extends를 통해 animal을 상속 받음으로써 Animal 타입을 사용할 수 있습니다.

추가 예시로

햄버거 <=> 더블햄버거 extends 햄버거
위와 같이 패티 2장인 더블햄버거를 만들때 패티 1장인 햄버거를 상속받아 만들 경우 부모 자식 모두 햄버거 인것은 변함 없습니다.

 

다양한 이름으로 불릴 수 있어야 합니다.

상속 특징 2. 데이터가 아닌 타입을 물려줍니다.

위의 그림의 알고리즘에서 손님 Love가 추가될 때 원래 손님인 Ssar을 변경해야 하는 알고리즘의 수정이 필요합니다.

상속 특징 3. 추상화를 위해 사용합니다.

손님과 직원의 개념을 추상화 시키면서 ssar, love에게 손님의 추상화 된 개념을 주입하고, cos에게 직원의 추상화 된 개념을 주입함을 통해 알고리즘의 수정을 막고 재사용성을 증대 시킵니다.

 

참고

https://www.youtube.com/watch?v=H41XvJKJ4Tk&list=PL93mKxaRDidGEaUXprXqhNvSW02xCjLZI&index=17 

 

728x90

'Study > Dart' 카테고리의 다른 글

[Dart] const 와 final 이해하기  (0) 2021.06.15
[Dart] Future, Isolate  (0) 2021.06.08
[Dart] 상속  (0) 2021.06.07
[Dart] 접근지정자, 생성자, getter, setter  (0) 2021.06.04
[Dart] 클래스  (0) 2021.06.04

Container

html의 div 역할

class FirstApp extends StatelessWidget {
  const FirstApp({Key? key}) : super(key: key);

  @override
  Widget build(BuildContext context) {
    return Container(
      color: Colors.amber, // 배경색 변경
    );
  }
}

MaterialApp

Container의 한종류로서 안드로이드 친화적인 디자인으로 개발한다는 것을 알려줌 이와 대비로 ios 친화적인 cupertinoapp이 있음

class FirstApp extends StatelessWidget {
  const FirstApp({Key? key}) : super(key: key);

  @override
  Widget build(BuildContext context) {
    return MaterialApp();
  }
}

Scaffold

기본 뼈대를 가지고 있음 - Appbar, bottomNavigationBar ...

class FirstApp extends StatelessWidget {
  const FirstApp({Key? key}) : super(key: key);

  @override
  Widget build(BuildContext context) {
    return MaterialApp( // Android app 만들꺼에요
      home: Scaffold( // 기본 구조를 들고 있어요
        body: Text("hello"),
      ),
    );
  }
}

위의 경우 안드로이드 상단 바 영역을 제대로 표현 못하기에 안전한 영역에만 그리도록 해줄 필요가 있음 -> SafeArea

Scaffold 끝에 커서를 두고 alt(option) + enter → wrap with widget 을 사용하면 쉽게 바꿀 수 있음

class FirstApp extends StatelessWidget {
  const FirstApp({Key? key}) : super(key: key);

  @override
  Widget build(BuildContext context) {
    return MaterialApp( // Android app 만들꺼에요
      home: SafeArea(
        child: Scaffold( // 기본 구조를 들고 있어요
          body: Text("hello"),
        ),
      ),
    );
  }
}

AppBar

class FirstApp extends StatelessWidget {
  const FirstApp({Key? key}) : super(key: key);

  @override
  Widget build(BuildContext context) {
    return MaterialApp( // Android app 만들꺼에요
      home: SafeArea(
        child: Scaffold( // 기본 구조를 들고 있어요
          appBar: AppBar( //appbar를 그려줌
            backgroundColor: Colors.blue,
          ),
          body: Text("hello"),
        ),
      ),
    );
  }
}

appBar: AppBar(
  backgroundColor: Colors.blue,
  title: Text("First app"),
  leading: Icon(Icons.menu), //appbar의 햄버거 버튼추가
),

FloatingActionButton

class FirstApp extends StatelessWidget {
  const FirstApp({Key? key}) : super(key: key);

  @override
  Widget build(BuildContext context) {
    return MaterialApp( // Android app 만들꺼에요
      home: SafeArea(
        child: Scaffold( // 기본 구조를 들고 있어요
          appBar: AppBar(
            backgroundColor: Colors.blue,
            title: Text("First app"),
          ),
          body: Text("hello"),
          floatingActionButton: FloatingActionButton( //하단 floating 버튼
            onPressed: () {  },
            child: Text("button"),
          ),
        ),
      ),
    );
  }
}

BottomNavigationBar

class FirstApp extends StatelessWidget {
  const FirstApp({Key? key}) : super(key: key);

  @override
  Widget build(BuildContext context) {
    return MaterialApp( // Android app 만들꺼에요
      home: SafeArea(
        child: Scaffold( // 기본 구조를 들고 있어요
          appBar: AppBar(
            backgroundColor: Colors.blue,
            title: Text("First app"),
          ),
          body: Text("hello"),
          floatingActionButton: FloatingActionButton(
            onPressed: () {  },
            child: Text("button"),
          ),
          bottomNavigationBar: BottomNavigationBar( // 하단 네비게이션바
            backgroundColor: Colors.yellow, 
            items: [ // 네비게이션 바에 들어갈 항목
              BottomNavigationBarItem(
                  label:"hello",
                  icon: Icon(
                    Icons.access_alarm_rounded,
                  )),
              BottomNavigationBarItem(
                  label:"hello",
                  icon: Icon(
                    Icons.access_alarm_rounded,
                  )),
            ],
          ),
        ),
      ),
    );
  }
}
728x90

'Study > Flutter' 카테고리의 다른 글

[Flutter] Factory 패턴  (0) 2021.07.13
[Flutter] Provider pattern (bloc -> provider )  (0) 2021.06.16
[Flutter] Stateful widget  (0) 2021.06.14

+ Recent posts