String.hashCode는 같은 문자열이지만 때로는 다른 값으로 바뀔 수 있습니다

6307 단어 FlutterDarttech
[추기] 이 오류는 수정이 됐어요.
Fluter/Dart에서 프로그램을 개발할 때 맵에서 '같은 문자열이 다른 키로 들어오는 것' 현상을 만났습니다.더 조사해 보면 같은 문자열String.hashCode이 다른 이유가 무엇인지 알 수 있다.
이것저것 여러 가지 이유로 Dart의 가장자리를 밟는 버그가 겹쳤지만 upstream에서 해결됐다.
  • https://dart-review.googlesource.com/c/sdk/+/68042
  • https://github.com/flutter/flutter/issues/20122
  • 어렵기 때문에 경과를 기록으로 남기다.

    대략적 실현


    문자열을 처리하고 반환할 수 있는 함수는 다음과 같습니다.
    String foo(String name) {
      return "prefix-${name}";
    }
    
    Map에 데이터를 넣는 프로세스:
    Map data = new Map<String, String>();
    
    String str1 = foo("test");
    
    // ...
    
    // str1 は "prefix-test" という文字列
    data[str1] = "value";
    
    Map에서 데이터를 체크 아웃하는 프로세스:
    String str2 = foo("test");
    
    // str2 は "prefix-test" という文字列
    // str1 と同じ
    String value = data[str2];
    
    // "value" を期待するが null になる
    print(value);
    
    위에서 말한 바와 같이 Map에 대해'동일 문자열'을 사용하여 접근했지만 예상대로 처리되지 않았다.

    hashCode

    Map 대상의 hashCode를 사용하여 저장 위치를 제어한다(여러 언어가 이렇게 이루어졌기 때문에 Dart도 그렇다고 생각한다). 그래서 hashCode는 이상하다. 그래서 다음과 같이'같은 문자열, 다르다hashCode'를 확인했다.
    print("${str1 == str2}"); // true
    print("${str1.hashCode == str2.hashCode}"); // false!!!
    
    통상적인Object.hashCode은'동일성을 나타내는 것'을 토대로 계산된 것이다.이런 상황에서 같은 점은 메모리의 주소도 같다는 점이다.
    그러나 String,Point,Rect 같은 클래스에서 같은 값이면 같은 값==이 되돌아와true하면 같은 값hashCode으로 처리할 수 있다.따라서 다른 곳에서 생성된 대상==이라도 같은 키String로 사용할 수 있다.이번 문제는 어떤 이유로 같은 값Map이 다른 값String으로 바뀌었기 때문이다.
    또 이런 현상은 iOS 시뮬레이터 및 실제 기기에서만 발생하며, 안드로이드 시뮬레이터 및 실제 기기에서는 일어나지 않는다.

    identityHashCode


    상기 코드에서 생략된 부분은 문자열hashCode에 대한 호출str1의 코드가 있다.호출identityHashCode이 변경identityHashCode의 원인이다.
    identity HashCode란 무엇입니까?
    Returns the identity hash code of hashCode .
    Returns the same value as object if [object] has not overridden
    [Object.hashCode]. Returns the value that [Object.hashCode] would return
    on this object, even if object.hashCode has been overridden.
    This hash code is compatible with [identical].
    덮어쓰기hashCode의 클래스 대상에 대해서도 기본 클래스hashCode의 값을 얻을 수 있는 함수다.등치라도 다른 대상이라면 다른 대상으로 처리할 때 이 함수Object.hashCode 또는identityHashCode를 사용한다.
    그러나 이 함수 내부 호출identical의 값으로 Object.hashCode의 결과도 캐시되었다.String.hashCode의 계산은 어느 정도의 원가가 필요하기 때문에 계산된 물건은 캐시hashCode가 필요하다면 문제가 된다.
    일반적으로 코드를 쓰면String 사용하지 않기 때문에 대부분의 응용 프로그램은 문제가 없을 것이라고 생각합니다.

    대책

    identityHashCode는 더 이상 사용하지 않습니다.이 함수를 사용하지 않아도 가능한 코드는 동일성이라기보다는'동일 여부'를 기반으로 처리돼 이렇게 수정됐다.
    어쨌든 Flutter가 사용하는Dart의 버전이 상승하고 그에 상응하는 수정이 포함되며 그 전에 대응해야 할 경우 코드를 다시 수정할 수 있다.


    원인을 규명하기 전에 시도해 본 일
  • Flutter 버전 변경(Dart 버전도 바뀌었기 때문)
  • 각종 캐시 지우기(fluter/bin/cache, ~/.pub-cache,/path/to/app/build)
  • print를 곳곳에 끼워넣고 조사값이 어디에서 변했는지
  • 이 문장은 Qiita의 문장을 도출했다

    좋은 웹페이지 즐겨찾기