Monotouch random crash in native code using UITableView - ios

Please help me, what am I doing wrong.
I use BubbleCell and BubbleElement from the examples.
For unknown reasons, the application sometimes crashes in native code.
I try out a little bit to correct the situation if I do not use DequeueReusableCell, and always create a new BubbleCell.
Also, the problem disappears if the constructor BubbleElement to pass a string constant as the caption, not the object field.
Stacktrace: http://pastebin.com/KAYzpHDk
The problem is deeper than it seems.
Your suggestion doesn't help, the application still crashes.
I made a separate project, leaving it a minimum of code necessary to show the problem.
The problem is somehow related to System.Json... maybe.
Here is an archive of the project:
https://dl.dropbox.com/u/63074515/BubbleNativeCrash.zip
The sequence of actions:
1) open the project
2) compile and run the emulator without debugging (configuration Debug|iPhoneSimulator)
3) click horse1 in the first view
4) in the list that appears, scroll whole items a few times up and down
5) in my case at this stage we crash... (stack trace: http://pastebin.com/KAYzpHDk)
If not immediately fail, you can return to the contact list, and vice versa, or even once a few times.
How quickly will fail depends on the number of messages in the chat.
Now the fun part.
The problem disappears completely (or I can not repeat it in a reasonable time) if in ChatViewController.cs comment the line 406 and uncomment line 407:
// string messageText = msg.MessageText ?? string.Empty;
string messageText = "Hello, World!!!";
That is, make a constant string passed into BubbleElement constructor.
After that, the problem does not repeat, I think.
I tried to leave everything as is, but remove the work with the Json - the problem disappears.
The problem in only when BubbleElement takes a string read from the JsonValue (IM\IMMessage.cs at line 36).
Even if there is, in IMMessage.cs, to set a constant string for MessageText - the problem disappears.
What am I doing wrong? :)
p.s. Sorry for my english... it powered by google translate.

