dart Flutter iOS崩溃,出现EXC_BAD_ACCESS错误

v64noz0r  于 2023-07-31  发布在  Flutter
关注(0)|答案(4)|浏览(214)

我正在用Flutter开发一个应用程序,并在物理iOS设备(iPhone 7)上测试该应用程序。
iOS版本是:15.3.1
Flutter的版本是:2.10.3
当我测试我的应用程序时,我偶尔会崩溃。崩溃时出现以下错误。它并不总是在同一个地方崩溃,所以我不知道在这里分享什么代码。错误信息本身并没有说太多给我,我找不到任何有用的信息在网上关于这个错误。

* thread #1, queue = 'com.apple.main-thread', stop reason = EXC_BAD_ACCESS (code=1, address=0x443b8000443b8000)
    frame #0: 0x0000000198405048 libobjc.A.dylib`objc_msgSend + 8
libobjc.A.dylib`objc_msgSend:
->  0x198405048 <+8>:  ldr    x13, [x0]
    0x19840504c <+12>: and    x16, x13, #0xffffffff8
    0x198405050 <+16>: mov    x15, x16
    0x198405054 <+20>: ldr    x11, [x16, #0x10]
Target 0: (Runner) stopped.
Lost connection to device.
Exited (sigterm)

字符串
接下来我可以检查什么?

补遗

我想知道我是不是对我的流构建器做错了什么。下面是我的代码的简化版本:

class PrepareList extends StatefulWidget {
  final String place;
  const PrepareList({
    Key? key,
    required this.place,
  }) : super(key: key);

  @override
  State<PrepareList> createState() =>
      _PrepareListState();
}

class _PrepareListState
    extends State<PrepareList> {
  late final Stream? _listStream;

  @override
  void initState() {
    super.initState();

    String dbChild = "events/" + widget.place + "/";

    final db = FirebaseDatabase.instance
        .ref()
        .child(dbChild)
        .orderByKey()
        .limitToLast(1500);

    _listStream = db.onValue;
  }

  @override
  Widget build(BuildContext context) {
    return StreamBuilder(
        stream: _listStream,
        builder: (context, AsyncSnapshot<dynamic> dbEvent) {
          if (dbEvent.hasError) {
            return CircularProgressIndicator();
          }
          else if (dbEvent.hasData) {
            DataSnapshot dataSnapshot = dbEvent.data!.snapshot;

            List<EventDetails> placeEventList = [];
            if (dataSnapshot.value != null) {
              (dataSnapshot.value as Map<dynamic, dynamic>)
                  .forEach((key, value) {
                placeEventList.add(EventDetails.fromRTDB(value));
              });
              placeEventList
                  .sort((a, b) => a.dateEST.compareTo(b.dateEST));

              return AttendeeList(placeEventList: placeEventList, place: place);
            } else {
              return PlaceDataNotAvailable(place: widget.place);
            }
          } else {
            return CircularProgressIndicator();
          }
        });
  }
}

class AttendeeList extends StatefulWidget {
  final List<EventDetails> placeEventList;
  final String place;
  const AttendeeList({
    Key? key,
    required this.placeEventList,
    required this.place,
  }) : super(key: key);

  @override
  State<AttendeeList> createState() =>
      _AttendeeListState();
}

class _AttendeeListState
    extends State<AttendeeList> {
  late final Stream? _attendeeListStream;

  @override
  void initState() {
    super.initState();

    String dbChild = "attendees/" + widget.place + "/";

    final db = FirebaseDatabase.instance
        .ref()
        .child(dbChild)
        .orderByKey()
        .limitToLast(1500);

    _listStream = db.onValue;
  }

  @override
  Widget build(BuildContext context) {
    return StreamBuilder(
        stream: _attendeeListStream,
        builder: (context, AsyncSnapshot<dynamic> dbEvent) {
          if (dbEvent.hasError) {
            return CircularProgressIndicator();
          }
          else if (dbEvent.hasData) {
            DataSnapshot dataSnapshot = dbEvent.data!.snapshot;

            List<AttendeeDetails> attendeeList = [];
            if (dataSnapshot.value != null) {
              (dataSnapshot.value as Map<dynamic, dynamic>)
                  .forEach((key, value) {
                attendeeList.add(EventDetails.fromRTDB(value));
              });
              attendeeList
                  .sort((a, b) => a.dateEST.compareTo(b.dateEST));

              return Scaffold(
                body: ShowLists(placeEventList, attendeeList);
            } else {
              return PlaceDataNotAvailable(place: widget.place);
            }
          } else {
            return CircularProgressIndicator();
          }
        });
  }
}


上面的小部件可以在应用程序的生命周期中多次调用。用户通过在初始屏幕上选择place进入此屏幕,该屏幕将执行PrepareList有状态小部件中的代码,然后调用AttendeeList有状态小部件。
我想强调的是,PrepareListAttendeeList都使用流。并且每次执行这些小部件中的代码时,从数据库下载大量节点(每个小部件1500个)。
一次执行可以如下所示:

PrepareList("London");


另一个执行可能看起来如下所示,在同一屏幕上呈现新的项目列表:

PrepareList("Manhattan");


我观察到的是:
当我第一次运行PrepareList("London");时,需要一些时间(3到4秒)才能看到屏幕上的内容。然后我运行PrepareList("Manhattan");,它也需要大约3到4秒来显示内容。但是当我再次运行PrepareList("London");时,内容出现在屏幕上的速度非常快,在~1秒钟内。
为了能够调用PrepareList(),我需要转到另一个屏幕,这意味着-在我的理解中-每次我离开与上面2个小部件相关联的屏幕时,我的流订阅都会被取消。但是,是不是流本身不会被取消,数据仍保留在内存中?

**我怀疑的是:**当使用应用程序时,当我在多个地方(多次)调用PrepareList(...)时,它会在内存中加载越来越多的数据,并且从不清理它。过了一段时间,应用程序消耗了所有可用的内存,并通过给出上面的错误而崩溃,这告诉我没有任何意义。

而且随着PrepareList(...)在使用应用程序时执行的次数越来越多,iPhone 7变得发热,我很容易就能感觉到。我甚至用iPhone 12进行了测试,它不会像iPhone 7那样发热,但也会崩溃。
我甚至尝试将dispose添加到这两个类中:

@override
  void dispose() {
    super.dispose();
  }


……但还是无济于事。
我的流实现是真的吗?我怀疑是这次坠机的根本原因?

附录2

我一直在努力我使用该应用程序的方式,使PrepareList("...");被触发了几次。我还观察了devtools中的内存使用情况。我可以观察到内存使用量随着时间的推移而增加。我得到了一个新的错误,这次说得更具体:

[ServicesDaemonManager] interruptionHandler is called. -[FontServicesDaemonManager connection]_block_invoke
[tcp] tcp_input [C17.1.1:3] flags=[R] seq=3749683210, ack=0, win=0 state=LAST_ACK rcv_nxt=3749683210, snd_una=3584722489
[tcp] tcp_input [C17.1.1:3] flags=[R] seq=3749683210, ack=0, win=0 state=CLOSED rcv_nxt=3749683210, snd_una=3584722489
* thread #46, name = 'DartWorker', stop reason = EXC_RESOURCE RESOURCE_TYPE_MEMORY (limit=1450 MB, unused=0x0)
    frame #0: 0x0000000108ef4d0c Flutter`dart::CompilerPass_TypePropagation::DoBody(dart::CompilerPassState*) const + 1644
