「Message from debugger: Terminated due to signal 9」エラーの原因と対応~CPU利用率・メモリ使用量改善~

# エラーの原因
個人開発アプリのアップデートのための実装中に「Message from debugger: Terminated due to signal 9」というエラーが起きたので調べてみました。
**CPU利用率が大きい・メモリ使用量が大きい**ことが原因と考えられるエラーが発生し、クラッシュしました。
|CPU利用率|メモリ使用量|
|---|---|
|<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3090887/5b331c68-84db-2072-6f63-49f52e114f19.png">|<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3090887/fa65417e-3a59-d781-36b8-e3733bcb79ed.png">|

# 改善・考察
アプリの状態によって改善方法は変わってくると思いますが、自分の個人開発のアプリでエラーの原因を考察しながら改善してみました。
|HomeView画面|ProfileView画面|
|---|---|
|<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3090887/5a461d1c-2c89-4489-4fc3-eec53f63ea40.png">|<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3090887/fb31a2dc-f2ae-743e-bdfb-1cfa57982e7d.png">|


## ①改善
LazyViewを作成

```Swift
struct LazyView<Content: View>: View {
    var content: () -> Content
    var body: some View {
        self.content()
    }
}
```
HomeViewの一つひとつの投稿のCellがPostViewです。
PostViewの画面遷移の一部
```Swift
NavigationLink(destination: {
                    if let myUserID = currentUserID, let profileBio = currentBio {
                        if post.userID == myUserID {
                            // LazyView作成
                            LazyView {
                                ProfileView(isMyProfile: true, posts: posts, profileBio: profileBio, profileDisplayName: post.username, profileUserID: post.userID)
                            }
                        } else {
                            // LazyView作成
                            LazyView {
                                ProfileView(isMyProfile: false, posts: posts, profileBio: "", profileDisplayName: post.username, profileUserID: post.userID)
                            }
                        }
                    }
                }, label: {
                    Image(uiImage: profileImage)
                        .resizable()
                        .scaledToFill()
                        .frame(width: 50, height: 50, alignment: .center)
                        .clipShape(RoundedRectangle(cornerRadius: 60))
                        .overlay {
                            RoundedRectangle(cornerRadius: 60)
                                .stroke(Color.black, lineWidth: 1.0)
                        }
                        .padding(.leading, 10)
                    Text(post.username)
                        .font(.title2)
                        .fontWeight(.medium)
                        .foregroundStyle(.black)
                        .padding(.leading, 10)
                })
```
ProfileViewが実際に必要になる前に作成されてしまっていたが、作成したLazyViewを間に挟むことによって実際にボタンをクリックしたときにのみ作成されるように改善しました。
## ②改善
PostModelをstructからclassに変更。
```Swift
struct PostModel: Hashable {
    var postID: String
    var userID: String
    var username: String 
    var caption: String 
    var dateCreated: Date
    var likeCount: Int
    var likedByUser: Bool
    var comentsCount: Int 
}

extension PostModel: Identifiable {
    var id: String {
        postID
    }
}
```

```Swift
class PostModel: Hashable {
    var postID: String 
    var userID: String
    var username: String
    var caption: String 
    var dateCreated: Date
    var likeCount: Int
    var likedByUser: Bool 
    var comentsCount: Int

    init(postID: String, userID: String, username: String, caption: String, dateCreated: Date, likeCount: Int, likedByUser: Bool, comentsCount: Int) {
        self.postID = postID
        self.userID = userID
        self.username = username
        self.caption = caption
        self.dateCreated = dateCreated
        self.likeCount = likeCount
        self.likedByUser = likedByUser
        self.comentsCount = comentsCount
    }

    func hash(into hasher: inout Hasher) {
        hasher.combine(postID)
        hasher.combine(userID)
        hasher.combine(username)
        hasher.combine(caption)
        hasher.combine(dateCreated)
        hasher.combine(likeCount)
        hasher.combine(likedByUser)
        hasher.combine(comentsCount)
    }

    static func == (lhs: PostModel, rhs: PostModel) -> Bool {
        return lhs.postID == rhs.postID &&
               lhs.userID == rhs.userID &&
               lhs.username == rhs.username &&
               lhs.caption == rhs.caption &&
               lhs.dateCreated == rhs.dateCreated &&
               lhs.likeCount == rhs.likeCount &&
               lhs.likedByUser == rhs.likedByUser &&
               lhs.comentsCount == rhs.comentsCount
    }
}

extension PostModel: Identifiable {
    var id: String {
        postID
    }
}
```
structでは値が渡されるたびにコピーが作成されてしまって、大量のデータを扱う場合や頻繁なデータの更新で、コピーによるオーバーヘッドがCPUに負荷をかけてたのが原因だと考察した。一方、classは参照型で、コピーされるのはポインタだけで、データのコピー回数が減り、メモリ使用量やCPU負荷が大幅に軽減できると考えた。

# 結果
CPU利用率・メモリ使用量ともに改善し、クラッシュしないように実装できました。
![c786ec6ea28d008f4cf401702048b24c.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3090887/a97430de-5c1d-d053-f94d-6109daf382e6.png)


![1e4657f5be8e10885c609a77ca601558.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3090887/8d83cbc7-7d7f-1392-26e8-49029ff68354.png)

# おわり
まだまだCPU使用量やメモリへの影響について勉強中ですが、CPU利用率・メモリ使用量が過剰でアプリがクラッシュするエラーが起きたので、自分のアプリでの改善方法を記事にしてみました。

# 参考文献

https://www.yuukinishiyama.com/2019/08/22/message-from-debugger-terminated-due-to-signal-9/

https://github.com/swiftlang/swift-evolution/blob/main/proposals/0206-hashable-enhancements.md