エラーの原因
個人開発アプリのアップデートのための実装中に「Message from debugger: Terminated due to signal 9」というエラーが起きたので調べてみました。
CPU利用率が大きい・メモリ使用量が大きいことが原因と考えられるエラーが発生し、クラッシュしました。
CPU利用率 | メモリ使用量 |
---|---|
改善・考察
アプリの状態によって改善方法は変わってくると思いますが、自分の個人開発のアプリでエラーの原因を考察しながら改善してみました。
HomeView画面 | ProfileView画面 |
---|---|
①改善
LazyViewを作成
struct LazyView<Content: View>: View {
var content: () -> Content
var body: some View {
self.content()
}
}
HomeViewの一つひとつの投稿のCellがPostViewです。
PostViewの画面遷移の一部
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に変更。
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
}
}
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利用率・メモリ使用量ともに改善し、クラッシュしないように実装できました。
おわり
まだまだCPU使用量やメモリへの影響について勉強中ですが、CPU利用率・メモリ使用量が過剰でアプリがクラッシュするエラーが起きたので、自分のアプリでの改善方法を記事にしてみました。
参考文献