Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[Bug]: ShapeSource.getClusterLeaves crash on iOS (NOBRIDGE mode) #3618

Open
villemuittari opened this issue Sep 10, 2024 · 3 comments
Open
Labels
bug 🪲 Something isn't working

Comments

@villemuittari
Copy link

villemuittari commented Sep 10, 2024

Mapbox Implementation

Mapbox

Mapbox Version

11.3.0

React Native Version

0.75.2

Platform

iOS

@rnmapbox/maps version

10.1.31

Standalone component to reproduce

import React, {useRef} from 'react';
import {Button} from 'react-native';
import {
  Images,
  MapView,
  ShapeSource,
  SymbolLayer,
  CircleLayer,
  Camera,
} from '@rnmapbox/maps';

const styles = {
  mapView: {width: '100%', height: 500},
  cluster: {
    circleRadiusTransition: {duration: 5000, delay: 0},
    circleColor: '#ff0000',
  },
};

const features = {
  type: 'FeatureCollection',
  features: [
    {
      type: 'Feature',
      id: 'a-feature',
      properties: {
        icon: 'example',
      },
      geometry: {
        type: 'Point',
        coordinates: [-74.00597, 40.71427],
      },
    },
    {
      type: 'Feature',
      id: 'b-feature',
      properties: {
        icon: 'example',
      },
      geometry: {
        type: 'Point',
        coordinates: [-74.001097, 40.71527],
      },
    },
    {
      type: 'Feature',
      id: 'c-feature',
      properties: {
        icon: 'example',
      },
      geometry: {
        type: 'Point',
        coordinates: [-74.00697, 40.72427],
      },
    },
  ],
};

const BugReportExample = () => {
  const mapView = useRef<MapView>(null);
  const shapeSourceMarkers = useRef<ShapeSource>(null);

  const getVisibleMarkers = async () => {
    let visibleMarkers: any[] = [];
    const arrFeatures = await mapView.current?.queryRenderedFeaturesInRect(
      [],
      [],
      ['marker', 'cluster'],
    );
    if (!arrFeatures) {
      return visibleMarkers;
    }
    for (let index = 0; index < arrFeatures.features.length; index++) {
      const feature = arrFeatures.features[index];
      const properties = feature.properties;
      if (!properties) {
        continue;
      }

      if (properties.cluster) {
        try {
          // ====> THE FOLLOWING LINE CRASHES THE WHOLE APP <====
          const collection = await shapeSourceMarkers.current?.getClusterLeaves(
            feature,
            properties.point_count,
            0,
          );
          collection.features.forEach((f: any) => {
            visibleMarkers.push(f);
          });
        } catch (error: any) {
          console.debug('error', error);
        }
      } else {
        visibleMarkers.push(feature);
      }
    }
    console.debug('visibleMarkers', visibleMarkers);
    return visibleMarkers;
  };

  const circleLayerStyle = {
    ...styles.cluster,
    ...{circleRadius: 15},
  };

  return (
    <>
      <MapView style={styles.mapView} ref={mapView}>
        <Camera
          defaultSettings={{
            centerCoordinate: [-74.001097, 40.71527],
            zoomLevel: 10,
          }}
        />
        <Images images={{example: {uri: 'https://27crags-sandbox.s3.amazonaws.com/v6-icon.png'}}} />
        <ShapeSource
          id={'shape-source-markers'}
          ref={shapeSourceMarkers}
          shape={features}
          cluster={true}
          clusterRadius={30}
          clusterMaxZoomLevel={19}>
          <SymbolLayer
            id="marker"
            style={{
              iconImage: ['get', 'icon'],
              iconAllowOverlap: false,
              iconSize: 0.5,
            }}
            slot={'middle'}
            filter={['!', ['has', 'point_count']]}
          />
          <SymbolLayer
            id="pointCount"
            style={{
              textField: ['format', ['concat', ['get', 'point_count']]],
              textSize: 12,
              textPitchAlignment: 'viewport',
              textAllowOverlap: false,
            }}
          />
          <CircleLayer
            id={'cluster'}
            belowLayerID="pointCount"
            style={circleLayerStyle}
            slot={'bottom'}
            filter={['has', 'point_count']}
          />
        </ShapeSource>
      </MapView>
      <Button
        title="Count all items inside clusters"
        onPress={() => {
          getVisibleMarkers().then(visibleMarkers => {
            console.debug('visibleMarkers', visibleMarkers);
          });
        }}
      />
    </>
  );
};

