NSManagedObjectContext save fails when saving too many items in ios7? - ios7

I have the following two lines of code that works almost all the time:
NSError *error = nil;
BOOL isSuccessful =[self.tempMoc save:&error]; // tempMoc is a NSManagedObjectContext
This code works as expected on ios6 simulator, ios6 physical devices and ios7 simulator. The variable isSuccessful evaluates to Yes.
However, on ios7 physical devices, isSuccessful evaluates to NO. Why is that?
error is always nil in all four cases mentioned.
Does anyone know why this is the case and how I can get isSuccessful to evaluate to YES on ios7 physical devices?
ADDITIONAL DETAILS
After more debugging I noticed something . Prior to the tempMoc save above, I have this code running:
- (void)saveCompatibilities:(NSArray *)objects {
NSString *entityName = NSStringFromClass([Compatibility class]);
for (NSDictionary *newObjectDict in objects) {
Compatibility *object = [NSEntityDescription insertNewObjectForEntityForName:entityName inManagedObjectContext:self.tempMoc];
object.prod1 = newObjectDict[#"prod1"]; // value is just the letter a
object.prod2 = newObjectDict[#"prod2"]; // value is just the letter a
}
}
I noticed that if the number of iterations in the for loop is very large, like let's say 50 000 loops, then I encounter the ios7 isSuccessful == NO issue mentioned above. If it is only say 20 loops, then isSuccessful evaluates to yes. The number of loops that causes the ios7 isSuccessful failure is different with every single run.
I'm starting to think this is a memory issue with my device?

Sounds like a memory issue. Try saving periodically, or on memory pressure. You could also make a child context to save on a different thread.

Related

NSOperationQueue methods parameter are becoming empty

I am having problem with NSOperationQueue, if I am adding the same operation for 200 times method is behaving as expected.
But if I increase the for loop to 500 times, parameter are becoming empty when queue will start executing the task. Below is the code snippet.
- (void)someMethod:(char *)param1 {
NSBlockOperation *theOp = [NSBlockOperation blockOperationWithBlock: ^{
// use this paramter and do something
}];
[[MyQueueService sharedMyQueueService] operationQueue] addOperation:theOp];
}
This is how I am invoking the above method
for (int index = 1; index < 500; index++) {
MyClass *classInstance = [[MyClass alloc] init];
NSString *parm1 = [NSString stringWithFormat:#"%d", index];
[classInstance someMethod:(char *)[string cStringUsingEncoding:NSUTF8StringEncoding]];
}
Here is becoming empty i.e "", if i run the same method for 500 time, due to this I am unable to perform other operation. Please help me regarding this.
The problem is not with NSOperationQueue. The issue is the use of char *. As the documentation for cStringUsingEncoding says:
The returned C string is guaranteed to be valid only until either the receiver is freed, or until the current memory is emptied, whichever occurs first. You should copy the C string or use getCString:maxLength:encoding: if it needs to store the C string beyond this time.
Bottom line, simple C pointers like char * do not participate in (automatic) reference counting. Your code is using dangling pointers to unmanaged buffer pointers. This is exceedingly dangerous and when not done properly (as in this case), will lead to undefined behavior. The resulting behavior is dictated whether the memory in question happened to be reused for other purposes in the intervening time, which can lead to unpredictable behavior that changes based upon completely unrelated factors (e.g. the loop count or whatever).
You should try running your app with the "address sanitizer" turned on (found in the scheme settings under the "run" settings, on the diagnostics tab) and it will likely report some of these issues. E.g. when I ran your code with address sanitizer, it reported:
==15249==ERROR: AddressSanitizer: heap-use-after-free on address 0x60300003a458 at pc 0x00010ba3bde6 bp 0x70000e837880 sp 0x70000e837028
For more information, see Address Sanitizer documentation or its introductory video.
The easiest solution is going to be to eliminate the char * and instead use the int index value or use an object, such as NSString *.

Non-deterministic crash in BlocksKit bk_apply block

I have a function that constructs an NSMutableDictionary using bk_apply, a method provided by the third-party block utility library BlocksKit. The function's test suite usually passes just fine, but once every couple runs it crashes.
NSMutableDictionary *result = [[NSMutableDictionary alloc] init];
[inputSet bk_apply:^(NSString *property) {
NSString *localValueName = propertyToLocalName[property];
NSObject *localValue = [self valueForKey:localValueName];
result[property] = localValue ?: defaults[property]; // Crash
// Convert all dates in result to ISO 8601 strings
if ([result[property] isKindOfClass:[NSDate class]]) { // Crash
result[property] = ((NSDate *)result[property]).ISODateString; // Crash
}
}];
The crash always happens on a line where result is referenced, but it's not the same line every time.
Examining the contents of result in the debugger, I've seen very strange values like
po result
{
val1 = "Some reasonable value";
val2 = "Also reasonable value";
(null) = (null);
}
It's impossible for an NSDictionary to have null keys or values, so clearly some invariant is being violated.
What is causing this crash and how do I fix it?
From the BlocksKit documentation for bk_apply:
Enumeration will occur on appropriate background queues. This will
have a noticeable speed increase, especially on dual-core devices, but
you must be aware of the thread safety of the objects you message
from within the block.
The code above is highly unsafe with respect to threading, because it reads from and writes to a mutable variable on multiple threads.
The intermittent nature of the crash comes from the fact that the thread scheduler is non-deterministic. The crash won't happen when several threads accessing shared memory happen to have their execution scheduled in sequence rather than in parallel. It is therefore possible to "get lucky" some or even most of the time, but the code is still wrong.
The debugger printout is a good example of the danger. The thread that's paused is most likely reading from result while another thread performs an insertion.
NSMutableDictionary insertions are likely not atomic; example steps might be,
allocate memory for the new entry
copy the entry's key into the memory
copy the entry's value into the memory
If you read the dictionary from another thread between steps 1 and 2, you will see an entry for which memory has been allocated, but the memory contains no values.
The simplest fix is to switch to bk_each. bk_each does the same thing as bk_apply but it's implemented in a way that guarantees sequential execution.

Changing Philips Hue Brightness with a UISlider Objective C

I am building an app that controls three philips hue RGB LED bulbs. I want to be able to change the brightness using the UISlider. Currently I have a UISlider that calls a method upon each change, however, this method far exceeds the philips hue bridge's 10 commands per second limitation. Here is the method I call upon a change in the UI slider.
- (void) changeBulbBrightness: (NSNumber *)currentBrightness
{
NSTimeInterval timeInterval = [self.timeLastCommandSent timeIntervalSinceNow];
NSLog(#"Time Since Last command: %f", timeInterval);
if (timeInterval < -0.3)
{
NSLog(#"COMMAND SENT!!!!");
PHBridgeResourcesCache *cache = [PHBridgeResourcesReader readBridgeResourcesCache];
PHBridgeSendAPI *bridgeSendAPI = [[PHBridgeSendAPI alloc] init];
for (PHLight *light in cache.lights.allValues)
{
PHLightState *lightState = light.lightState;
//PHLightState *lightState = [[PHLightState alloc] init];
if (lightState.on)
{
[lightState setBrightness:currentBrightness];
// Send lightstate to light
[bridgeSendAPI updateLightStateForId:light.identifier withLightState:lightState completionHandler:^(NSArray *errors) {
/*if (errors != nil) {
NSString *message = [NSString stringWithFormat:#"%#: %#", NSLocalizedString(#"Errors", #""), errors != nil ? errors : NSLocalizedString(#"none", #"")];
if (self.loggingOn)
{
NSLog(#"Brightness Change Response: %#",message);
}
}
*/
}];
}
self.timeLastCommandSent = [[NSDate alloc]init];
}
}
self.appDelegate.currentSetup.brightnessSetting = currentBrightness;
NSLog(#"Brightness Now = %#", currentBrightness);
I've tried making a timer to limit the amount of commands to 10 per second but the bridge still acts the same way it does when it is overwhelmed with commands (stops acceptance of all commands). Any help or direction would be greatly appreciated. Thanks in advance!
One reason might be your multiple lights. You are sending an update command for each light. So if you have 3 bulbs connected as in the Hue starter kit, you might still send 10 or a little more if there is some unfortunate caching involved packing the updates from 2 seconds into 1 second of sending. Thus I suggest you further decrease the number of updates you are sending (try 0.5 or even 1.0) as an interval and see if it gets better.
Also note that the SDK is quite vague about the rate limit. It says:
If you stay roughly around 10 commands per second
Since the Philips Hue SDK is generally not that well supported (look at the open GitHub issues), take this with a grain of salt and do your own experiments. Once I have the time to check it myself, I will post an update here.
Update 1: I just discovered this remark by one of the contributors to the Hue SDK github repo (maybe Philips employee) advising to only send 2 commands per second:
As mentioned earlier, be careful when doing lot's of requests in a loop as the calls to PHBridgeSendAPI are not blocking and requests are retained until they get a response or timeout. Two calls per second seems like a safe rate, if you want to have a higher rate, it's advised to chain requests with a queuing mechanism of your own (to ensure memory will be released before new requests are getting called).

Objective-C thread safe code to prevent a crash

I got an iPhone crash report with a SIGSEGV and I think I've narrowed down the possible cause and a solution.
Since crashes caused by threads are hard to debug I can't repro this problem, but could use some help with my hypothesis - is it sound?
My code uses ASIHttpRequest to download a set of files using an ASINetWorkQueue. Here is a simplified sample
//initialize download queue and do this code block in a loop for each file
NSURL *fileURL = [NSURL URLWithString:...
__block ASIHTTPRequest *fileRequest = [ASIHTTPRequest requestWithURL:fileURL];
[fileRequest setCompletionBlock:^{
//do some stuff
}];
[fileRequest setFailedBlock:^{
NSString *someError = [NSString stringWithFormat:...
[self someErrorMethod:someError];
}];
[downloadQueue addOperation:...
-(void)someErrorMethod(NSString *errorMessage) {
DDLogWarn(errorMessage);
if ([self downloadQueue]) {
for (ASIHTTPRequest *request in [[self downloadQueue] operations]) {
[request clearDelegatesAndCancel];
}
[[self downloadQueue] reset];
}
}
The top 2 lines of the crash report are
libobjc.A.dylib 0x31846fbc objc_msgSend + 15
MyApp 0x0002cab5 -[Myapp someErrorMethod:] (MyApp.m:)
My thinking for why this happened
A file download fails and the failed block is called
It goes through every request and clears delegates and cancels them and then resets the queue
However, while it is till running, another file download fails and enters the failed block callback
However, since it has now been cancelled, its failed block has been released
When the code tries to log the error message, its memory has been released and unpredictable results follow
Does this make sense? Since I am newish to Objective-C, is my analysis correct or am I missing something obvious?
I am thinking of using a lock to make the errorMethod thread safe with the hope that it will fix this issue. Does that sound like the right solution based on the code above?
Thanks
This doesn't sound likely. ASIHttpRequest likely performs all of its callbacks on the same thread (I'm fairly certain on this one).
If I had to guess, your error is more likely in this line:
DDLogWarn(errorMessage);
The first parameter to DDLogWarn is a format, not a string. This will likely crash in any case that errorMessage includes a %. What you meant is:
DDLogWarn(#"%#", errorMessage);
Since DDLogWarn() is a varags method, it will start substituting the (random) values it finds on the stack for any % substitutions in the string. It will read the stack until you run out of % substitutions. If any of the % substitutions are pointer-based (like %s or %#), then it will follow the pointer to a random location.
SEG_ACCERR means that you've requested a piece of memory you don't own. SEG_MAPERR means you've requested a piece of memory that is not mapped. Either is an expected result of following a totally random pointer.

problem with saving data at coredata?

In my application there is searchBar. when we input a text, it will do functionGrab (grab data from internet and save it to coredata), example :
if we input "Hallo"
if([[dict objectForKey:#"Category"] isNotEmpty] && [[[dict objectForKey:#"Category"] objectAtIndex:0] class]!=[NSNull class]){
NSMutableArray * DownloadedTags =[dict objectForKey:#"Category"];
NSMutableSet * TagsReturn=[NSMutableSet set];
for(int i=0;i<[DownloadedTags count];i++){
NSString * Value=[DownloadedTags objectAtIndex:i];
Tag * thisTag= (Tag*)[GrabClass getObjectWithStringOfValue:Value fromTable:#"Tag" withAttribut:#"Name"];
[TagsReturn addObject:thisTag];
}
NSMutableSet * manyManagedObjects = [BusinessToSave mutableSetValueForKey:#"Tags"];
[self removeDifferenceBetween2MutableManagedObjectSets:manyManagedObjects withDownloadedVersion:TagsReturn];
}
So each biz has many categories. WHat happen in multi threaded application is one thread put category. The other thread also put the same category before committing.
So, [GrabClass getObjectWithStringOfValue:Value fromTable:#"Tag" withAttribut:#"Name"]; gives a new object even though some other thread already created the same object without knowing it.
If I synchronized the whole thing that the code would run serially and that'll be slow.
functionGrab:"H"
functionGrab:"Ha"
functionGrab:"Hal"
functionGrab:"Hall"
functionGrab:"Hallo"
something like,it do that functionGrab 5 times
I want to make functionGrab at background, but the problem is when I do that function without synchronized it will save more than one of data, so the result is there are 5 hallo words in my coredata, but if I do that with synchronized, it spent so much time, so slow..
is there any way to help my problem?
I do not recommended having more than one thread "creating" the same types of data for the exact reason you are running into.
I would suggest you queue all of your "creates" into a single thread and a single NSManagedObjectContext to avoid merge or duplication issues.
The other option would be to make the app Lion only and use the parent/child NSManagedObjectContext design and then your children will be more "aware" of each other.