18 Replies
      Latest reply on Dec 26, 2019 1:29 PM by pjbrockmann
      AjohnB Level 1 Level 1 (0 points)

        On iPadOS and iOS13 on a call to visibleCells on a UITableView ( on code which works fine on iOS9 to iOS12 ) I get an exception with the message

         

        Attempted to access the table view's visibleCells while they were in the process of being updated, which is not allowed.

         

        Searching on the web gives me no hits for this exact message, so I suspect the message itself is new or changed.

         

         

        Now the general gist of the message is fairly clear, but the specifics and but how one is expected to avoid this in a multithreaded application are far from clear.

         

        For example ...what sort of changes qualify,

         

        For example ... the user may rotate the device, a redisplay may be caused by incoming data. Are these now to be expected to crash your application?

         

        Maybe there is a new threading model / usage requirement that I don't know about,  yet.

         

        Synchronising on the UITableView itself doesn't help.

         

        What am I missing

         

         

        And Yes, I can avoid the problem by catching the error and backing off until I succeed.  But the hit on performance is eyewatering.  And it's horrible.

        • Re: Exception… Attempted to access the table view's visibleCells while they were in the process of being updated, which is not allowed
          AjohnB Level 1 Level 1 (0 points)

          UITableView beginUpdates / end updates is not documented as a locking mechanism but one of grouping updates for smooth animation.

           

          Despite that, it appears that it does some locking.

           

          Although my code is making no changes, accessing the value of visibleCells, wrapping the code in a beginUpdates / endUpdates pair allows it to complete without the "Attempted to access the table view's visibleCells while they were in the process of being updated" error.

          • Re: Exception… Attempted to access the table view's visibleCells while they were in the process of being updated, which is not allowed
            tylerf Apple Staff Apple Staff (55 points)

            This is a new exception in iOS 13 that UITableView will raise in order to prevent and proactively alert you of a situation that would previously cause undefined behavior and a variety of strange, seemingly unrelated, and hard-to-debug issues (including crashes).

             

            What is happening here is that UITableView is in the middle of asking its dataSource to return a cell for each visible row and is configuring the properties of the returned cells so they can be displayed. And in the middle of this updating -- most likely inside a callback from the table view itself about a specific row such as tableView(_:cellForRowAt:) tableView(_:canEditRowAt:), etc -- your code is asking the table view to return the visibleCells. This is obviously problematic, because UITableView is right in the middle of preparing those cells, so it cannot possibly return a meaningful answer.

             

            The fix for this is to look at where you are calling visibleCells in the backtrace when this exception is raised, and then do one of two things:

             

            Option 1: Move the usage of visibleCells to a better place, so that you aren't asking for the visibleCells from some place that is called during the process of creating/configuring/updating those same cells. A great place to ask for the visible cells is after the table view lays out, so for example if the table view is the view of a view controller you can use viewDidLayoutSubviews(), or in a subclass of UITableView do it after calling super.layoutSubviews().

             

            Option 2: Depending on what you're actually trying to do, you might be able to skip using visibleCells altogether. For example, you might be able to leverage the callbacks tableView(_:willDisplay:forRowAt:) and tableView(_:didEndDisplaying:forRowAt:) to track when cells are visible instead.

             

            Another place you may hit this is if you are using Key-Value Observation to observe the table view's contentOffset directly, and are asking for the visibleCells every time that changes. You should use the UIScrollView delegate method scrollViewDidScroll(_:) instead, or possibly a different approach altogether (such as the suggestion in Option 2 above).

             

            If you are hitting this exception and you think you are requesting the visibleCells from a location that should be valid/allowed, please share the backtrace when you hit this exception and details about what you're trying to do.

              • Re: Exception… Attempted to access the table view's visibleCells while they were in the process of being updated, which is not allowed
                palmin Level 1 Level 1 (10 points)

                I'm getting this is a consequence of making a UITextField inside one of the cells first responder in viewDidAppear.

                Looking at the stack trace it seems to be UIKit that calls visibleCells.

                 

                Thread 1 Queue : com.apple.main-thread (serial)
                #0 0x000000010e8eaf27 in objc_exception_throw ()
                #1 0x000000010f55d8e8 in +[NSException raise:format:arguments:] ()
                #2 0x000000010e253c29 in -[NSAssertionHandler handleFailureInMethod:object:file:lineNumber:description:] ()
                #3 0x0000000113f863a8 in -[UITableView _visibleCellsUsingPresentationValues:] ()
                #4 0x0000000113f93cc4 in -[UITableView _scrollFirstResponderCellToVisible:] ()
                #5 0x0000000113fab37c in -[UITableView _adjustForAutomaticKeyboardInfo:animated:lastAdjustment:] ()
                #6 0x00000001135af9fb in -[UIAutoRespondingScrollViewControllerKeyboardSupport _adjustScrollViewForKeyboardInfo:] ()
                #7 0x00000001135afa77 in -[UIAutoRespondingScrollViewControllerKeyboardSupport _keyboardWillShow:] ()
                #8 0x000000010f4886fc in __CFNOTIFICATIONCENTER_IS_CALLING_OUT_TO_AN_OBSERVER__ ()
                #9 0x000000010f487b79 in _CFXRegistrationPost1 ()
                #10 0x000000010f4878bf in ___CFXNotificationPost_block_invoke ()
                #11 0x000000010f584973 in -[_CFXNotificationRegistrar find:object:observer:enumerator:] ()
                #12 0x000000010f487226 in _CFXNotificationPost ()
                #13 0x000000010e2b5108 in -[NSNotificationCenter postNotificationName:object:userInfo:] ()
                #14 0x0000000113b8b044 in __59-[UIInputWindowController postStartNotifications:withInfo:]_block_invoke ()
                #15 0x0000000113b8ab94 in -[UIInputWindowController postStartNotifications:withInfo:] ()
                #16 0x0000000113b8e03e in __77-[UIInputWindowController moveFromPlacement:toPlacement:starting:completion:]_block_invoke.839 ()
                #17 0x00000001142674a7 in +[UIView(UIViewAnimationWithBlocks) _setupAnimationWithDuration:delay:view:options:factory:animations:start:animationStateGenerator:completion:] ()
                #18 0x00000001142678f7 in +[UIView(UIViewAnimationWithBlocks) _animateWithDuration:delay:options:animations:start:completion:] ()
                #19 0x0000000113b8daae in -[UIInputWindowController moveFromPlacement:toPlacement:starting:completion:] ()
                #20 0x0000000113b9324d in -[UIInputWindowController setInputViewSet:] ()
                #21 0x0000000113b8d122 in -[UIInputWindowController performOperations:withAnimationStyle:] ()
                #22 0x0000000113899b12 in -[UIInputResponderController setKeyWindowSceneInputViews:animationStyle:] ()
                #23 0x000000011389926e in -[UIInputResponderController setInputViews:animationStyle:] ()
                #24 0x000000011389a392 in -[UIInputResponderController setInputViews:animated:] ()
                #25 0x000000011389a3f9 in -[UIInputResponderController setInputViews:] ()
                #26 0x0000000113897e50 in -[UIInputResponderController _reloadInputViewsForKeyWindowSceneResponder:] ()
                #27 0x0000000113897163 in -[UIInputResponderController _reloadInputViewsForResponder:] ()
                #28 0x0000000113de334d in -[UIResponder(UIResponderInputViewAdditions) reloadInputViews] ()
                #29 0x0000000113ddea3c in -[UIResponder becomeFirstResponder] ()
                #30 0x000000011425d3ea in -[UIView(Hierarchy) becomeFirstResponder] ()
                #31 0x0000000114088a50 in -[UITextField becomeFirstResponder] ()
                #32 0x000000011425d42c in -[UIView(Hierarchy) deferredBecomeFirstResponder] ()
                #33 0x000000011425d4d0 in -[UIView(Hierarchy) _promoteSelfOrDescendantToFirstResponderIfNecessary] ()
                #34 0x000000011425d947 in __45-[UIView(Hierarchy) _postMovedFromSuperview:]_block_invoke ()
                #35 0x000000011425d7d4 in -[UIView(Hierarchy) _postMovedFromSuperview:] ()
                #36 0x000000011426cd8a in -[UIView(Internal) _addSubview:positioned:relativeTo:] ()
                #37 0x0000000113f7329e in -[UITableView _addSubview:positioned:relativeTo:] ()
                #38 0x0000000114201aea in -[UIScrollView _addContentSubview:atBack:] ()
                #39 0x0000000113f72fec in -[UITableView _addContentSubview:atBack:] ()
                #40 0x0000000113f96096 in __53-[UITableView _configureCellForDisplay:forIndexPath:]_block_invoke ()
                #41 0x0000000114265d65 in +[UIView(Animation) performWithoutAnimation:] ()
                #42 0x0000000113f95816 in -[UITableView _configureCellForDisplay:forIndexPath:] ()
                #43 0x0000000113fa7986 in -[UITableView _createPreparedCellForGlobalRow:withIndexPath:willDisplay:] ()
                #44 0x0000000113fa7e0b in -[UITableView _createPreparedCellForGlobalRow:willDisplay:] ()
                #45 0x0000000113f7145d in -[UITableView _updateVisibleCellsNow:] ()
                #46 0x0000000113f90f63 in -[UITableView layoutSubviews] ()
                #47 0x0000000114273821 in -[UIView(CALayerDelegate) layoutSublayersOfLayer:] ()
                #48 0x0000000110b57b11 in -[CALayer layoutSublayers] ()
                #49 0x0000000110b5c51b in CA::Layer::layout_if_needed(CA::Transaction*) ()
                #50 0x0000000110b68c44 in CA::Layer::layout_and_display_if_needed(CA::Transaction*) ()
                #51 0x0000000110ab1a23 in CA::Context::commit_transaction(CA::Transaction*, double) ()
                #52 0x0000000110ae6ecd in CA::Transaction::commit() ()
                #53 0x0000000113ddaf20 in _afterCACommitHandler ()
                #54 0x000000010f4c07c7 in __CFRUNLOOP_IS_CALLING_OUT_TO_AN_OBSERVER_CALLBACK_FUNCTION__ ()
                #55 0x000000010f4bb26e in __CFRunLoopDoObservers ()
                #56 0x000000010f4bb8ea in __CFRunLoopRun ()
                #57 0x000000010f4bafd6 in CFRunLoopRunSpecific ()
                #58 0x00000001191b99f8 in GSEventRunModal ()
                #59 0x0000000113db1722 in UIApplicationMain ()
                #60 0x000000010d6ccb6b in main at /Users/ander/opgaver/ShellFish/ShellFish/AppDelegate.swift:19
                #61 0x0000000111f3c6ad in start ()
                • Re: Exception… Attempted to access the table view's visibleCells while they were in the process of being updated, which is not allowed
                  AjohnB Level 1 Level 1 (0 points)

                  Thanks enormously.

                   

                  I am indeed watching changes in contentOffset.  The pointer to scrollViewDidScroll() makes a lot of sense.

                  • Re: Exception… Attempted to access the table view's visibleCells while they were in the process of being updated, which is not allowed
                    powerqian Level 1 Level 1 (0 points)

                    Unfortunately my case is different. I am not calling any visibleCells function. Instead, what I do is just having a UITextView inside a UITableViewCell, with top, bottom, leading and trailing constraint set to the cell, and scrollable disabled so that the cell is a self-resizing. To replicate the exception, I just need to update the text of the text view through data source update. Here's the sample code:

                     

                    import UIKit
                    
                    class DemoCell: UITableViewCell {
                        @IBOutlet var demoTextView: UITextView!
                    }
                    
                    let demoText = "This is a demo text"
                    let demoText2 = "This is a different demo text"
                    
                    class ViewController: UIViewController {
                    
                        @IBOutlet var tableView: UITableView!
                        var demoDatas = Array(repeating: demoText, count: 10)
                        
                        override func viewDidLoad() {
                            super.viewDidLoad()
                            tableView.dataSource = self
                            
                            DispatchQueue.main.asyncAfter(deadline: .now() + 2) {
                                self.demoDatas = Array(repeating: demoText2, count: 10)
                                self.tableView.reloadData()
                            }
                        }
                    }
                    
                    extension ViewController: UITableViewDataSource {
                        func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
                            return demoDatas.count
                        }
                    
                        func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
                            let cell = tableView.dequeueReusableCell(withIdentifier: "DemoCellIdentifier", for: indexPath) as! DemoCell
                            cell.demoTextView.text = demoDatas[indexPath.row]
                            return cell
                        }
                    }
                    
                    

                    I believe this kind of setup is quite common in messaging app. Any idea for the work around?

                    • Re: Exception… Attempted to access the table view's visibleCells while they were in the process of being updated, which is not allowed
                      Jaelee008 Level 1 Level 1 (0 points)

                      Hey tylerf!

                      Been experiencing crash all day with exception. Please help

                       

                      2019-10-03 13:51:21.910027-0400 Proteus[69427:3583092] *** Terminating app due to uncaught exception 'NSInternalInconsistencyException', reason: 'Requested the number of rows for section (1) which is out of bounds.'

                      *** First throw call stack:

                      (

                          0   CoreFoundation                      0x00007fff23b98bde __exceptionPreprocess + 350

                          1   libobjc.A.dylib                     0x00007fff503b5b20 objc_exception_throw + 48

                          2   CoreFoundation                      0x00007fff23b98958 +[NSException raise:format:arguments:] + 88

                          3   Foundation                          0x00007fff255eb7be -[NSAssertionHandler handleFailureInFunction:file:lineNumber:description:] + 166

                          4   UIKitCore                           0x00007fff47381d87 -[UITableViewRowData numberOfRowsInSection:] + 436

                          5   Proteus                             0x000000010952959f $s7Proteus26ElasticSearchTableViewCellC15prepareForReuseyyF + 127

                          6   Proteus                             0x0000000109529658 $s7Proteus26ElasticSearchTableViewCellC15prepareForReuseyyFTo + 24

                          7   UIKitCore                           0x00007fff47346d68 -[UITableView _dequeueReusableViewOfType:withIdentifier:] + 181

                          8   UIKitCore                           0x00007fff473473fa -[UITableView _dequeueReusableCellWithIdentifier:forIndexPath:usingPresentationValues:] + 153

                          9   UIKitCore                           0x00007fff4734732d -[UITableView dequeueReusableCellWithIdentifier:forIndexPath:] + 91

                          10  Proteus                             0x0000000109331cac $s7Proteus17ElasticControllerC9tableView_12cellForRowAtSo07UITableE4CellCSo0jE0C_10Foundation9IndexPathVtF + 2700

                          11  Proteus                             0x0000000109332100 $s7Proteus17ElasticControllerC9tableView_12cellForRowAtSo07UITableE4CellCSo0jE0C_10Foundation9IndexPathVtFTo + 128

                          12  UIKitCore                           0x00007fff47360e4b -[UITableView _createPreparedCellForGlobalRow:withIndexPath:willDisplay:] + 787

                          13  UIKitCore                           0x00007fff4732a3c6 -[UITableView _updateVisibleCellsNow:] + 3081

                          14  UIKitCore                           0x00007fff4734a318 -[UITableView layoutSubviews] + 163

                          15  UIKitCore                           0x00007fff47636722 -[UIView(CALayerDelegate) layoutSublayersOfLayer:] + 2478

                          16  QuartzCore                          0x00007fff2b030ef9 -[CALayer layoutSublayers] + 255

                          17  QuartzCore                          0x00007fff2b0358ff _ZN2CA5Layer16layout_if_neededEPNS_11TransactionE + 517

                          18  QuartzCore                          0x00007fff2b041fe4 _ZN2CA5Layer28layout_and_display_if_neededEPNS_11TransactionE + 80

                          19  QuartzCore                          0x00007fff2af8a4a8 _ZN2CA7Context18commit_transactionEPNS_11TransactionEd + 324

                          20  QuartzCore                          0x00007fff2afbfab3 _ZN2CA11Transaction6commitEv + 643

                          21  QuartzCore                          0x00007fff2afc041a _ZN2CA11Transaction17observer_callbackEP19__CFRunLoopObservermPv + 76

                          22  CoreFoundation                      0x00007fff23afaeb7 __CFRUNLOOP_IS_CALLING_OUT_TO_AN_OBSERVER_CALLBACK_FUNCTION__ + 23

                          23  CoreFoundation                      0x00007fff23af594e __CFRunLoopDoObservers + 430

                          24  CoreFoundation                      0x00007fff23af5fca __CFRunLoopRun + 1514

                          25  CoreFoundation                      0x00007fff23af56b6 CFRunLoopRunSpecific + 438

                          26  GraphicsServices                    0x00007fff3815cbb0 GSEventRunModal + 65

                          27  UIKitCore                           0x00007fff47162a67 UIApplicationMain + 1621

                          28  Proteus                             0x00000001092f542a main + 58

                          29  libdyld.dylib                       0x00007fff5123bcf5 start + 1

                      )

                      libc++abi.dylib: terminating with uncaught exception of type NSException

                      (lldb)

                      • Re: Exception… Attempted to access the table view's visibleCells while they were in the process of being updated, which is not allowed
                        djs-code2 Level 1 Level 1 (0 points)

                        What're the guidelines for dealing with this in a tabbed application w/ an active NSFetchedResultsController?  We have a Notifications tab that periodically fetches data in the background, which can trigger the tab's NSFetchedResultsController even when that tab isn't currently selected.  It seems that UITableView.beginUpdates(), invoked via my NSFetchedResultsControllerDelegate callback, throws this warning.

                        • Re: Exception… Attempted to access the table view's visibleCells while they were in the process of being updated, which is not allowed
                          pjbrockmann Level 1 Level 1 (0 points)

                          I had this problem in two places.

                           

                          I used the suggestion in the console message to create a symbolic break on UITableViewAlertForCellForRowAtIndexPathAccessDuringUpdate and then clicked on the white bolded references in the (backtrace window, which was the breakpoint navigator before running the simulator). These took me right to the line in my code with the issue.

                           

                          I missed this 'click on the white bolded line in backtrace' previous attempts at debugging this and like this issue.

                           

                          In one case, in the viewForHeaderInSection func, I had some UI element based on the presence of a row for that section or not. I changed that to some other attribute not based on the related formed/forming cell.

                           

                          In the other case, I had similarly put a condition in the canEditRowAt that would test if the `tableView.cellForRow(at:)` was of a certain class. Unhappy form. Replaced it with a condition based on a non-UI element and issue... gone.

                           

                          Nowhere did I use visibleCells explicitly, but I did analyze a visible cell. The point: don't be so literal in interpreting a console message.

                        • Re: Exception… Attempted to access the table view's visibleCells while they were in the process of being updated, which is not allowed
                          Dougie Level 1 Level 1 (0 points)

                          I've been experiencing this error recently too on a UITableView.

                          Interestingly I was only receiving the error on an actual device, no issues whilst using Simulator.

                           

                          My UITableView uses CellForRowAt to display the cells depending on the section.

                          After trial and error, the offending line appeared to be:

                          cell.infoTextView.isEditable = tableView.isEditing

                           

                          Changing to the following line has resolved the crash (at least for me):

                          cell.infoTextView.isUserInteractionEnabled = tableView.isEditing
                          • Re: Exception… Attempted to access the table view's visibleCells while they were in the process of being updated, which is not allowed
                            sndive Level 1 Level 1 (0 points)

                            (lldb) bt

                            * thread #1, queue = 'com.apple.main-thread', stop reason = breakpoint 7.1

                                frame #0: 0x00000001ab675140 UIKitCore`UITableViewAlertForLayoutOutsideViewHierarchy

                                frame #1: 0x00000001ab674100 UIKitCore`-[UITableView _updateVisibleCellsNow:] + 284

                                frame #2: 0x00000001ab687ac0 UIKitCore`-[UITableView _visibleCellsUsingPresentationValues:] + 456

                                frame #3: 0x00000001ab67c294 UIKitCore`-[UITableView _updateAnimationDidStopWithOldVisibleViews:finished:context:] + 1608

                                frame #4: 0x00000001ab93eb28 UIKitCore`-[UIViewAnimationBlockDelegate _didEndBlockAnimation:finished:context:] + 588

                                frame #5: 0x00000001ab913034 UIKitCore`-[UIViewAnimationState sendDelegateAnimationDidStop:finished:] + 244

                                frame #6: 0x00000001ab91353c UIKitCore`-[UIViewAnimationState animationDidStop:finished:] + 240

                                frame #7: 0x00000001adefe28c QuartzCore`CA::Layer::run_animation_callbacks(void*) + 276

                                frame #8: 0x0000000101e4ac88 libdispatch.dylib`_dispatch_client_callout + 16

                                frame #9: 0x0000000101e58ce8 libdispatch.dylib`_dispatch_main_queue_callback_4CF + 1316

                                frame #10: 0x00000001a74697d4 CoreFoundation`__CFRUNLOOP_IS_SERVICING_THE_MAIN_DISPATCH_QUEUE__ + 12

                                frame #11: 0x00000001a74647c8 CoreFoundation`__CFRunLoopRun + 2004

                                frame #12: 0x00000001a7463ccc CoreFoundation`CFRunLoopRunSpecific + 464

                                frame #13: 0x00000001b1765328 GraphicsServices`GSEventRunModal + 104

                                frame #14: 0x00000001ab4c7c80 UIKitCore`UIApplicationMain + 1936

                              * frame #15: 0x000000010051c168 belweb`main at AppDelegate.swift:21:7

                                frame #16: 0x00000001a72ee424 libdyld.dylib`start + 4

                             

                            no my code in a backtrace that has visibleCells . 13 beta 6