export default BugReportExample;

Observed behavior and steps to reproduce

When trying to get clusterLeaves using the method getClusterLeaves() of ShapeSource the app crashes immediately.

0   libobjc.A.dylib               	       0x18005d44c objc_retain + 8
1   libobjc.A.dylib               	       0x1800856c0 objc_storeStrong + 44
2   RNLatest                      	       0x101870b78 -[RNMBXShapeSourceModule getClusterLeaves:featureJSON:number:offset:resolve:reject:] + 120 (RNMBXShapeSourceModule.mm:56)
3   CoreFoundation                	       0x1804b4720 __invoking___ + 144
4   CoreFoundation                	       0x1804b1a84 -[NSInvocation invoke] + 276
5   CoreFoundation                	       0x1804b1d1c -[NSInvocation invokeWithTarget:] + 60
6   RNLatest                      	       0x1012bc5c8 invocation function for block in facebook::react::ObjCTurboModule::performMethodInvocation(facebook::jsi::Runtime&, bool, char const*, NSInvocation*, NSMutableArray*) + 240 (RCTTurboModule.mm:347)
7   RNLatest                      	       0x1012d2d90 facebook::react::ObjCTurboModule::performMethodInvocation(facebook::jsi::Runtime&, bool, char const*, NSInvocation*, NSMutableArray*)::$_2::operator()() const + 96 (RCTTurboModule.mm:380)
8   RNLatest                      	       0x1012d2d24 decltype(std::declval<facebook::react::ObjCTurboModule::performMethodInvocation(facebook::jsi::Runtime&, bool, char const*, NSInvocation*, NSMutableArray*)::$_2&>()()) std::__1::__invoke[abi:ue170006]<facebook::react::ObjCTurboModule::performMethodInvocation(facebook::jsi::Runtime&, bool, char const*, NSInvocation*, NSMutableArray*)::$_2&>(facebook::react::ObjCTurboModule::performMethodInvocation(facebook::jsi::Runtime&, bool, char const*, NSInvocation*, NSMutableArray*)::$_2&) + 24 (invoke.h:340)
9   RNLatest                      	       0x1012d2cdc void std::__1::__invoke_void_return_wrapper<void, true>::__call[abi:ue170006]<facebook::react::ObjCTurboModule::performMethodInvocation(facebook::jsi::Runtime&, bool, char const*, NSInvocation*, NSMutableArray*)::$_2&>(facebook::react::ObjCTurboModule::performMethodInvocation(facebook::jsi::Runtime&, bool, char const*, NSInvocation*, NSMutableArray*)::$_2&) + 24 (invoke.h:415)
10  RNLatest                      	       0x1012d2cb8 std::__1::__function::__alloc_func<facebook::react::ObjCTurboModule::performMethodInvocation(facebook::jsi::Runtime&, bool, char const*, NSInvocation*, NSMutableArray*)::$_2, std::__1::allocator<facebook::react::ObjCTurboModule::performMethodInvocation(facebook::jsi::Runtime&, bool, char const*, NSInvocation*, NSMutableArray*)::$_2>, void ()>::operator()[abi:ue170006]() + 28 (function.h:193)
11  RNLatest                      	       0x1012d1a1c std::__1::__function::__func<facebook::react::ObjCTurboModule::performMethodInvocation(facebook::jsi::Runtime&, bool, char const*, NSInvocation*, NSMutableArray*)::$_2, std::__1::allocator<facebook::react::ObjCTurboModule::performMethodInvocation(facebook::jsi::Runtime&, bool, char const*, NSInvocation*, NSMutableArray*)::$_2>, void ()>::operator()() + 28 (function.h:364)
12  RNLatest                      	       0x100e7b534 std::__1::__function::__value_func<void ()>::operator()[abi:ue170006]() const + 68 (function.h:518)
13  RNLatest                      	       0x100e7b454 std::__1::function<void ()>::operator()() const + 24 (function.h:1169)
14  RNLatest                      	       0x1012e25e4 invocation function for block in (anonymous namespace)::ModuleNativeMethodCallInvoker::invokeAsync(std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char>> const&, std::__1::function<void ()>&&) + 44 (RCTTurboModuleManager.mm:129)
15  libdispatch.dylib             	       0x180170104 _dispatch_call_block_and_release + 24
16  libdispatch.dylib             	       0x180171978 _dispatch_client_callout + 16
17  libdispatch.dylib             	       0x180179b10 _dispatch_lane_serial_drain + 960
18  libdispatch.dylib             	       0x18017a688 _dispatch_lane_invoke + 388
19  libdispatch.dylib             	       0x180185a84 _dispatch_root_queue_drain_deferred_wlh + 276
20  libdispatch.dylib             	       0x1801850d0 _dispatch_workloop_worker_thread + 448
21  libsystem_pthread.dylib       	       0x1042cb814 _pthread_wqthread + 284
22  libsystem_pthread.dylib       	       0x1042ca5d4 start_wqthread + 8

