How iOS handles memory corruption from low level C/ Objective-C code? - ios

I have an iOS app that uses some legacy low level memory manipulation code using pointers. I'm debugging an issue where multiple threads are causing multiple copies of these code to be executed on global variables simultaneously and cause memory corruption by writing invalid length or overwriting data.
The effect is that the length of the buffer below may change. I've seen iOS throw EXC_BAD_ACCESS or EXC_BREAKPOINT as a result of these calls.
My question is - would iOS always throw exceptions when I use memcpy incorrectly, or will it complain only when I write outside my allowed memory?
In other words, is my code free to corrupt my memory and create invisible issues, without causing exceptions, as long as it does not step outside allocated memory or access deallocated memory?
NSData* buffer = ...
Byte *array = (Byte*)malloc(buffer.length);
memcpy(array, buffer.bytes, buffer.length);

The last time I checked or had such an issue it would only complain when outside your own allowed memory. In my situation I was writing over whatever objects followed the address I was trying to write to. The result was what seemed a random crash on objects as unrelated as even NSString.
My mistake was something like the following:
MyStructure *myStructure = malloc(sizeof(myStructure)); // Incorrect
MyStructure *myStructure = malloc(sizeof(MyStructure)); // Fixed
A simple autocomplete error which led to days long hunting of this bug. MyStructure was a fairly big one so accessing some property (both read and write) would in this case simply overflow and read/write through whatever was after it. It eventually randomly crash; sometimes bad access, other times just some random exception on a random object.

Related

Does dart reuse memory for previously used instances?

It is hard to find a good heading for this, but i think my problem comes clear if i post a small code snipped:
SomeObject instance = SomeObject(importantParameter);
// -> "instance" is now a reference to the instance somewhere in RAM
instance = SomeObject(anotherImportantParameter);
// -> "instance" is now a reference to a different instance somewhere in RAM
My question is now, is the used RAM that was allocated at the first construction reused at the second construction? Or is the RAM of the first instance marked as unused for the garbage collector and the second construction is done with a completely new instance with a different portion of RAM?
If the first is true, what with this:
while(true) {
final SomeObject instance = SomeObject(importantParameter);
}
Will then, each time the while is repeated, the RAM be reused?
It's unspecified. The answer is a resounding "maybe".
The language specification never says what happens to unreachable objects, since it's unobservable to the program. (That's what being unreachable means).
In practice, the native Dart implementation uses a generational garbage collector.
The default behavior would be to allocate a new object in "new-space" and overwrite the reference to the previous object. That makes the previous object unreachable (as long as you haven't store other references to it), and it can therefore be garbage collected. If you really go through objects quickly, that will be cheap, since the unreachable object is completely ignored on the next new-space garbage collection.
Allocating a lot of short-lived objects still has an overhead since it causes new-space GC to happen more often, even if the individual objects don't themselves cost anything.
There is also a number of optimization that may change this behavior.
If your object is sufficiently simple and the compiler can see that no reference to it ever escapes, or is used in an identical check, or ... any other number of relevant restrictions, then it might "allocation sink" the object. That means it never actually allocates the object, it just stores the contents somewhere, perhaps even on the stack, and it also inlines the methods so they refer to the data directly instead of going through a this pointer.
In that case, your code may actually reuse the memory of the previous object, because the compiler recognizes that it can.
Do not try to predict whether an optimization like this happens. The requirements can change at any time. Just write code that is correct and not unnecessarily complex, then the compiler will do its best to optimize in all the ways that it can.

How can we decide whether we should use autoreleasepool?

Since Apple's API is not opened source nor it is mentioned in the documentation, when writing in Swift, we have no way, to know whether the returned object is an autorelease objective-c object.
Hence, it becomes unclear when we should use autoreleasepool
https://developer.apple.com/library/archive/documentation/Cocoa/Conceptual/MemoryMgmt/Articles/mmAutoreleasePools.html#//apple_ref/doc/uid/20000047-1041876
If you write a loop that creates many temporary objects.
You may use an autorelease pool block inside the loop to dispose of
those objects before the next iteration. Using an autorelease pool
block in the loop helps to reduce the maximum memory footprint of the
application.
Without autoreleasepool
for ... {
FileManager.default.copyItem
CGImageSourceCreateWithURL
CGImageSourceCopyPropertiesAtIndex
CGImageSourceCreateThumbnailAtIndex
CGImageDestinationCreateWithURL
CGImageDestinationFinalize
}
With autoreleasepool
for ... {
autoreleasepool {
FileManager.default.copyItem
CGImageSourceCreateWithURL
CGImageSourceCopyPropertiesAtIndex
CGImageSourceCreateThumbnailAtIndex
CGImageDestinationCreateWithURL
CGImageDestinationFinalize
}
}
I try to run an intensive loop over the above 2 code for comparison purpose.
I found no significant difference in their memory usage pattern, based on XCode memory report.
I was wondering, what are some good guideline/ thought process, to decide whether we should apply autoreleasepool throughout our code?
I have such concern, as recently I saw autoreleasepool is required in code which involves FileHandle.read - https://stackoverflow.com/a/42935601/72437
Use FileManager to copy item doesn't have a huge payload. And Image I/O you're using will save a lot of memory during the I/O process. In addition, apple's image api will have caches for the same file.
That's why your code won't have a significant difference. Because you didn't make any memory payload.
You could try another way to validate the usage of autoreleasepool. And I can assure that it will have a tremendous difference.
Use for-loop(10000 times) to generate random strings (longer is better), and use each string to transform an UTF-8 data in each loop. Then see different memory growth from the with or without autoreleasepool case.
Try it out.

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.

[__NSCFString count]: unrecognized selector sent to instance 0x75bc230'

My code is:
SCDownloadManagerView *downLoadMnger = [[SCDownloadManagerView alloc]init]
[self.vw_ownVw addSubview:downLoadMnger.view]
[self.vw_ownVw bringSubviewToFront:downLoadMnger.view]
I am getting this error on second line [self.vw_ownVw addSubview:downLoadMnger.view]
Please help me.
In my experience, what usually causes this error is when memory has been released prematurely. In this case, it is possible that your program is trying to use an array, but because it was not properly retained, the array was deallocated, and an NSString was allocated in the same spot. When your program tries to access the array, it sends the count message to where it thinks the array is, but because a string has been allocated there instead, the string gets the count message and this causes an error because strings don't respond to count.
The code you posted is not the cause of the problem, it is only the point at which this bug is manifesting. In order to find the cause, you need to review your memory management. Try running "Build & Analyze", the static analyser is very good at picking up obvious mistakes in memory management. Review parts of your code that deal with arrays, but keep in mind that the array in question could also be managed by another object outside of your code (such as a view or view controller) that you have released too early, etc.

Resources