Flutter`dart::CompilerPass_TypePropagation::DoBody:
->  0x108ef4d0c <+1644>: str    xzr, [x20, x28, lsl #3]
    0x108ef4d10 <+1648>: ldr    x8, [x22, #0x48]
    0x108ef4d14 <+1652>: cmp    x24, x8
    0x108ef4d18 <+1656>: b.ge   0x108ef4d84               ; <+1764>
Target 0: (Runner) stopped.
Lost connection to device.


这一次,它显示为EXC_RESOURCE RESOURCE_TYPE_MEMORY (limit=1450 MB, unused=0x0)。看起来,内存使用量随着时间的推移而增加。为什么当用户离开手机屏幕时内存没有释放?(我问这个问题是因为怀疑即使在用户移动到另一个屏幕后,流仍然占用内存...)

iyr7buue

iyr7buue1#

对于永久增加的内存使用量有几点想法,因为如果没有完整的例子,几乎不可能回答你的问题。

  1. Firebase缓存对象,这就是为什么它更快,第二次。这不应该影响你的内存使用量,破坏应用程序。
    1.我问这个问题是因为怀疑即使在用户移动到另一个屏幕后,流仍然占用内存...
    也许你正在使用Navigator.push*,并且从未弹出任何以前的路由,以前路由中的所有列表都保存在内存中。
    1.跟踪dispose调用,看看小部件是否得到清理。通过使用日志记录、断点甚至更好地使用DevTools内存视图来跟踪小部件计数:https://docs.flutter.dev/development/tools/devtools/memory内存快照可以准确地告诉您内存的使用情况。
    1.一般来说,最好将排序从build函数中移除。但这更多的是一个性能提示,而不是一个问题。
    1.您的内存问题可能与EXC_BAD_ACCESS崩溃无关,但您应该首先尝试解决它们。这将更容易找到潜在的原因,如果有任何。
ergxz8rk

ergxz8rk2#

我知道我的问题没有真正组织起来,我无法提供一个适当的代码来复制这个问题。经过多次尝试,我似乎解决了这个问题。由于它只发生在有时,图片对我来说仍然模糊,但我至少知道是什么解决了我的问题。
提供一个完整的代码仍然很困难,但我将尝试解释是什么解决了我的问题,在这里用一些伪代码,我希望这可能是有帮助的其他人谁可能遭受类似的问题。
不管我对它有多不满意,我在应用程序的某些部分有一些不可避免的嵌套流。在一个页面上,我有多个嵌套的流构建器,但我将在下面仅用2个流来说明情况:

StreamBuilder1
   --> Read data A from database
   StreamBuilder2
      ---> Read data B from database (by using data from data A)

字符串
从数据库阅读数据B时,需要用到StreamBuilder 1的一些输入。有一种依赖性。
这两个流偶尔会导致页面刷新。可能导致EXC_BAD_ACCESSSS的原因之一似乎是试图读取已经释放的内存部分。我“怀疑”的是,当StreamBuilder 2需要使用data A从DB中读取自己的数据时,data A偶尔会不在内存中。
我做了如下改动。。

StreamBuilder1
   --> Read data A from database
   FutureBuilder2
      ---> Read data B from database (by using data from data A)


我用FutureBuilder替换了StreamBuilder 2,意外的崩溃消失了。
我基本上有多个嵌套的流构建器。这将是伟大的有流为所有他们,但它不是真的必须的。只有一个数据提要需要用流构建器实现,我做到了。我用future builder替换了所有其他流。从那以后,我再也没有遇到过EXC_BAD_ACCESS崩溃。
我可能误解了事情是如何运作的,或者这个改变是如何解决我的问题的。但这至少是我所做的,也是最终真正解决我问题的。任何人都有任何进一步的意见,他/她是非常欢迎分享他们,因为我很有兴趣学习什么事情,我需要考虑时,使用嵌套流,因为它是一个有点难以找到好的例子在那里。
侧记:我甚至研究了一下BLoC,希望它能帮助解决这个问题。我想对于那些已经知道如何使用它的人来说很容易。但这是相当令人生畏的,特别是如果你想在从firebase阅读数据时将其用于多个流,我找不到任何如何使用BLoC的例子。

**附录(2022年6月24日):**我仍然有这个问题,偶尔会发生。这在Android中还没有发生,但该应用程序因未知原因被关闭。在我上面提到的变化之后,这种情况并不经常发生,但问题还没有完全消失。

1cosmwyk

1cosmwyk3#

在我的情况下,它的崩溃,由于firebase推送通知。我从通知有效负载及其工作中删除了通知:

{
    // "notification":{"title": "title","body": "body" }, DON'T USE THIS LINE EVER!
    "to": "token",
    "priority": "high",
    "data": {
      "click_action": "FLUTTER_NOTIFICATION_CLICK",
      "content": {
        "payload":{
          "type": "Message",
          "userId": "123",
          "postId": "postId"
        },
        "id": 100,
        "channelKey": "basic_channel",
        "body": "body",
        "title": "title"
      }
    },
    "mutable_content" : true,
    "content_available": true
  }

字符串

jslywgbw

jslywgbw4#

我有一个类似的错误(EXC_BAD_ACCESS)。在我的例子中,这是一个无意义的(不确定是否正确)使用late关键字。(截图)可能是从别的地方复制粘贴过来的吧……分析器和编译器都没有警告这个错误。仅平台级崩溃日志。此外,扑干净帮助,但一段时间后错误再次发射。


的数据

相关问题