You do not keep any reference to the managed instance you return from GetCell. As such the GC can (and will) collect the managed instance once the method returns while the native instance will continue to live (since it's referenced inside iOS).
This works fine when you use UITableViewCell since all the state is kept inside the native instance. However this is not the case if you inherit from it and add your own managed fields. In such cases you won't be able to access the managed state since it won't exists (the instance you'll have won't be the one you created).
The easy way to solve this is to keep a reference to the BubbleCell you create, e.g. in a list, so the GC won't collect them.
static List<BubbleCell> cell_cache = new List<BubbleCell> ();
public override UITableViewCell GetCell(UITableView tableView)
{
var cell = tableView.DequeueReusableCell(isLeft ? BubbleCell.KeyLeft : BubbleCell.KeyRight) as BubbleCell;
if (cell == null) {
cell = new BubbleCell(isLeft);
cells_cache.Add (cell);
}
cell.Update(Caption);
return cell;
}
Do not forget to clear the list once the cells are not needed anymore (e.g. when closing the UITableView).
UPDATE FROM EXTRA INFORMATION
The cell_cache must be static or it won't help (my mistake, fixed above) to keep references alive. But, as you found out, it's not the fix for this case (source shows the cells can be reused).
OTOH the issue is not related to using JSON. Using the same string messages (e.g. from an array) results in the same crash. That's a bit weird and I'll investgate this further...
FINAL UPDATE
It turns out the calls to CreateResizableImage can hit an iOS bug. The workaround is to use the older StretchableImage API. The issue was (re)submitted to Apple.

Related

What can cause Exc_Bad_Access on first function call in first VC?

Have app in App Store running fine. New version adds a bunch of calculations and minimal new interface. During dev and testing, runs (seemingly) perfectly in Simulator on all devices. Started testing on physical devices, and getting Bad_Access 1 or 2. I read every link on those I could find, and realize it's a difficult error. Using Swift, so no specifically dereferenced pointers of the type mentioned in most links.
Refactored code in many ways over last week. Eventually got it as simple as possible, posted here. My AppDelegate.swift is as basic as possible with no extra stuff or app-specific processing/initialization. The storyboard entry point is a collection view controller of type MainCollectionViewController. In the viewDidLoad method of that controller, the very first statement is a function call to getDrawingInstructions() with parameters. Using default parameter values, and the first two parameters are structs full of CGFloat, Bool, and UIColor.
As you can see, it's crashing as it gets to that func before even entering the func and executing the func's code. The first func call from viewDidLoad of the initial vc.
I've cleaned the build folder and restarted XCode, I'm Swift 5 compliant, I've updated to XCode 11 GM, I've turned on zombies and have no console message. Looking for suggestions on what to try next, 'cause I'm stumped on how I could get screwed up addresses before (seemingly) executing any app-specific code. And remember, this part runs in Simulator and is unchanged from the prior version that ran fine on all devices.
(In different iterations of code, Zombies would yield a console message like objc[7801]: Class _NSZombie_FBSXPCMessage is implemented in both ?? (0x162585890) and ?? (0x162576dd0). One of the two will be used. Which one is undefined. That gives me no clue where to look, especially with the ??.)
Thank you for any insights.
Edit: Per suggestions, removed parameters from function (into struct), but the result was the same as before.
Edit: Removed first argument to the call, a large struct, and got same result. Also got console message: objc[8038]: Class _NSZombie__NSCallStackArray is implemented in both ?? (0x163481590) and ?? (0x163480750). One of the two will be used. Which one is undefined. Not sure how to use this message.
Edit: If the called func is a stub that returns immediately, it is safely processed. So...it's not even entered with a trace if there's code, but it processes if there's an immediate return. Confusing.
Edit: Reconfigured yet again to remove all arguments to the function and just initialize them with default values in the function. That crashes with the same error at the same place, the entry point to the function, without reaching any of the initialization statements in the function. So the first statement in viewDidLoad of the primary VC calls a func with no arguments and crashes with a dereferenced pointer or similar memory error without stepping into that func. I'm not sure how to make that even intentionally happen.

Method mysteriously exits execution in the middle to resume the rest of the program?

So i've got this code that tries to find an unused upload name, using the user's email and a number at its end. It does this with a list of uploaded objects we've already collected, the user's email.(upload_name), and the
current number that might be open (it is incremented when a match is found).
The list is not sorted, and it's pretty tricky to sort for a few reasons, so I'm having the method read through the list again if it reaches the end and the upload_number has changed.
- (NSString*)findUnusedUploadNameWithPreviousUploads:(NSMutableArray*)objects withBaseUploadName:(NSString*)upload_name {
previous_upload_number = upload_number;
for (NSString *key in objects) {
// the component of the object name before the first / is the upload name.
NSLog([key componentsSeparatedByString:#"/"][1]);
if ([[key componentsSeparatedByString:#"/"][1]
isEqualToString:([NSString stringWithFormat:#"%#_%ld", S3KeyUploadName1, upload_number])]) {
upload_number++;
NSLog([NSString stringWithFormat:#"upload name: %#_%ld", S3KeyUploadName1, upload_number]);
}
NSLog(#"pang");
}
NSLog(#"ping");
if (previous_upload_number == upload_number) {
return [NSString stringWithFormat:#"%#%ld", upload_name, upload_number];
}
return [self findUnusedUploadNameWithPreviousUploads:objects withBaseUploadName:upload_name];
}
The problem is, the program never reads the "ping". it just leaves the method after the first for loop is done.
Edit: No the NSlogs are fine, you can do simple string OR StringWithFormat.
Edit: Don't mind the unnecessary use of recursion, I did this because the simple way was having the same problem and i wanted to see if a different (albeit unnecessarily recursive) way would share that problem. It does.
Edit: I set a breakpoint in the for loop, and I set a break point at the "ping". It does not reach the ping. It completes the for loop and the ditches the whole thing.
Edit: Please try to help me figure out why it's exiting the the method immediately after the for loop. I'm aware this is stylistically meh, and I promise I'll make it shiny and spotless when it works. =]
Edit: to be clear, the method DOES exit. it does so early I know this because the rest of the program following this method (which is not threaded such that it wouldn't have to wait for it) runs AFTER this for loop, consistently.
There are a couple of possible explanations for the described behavior:
The method never exits. For some reason it blocks or performs infinitely somewhere in the loop. Make sure this is not the case by setting a breakpoint after the place where the message is called (i.e. the place to where it should return).
The method, or some method it calls, throws an exception. While seldom and unsupported in productive Cocoa code it could be some misbehaving 3rd party library or just a simple programmer error, where Cocoa actually does throw. Make sure this does not happen by setting an exception breakpoint in Xcode.
Undefined behavior. This is, sadly, part of official C and heavily exploited by modern compilers. It basically means: Anything can happen, if there's something in your code, where the standard says that the behavior is not defined. One example would be accessing a deallocated object. Another fine reason of undefined behavior can be threads accessing common data in an unsynchronized way.
Other than exceptions there's no explanation for a method to "exit early". If you still can't find the reason I suggest you invest some time to learn the debugger. Single stepping, like Aris suggested, might be a way to find out what's going on.

message sent to deallocated instance error

Im constantly being given an error that reads *** -[NSKeyValueObservance retain]: message sent to deallocated instance 0x86c75f10. I have tried running the Zombies template and here is the screenshot of what it provides.
It points to a managedObject, and I'm having trouble figuring out where the object is being deallocated. Here is the block of code that the compiler takes me to after each crash.
- (void)setIsFavourite:(BOOL)isFavourite shouldPostToAnalytics:(BOOL)shouldPostToAnalytics;
{
// check whether we need to generate preferences objects just in time
if(!self.preferences && !self.series.preferences /*&& isFavourite*/)
{
if(self.series)
{
[self.series addPreferencesObject];
}
else
{
[self addPreferencesObject];
}
}
//Crash In here
self.preferences.isFavourite = #(isFavourite);
self.series.preferences.isFavourite = #(isFavourite);
EDIT: If you need to see a larger size of the image here is a larger resolution link.
OK, I hit something similar and found a way to debug this kind of issue with NSKeyValueObservance. To debug, do the following:
In Xcode, open the "Breakpoint Navigator".
Add a new symbolic breakpoint with:
-[NSKeyValueObservance _initWithObserver:property:options:context:originalObservable:]
To that breakpoint, add an action and set it to "Debugger Command".
Set the following command: expr (void)NSLog(#"observer <0x%p>: %# <%p>, property: %#", $arg1, (id)NSStringFromClass((id)[(id)$arg3 class]), $arg3, (id)$arg4)
Click the "Automatically continue after evaluating expression".
Now you can run your application and take the steps necessary to reproduce your crash. And yes, you'll want NSZombies enabled. Note: it's going to run slow and you're going to get a ton of debug output, but just be patient. It'll get there eventually.
When you hit the crash when trying to message a deallocated NSKeyValueObservance, you'll be presented with the address of the original object. Highlight the address and hit cmd-e to enter the text in the search buffer. Then hit cmd-g find the next occurrence of the string in the debugger output. You're going to potentially find the address a couple of times, so look for the address that follows the observer <0x?????> output. The output on that line should tell you what object is being observed and for which property.
In my case, when I figured this all out, it turned out that I was observing a synthesized property that depended on an object in array and during a certain operation, the order of the objects in the array changed without doing the correct KVO notifications, and that caused my crash.
Are you using manual reference counting? If so, why? Convert your app to ARC. Manual reference counting is painful at best, and ARC is much better.
I am an experienced iOS and Mac OS developer and can do either, but I far prefer ARC. It's much less fussy and error-prone.
There is a feature built into Xcode that will convert your project to ARC for you. You might have to do some cleanup afterwords, but it's worth it.
If you do that your problem will likely go away.
As to the specifics, your screenshot is far too small to be able to read it. You will need to post a full-sized image if you want somebody to try to figure out what's going on.
However, in broad terms it sounds to me like you have an autorelease bug.
in manual reference counted code, lots of system method return objects that are "autoreleased." That means that when you receive them their retain count is positive (usually 1) so they stick around. However, they have been added to the "autorelease pool," which means that they are going to be released on the next pass through the event loop if nobody retains them first.
When you receive an autoreleased object you should either accept that it will be released once your current method returns, or retain it.
If you are trying to write Core Data code using manual reference counting and don't understand this then you are setting yourself up for failure.
Core Data is pretty complex, and you should have a solid understanding of Cocoa memory management before attempting to write a program that uses it, especially if you're using manual reference counting.

Memory leak when accessing GCController's gamepad

I'm adding support for the iOS GameController Framework to a cross-platform input library, and running into some trouble with a memory leak. Here's a simplified version of the troublesome code:
void InputSystem::UpdateControllerState(size_t nControllerIndex)
{
Controller& c = mController[nControllerIndex];
GCController* gc = c.mGameController;
GCExtendedGamepad* gp = [gc extendedGamepad]; // ***
c.mState[kIOSThumbLX] = gp.leftThumbstick.xAxis.value;
c.mState[kIOSThumbLY] = gp.leftThumbstick.yAxis.value;
// ... etc
}
The memory leak seems to be caused by the line marked with ***. I can comment out the lines reading values from the controller and memory is still leaked (under certain circumstances; this could be the key). I have tried adding [gc release] at the end of the function but that doesn’t seem to have any impact. This doesn’t really surprise me since I am not retaining the pointer so I should not need to release it.
The interesting thing is that this code works fine in some conditions. As I was working on the implementation, I set up a basic test that worked just fine. The problem only showed up when I tried hooking up the existing cross-platform unit tests for this library. Here is a bit more detail on the two setups:
(No leak) I set up my InputSystem in applicationDidFinishLaunching,
along with a displayLink to call a function each frame. This
function would call InputSystem code that would call the above
UpdateControllerState method and then print out any new input (button
presses, etc) detected. This setup worked fine; I saw the input from
the controller that I expected, and memory use stayed nice and flat.
(Memory leak) In order to test more rigourously, I hooked in the
cross-platform InputSystem unit tests. These tests include an
interactive mode that continuously loops to poll and print out new
inputs. This doesn’t give iOS the chance to receive input and update
the GCController, so I created a new thread in
applicationDidFinishLaunching to call into the tests. This worked
for getting input, but memory use was steadily increasing. As I
mentioned above, commenting out parts of the new code helped me
narrow it down to the highlighted line above.
I don't understand why these two setups behave differently in this way. Any help understanding this problem would be appreciated. I don't know at this point if this is a problem with how I am using the GCController class, or something I am not understanding about iOS memory in general. Thanks!
EDIT: I just tried storing the pointer to the extendedGamepad in my Controller object, so I could read from it directly and remove the line marked with ***. In this case, however, memory is leaked when I include the lines that read the values from the buttons or thumbsticks.

How to prevent "expression not used" warning with iPad tableView row selection code

I have an iPad app that uses a TabBar setup with a custom SplitViewController and allows selections from a standard left-hand table menu (controlled by a ViewController) to change the contents to the right (landscape only) controlled by a DetailViewController. This runs fine and is in the App Store. I am in the process of updating it to ARC and iOS7 using Xcode 5.1.1 and the conversion has worked and the program seems to run OK in the simulator. However I am getting a warning (which I never had before) in the didSelectRowAtIndexPath method of each of the Views. For example in a class, StylesViewController:
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath
{
[(StylesDetailViewController *)detailViewController initWithIndexPath:indexPath];
[((StylesDetailViewController *)detailViewController).masterPopover dismissPopoverAnimated:YES];
}
The warning is for the first line (the second is probably not used as the app only supports landscape) and is "expression result unused". The line is necessary, the program does not crash, so I assume that this warning could be ignored. However, knowing Apple, I would prefer not to submit an app with warnings for review. Can anyone suggest a harmless statment to 'use' the 'result of the expression' and hence silence the compiler?
Sending a message with the string 'init' in it's selector to an object that is not created on the same line of code is an important anti-pattern in iOS/Objective-C. The details are not entirely necessary, but the important (and relevant) bit of info is that technically an 'init' method returns an id, which is actually allowed to be a different object than the one that the message was sent to!
Your warning is simply telling you that you are not using that object. The calling of an init method outside of it's alloc line may have other unwieldy implications.
One caveat to this answer: The specifics will certainly depend on your implementations of the method in question, and posting that method (at least it's signature) will help. However, even if you are using that method to simply prime the object in a way that can/should happen over and over and apart from the alloc step, I highly encourage you to avoid the use of the string init in the name... I like to use loadWith... or something similar.

Resources