Expected behavior

ShapeSource.getClusterLeaves() used to worked on old architecture.

Notes / preliminary analysis

No response

Additional links and references

rnmapbox-getclusterleaves-crash.mov
@villemuittari villemuittari added the bug 🪲 Something isn't working label Sep 10, 2024
@github-actions github-actions bot reopened this Sep 10, 2024
@villemuittari villemuittari changed the title [Bug]: ShapeSource.getClusterLeaves crash on iOS (NOBRIDE mode) [Bug]: ShapeSource.getClusterLeaves crash on iOS (NOBRIDGE mode) Sep 27, 2024
@gregoryalary
Copy link

gregoryalary commented Nov 5, 2024

This is happening to me as well.

Screen.Recording.2024-11-05.at.15.59.28.mp4

Here is the relevant crash report :

Thread 24 name:
Thread 24 Crashed:
0   libobjc.A.dylib               	0x000000018382605c objc_retain + 8 (:-1)
1   MhMap                         	0x0000000102aab504 -[RNMBXShapeSourceModule getClusterLeaves:featureJSON:number:offset:resolve:reject:] + 72 (RNMBXShapeSourceModule.mm:56)
2   CoreFoundation                	0x000000018b92d814 __invoking___ + 148 (:-1)
3   CoreFoundation                	0x000000018b92c860 -[NSInvocation invoke] + 428 (NSForwarding.m:3411)
4   CoreFoundation                	0x000000018b9a31dc -[NSInvocation invokeWithTarget:] + 64 (NSForwarding.m:3508)
5   MhMap                         	0x0000000102895254 invocation function for block in facebook::react::ObjCTurboModule::performMethodInvocation(facebook::jsi::Runtime&, bool, char const*, NSInvocation*, NSMutableArray*) + 120 (RCTTurboModule.mm:347)
6   MhMap                         	0x000000010289a978 facebook::react::ObjCTurboModule::performMethodInvocation(facebook::jsi::Runtime&, bool, char const*, NSInvocation*, NSMutableArray*)::$_2::operator()() const + 68 (RCTTurboModule.mm:380)
7   MhMap                         	0x000000010289a978 decltype(std::declval<facebook::react::ObjCTurboModule::performMethodInvocation(facebook::jsi::Runtime&, bool, char const*, NSInvocation*, NSMutableArray*)::$_2&>()()) std::__1::__invoke[abi:ue1700... + 68 (invoke.h:340)
8   MhMap                         	0x000000010289a978 void std::__1::__invoke_void_return_wrapper<void, true>::__call[abi:ue170006]<facebook::react::ObjCTurboModule::performMethodInvocation(facebook::jsi::Runtime&, bool, char const*, NSInvocation*, NS... + 68 (invoke.h:415)
9   MhMap                         	0x000000010289a978 std::__1::__function::__alloc_func<facebook::react::ObjCTurboModule::performMethodInvocation(facebook::jsi::Runtime&, bool, char const*, NSInvocation*, NSMutableArray*)::$_2, std::__1::allocator<fa... + 68 (function.h:193)
10  MhMap                         	0x000000010289a978 std::__1::__function::__func<facebook::react::ObjCTurboModule::performMethodInvocation(facebook::jsi::Runtime&, bool, char const*, NSInvocation*, NSMutableArray*)::$_2, std::__1::allocator<facebook... + 88 (function.h:364)
11  libdispatch.dylib             	0x000000019383513c _dispatch_call_block_and_release + 32 (init.c:1530)
12  libdispatch.dylib             	0x0000000193836dd4 _dispatch_client_callout + 20 (object.m:576)
13  libdispatch.dylib             	0x000000019383e400 _dispatch_lane_serial_drain + 748 (queue.c:3900)
14  libdispatch.dylib             	0x000000019383ef30 _dispatch_lane_invoke + 380 (queue.c:3991)
15  libdispatch.dylib             	0x0000000193849cb4 _dispatch_root_queue_drain_deferred_wlh + 288 (queue.c:6998)
16  libdispatch.dylib             	0x0000000193849528 _dispatch_workloop_worker_thread + 404 (queue.c:6592)
17  libsystem_pthread.dylib       	0x00000001e83e0934 _pthread_wqthread + 288 (pthread.c:2696)
18  libsystem_pthread.dylib       	0x00000001e83dd0cc start_wqthread + 8 (:-1)

@gregoryalary
Copy link

For information, I did a bit more debugging.

Here are the related line portion in the bridge :

// RNMBXShapeSourceModule.mm:56
RCT_EXPORT_METHOD(getClusterLeaves:(nonnull NSNumber *)viewRef featureJSON:(NSString *)featureJSON  number:(double)number offset:(double)offset resolve:(RCTPromiseResolveBlock)resolve reject:(RCTPromiseRejectBlock)reject)
{
 [self withShapeSource:viewRef block:^(RNMBXShapeSource *view) {
     [RNMBXShapeSourceViewManager getClusterLeavesWithShapeSource:view featureJSON:featureJSON number:number offset:offset resolver:resolve rejecter:reject];
 } reject:reject methodName:@"getClusterLeaves"];
}

I'm absolutely not competent in native iOS, here is the ChatGPT insight if that helps :

This crash log suggests an issue with memory management, particularly involving objc_retain in the context of the getClusterLeaves method. Here's a breakdown of the potential problem and solutions:

Problem Analysis

Crash Context
The crash happens in the objc_retain function, indicating an issue with object lifecycle management (e.g., an object being released prematurely or being incorrectly retained).

Error Trace
The issue is in the RNMBXShapeSourceModule method, specifically at [RNMBXShapeSourceModule getClusterLeaves:featureJSON:number:offset:resolve:reject:].
The block invocation in [self withShapeSource:viewRef block:] may not properly handle the lifecycle of the RNMBXShapeSource.

Underlying Causes
Null or Invalid Object: The view object passed to the block could be null or improperly managed.
Retain Cycle or Weak Reference: If the view object is weakly referenced but is deallocated before being used, the code might attempt to retain a dangling pointer.
TurboModule Interaction: TurboModule bindings might be passing incorrect or corrupted references to Objective-C objects, causing improper retain/release behavior.

@aderiushev
Copy link

aderiushev commented Nov 21, 2024

same here!
temporary disabled new arch (RCT_NEW_ARCH_ENABLED=0 pod install)

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
bug 🪲 Something isn't working
Projects
None yet
Development

No branches or pull requests

3 participants