Skip to content
isdnetworks
Go back

저장 파일이 깨지자 앱이 안 떴다

앱이 아예 안 뜬다는 문의가 들어왔고 지웠다 다시 깔면 된다고 했다. 그러면 진행 상황이 다 날아간다.

Table of contents

Open Table of contents

시작할 때 저장 파일을 읽는다

앱이 켜지면 저장 파일부터 읽는데 그 안의 개수 값이 깨져 있으면 그만큼 메모리를 잡으려다 죽는다. 파일이 잘렸으면 읽을 것이 없어 값이 초기화 안 된 채 남는다.

쓰던 판의 CCUserDefault 도 안 쓰고 직접 저장하고 있었는데 그쪽도 사정은 같았다. flush() 가 XML 파일을 통째로 덮어쓰는 구조라 쓰는 도중에 죽으면 반만 쓰인 파일이 남는다. 전화가 오거나 배터리가 나가면 그렇게 된다.

다 쓰고 나서 바꿔치기했다

임시 파일에 다 쓰고 나서 rename() 으로 이름을 바꾸게 했다. POSIX 규격상 같은 저장소 안에서 부르면 원자적으로 교체돼서 중간 상태가 안 보인다. 다른 저장소로 넘기면 EXDEV 로 실패한다.

FILE* fp = fopen(tmp.c_str(), "wb");
fwrite(...);
fflush(fp);
fsync(fileno(fp));
fclose(fp);
rename(tmp.c_str(), path.c_str());

처음에는 fflush 만 하고 넘어갔다. 찾아보니 그건 stdio 버퍼를 커널로 넘기는 것까지이고 디스크로 내리는 것은 fsync 다. 전원이 나가면 이름은 바뀌었는데 내용이 없는 경우가 나올 수 있다. 파일을 새로 만든 것까지 확실히 하려면 그 파일이 담긴 디렉터리에도 fsync 를 걸어야 한다.

읽을 때 확인했다

그래도 깨질 수 있으니 읽을 때 세 가지를 확인하게 했다. 파일 앞에 형식 표시를 두고 그것이 맞는지 보고, 개수 값이 범위 안인지 보고, fread 가 돌려주는 개수를 본다.

if (fread(&gold, sizeof(gold), 1, fp) != 1) {
    CCLOG("파일이 잘림");
    return false;
}

마지막이 중요했다. 읽기가 실패해도 돌려주는 값을 안 보면 그냥 넘어간다.

못 읽었을 때의 선택지

읽기가 실패하면 새로 시작하거나 이전 저장을 읽거나 안 켜지거나 셋 중 하나다. 두 번째를 쓰려고 저장할 때 이전 것을 한 벌 남기게 했다.

읽는 순서는 본 파일과 백업과 새로 시작이다. 최악의 경우에도 앱은 켜진다. 백업에서 읽었거나 새로 시작했으면 화면에 알렸다. 아무 말 없이 진행이 줄어 있으면 더 혼란스럽다.

일부러 깨뜨려서 각 경로가 도는지도 봤다. 파일을 반으로 자르고 앞부분을 바꾸고 둘 다 지우고 백업만 지우는 네 가지를 해 보니 본 파일과 백업이 함께 깨진 경우를 못 다루고 있었다. 그 경로를 추가했다.

정리


Share this post on:

Previous Post
코드 하나로 두 플랫폼을 낸다고 했다
Next Post
글자가 잘려서 기능이 안 됐다