creating a nsmanagedcontext in IOS 9 and below - objective-c

In IOS 10, creating an NSManagedObjectContext and nsmanagedObject was in the followoing:
NSManagedObjectContext *context = ((AppDelegate*)[[UIApplication sharedApplication] delegate]).persistentContainer.viewContext;
NSManagedObject *object = [NSEntityDescription insertNewObjectForEntityForName:#"Leads" inManagedObjectContext:context];
but, in ios 9 and above, there isnt the presistentContainer, so how do i create an NSManagedObjectContext in IOS 9? I tried the following, but didnt work, it returned nil:
- (NSManagedObjectContext *)managedObjectContext {
// Returns the managed object context for the application (which is already bound to the persistent store coordinator for the application.)
if (_managedObjectContext != nil) {
return _managedObjectContext;
}
NSPersistentStoreCoordinator *coordinator = [self persistentStoreCoordinator];
if (!coordinator) {
return nil;
}
_managedObjectContext = [[NSManagedObjectContext alloc] init];
[_managedObjectContext setPersistentStoreCoordinator:coordinator];
return _managedObjectContext;
}

In iOS 9, the instantiation of the NSManagedObjectContext changed to specify the concurrency type for this object.
This means that we now have to make a choice about what thread our managed object context is initialised on: the main queue, or perhaps a special background queue we’ve created. Our choices are:
NSPrivateQueueConcurrencyType
NSMainQueueConcurrencyType
So the below:
_managedObjectContext = [[NSManagedObjectContext alloc] init];
Should become:
_managedObjectContext = [[NSManagedObjectContext alloc] initWithConcurrencyType:NSMainQueueConcurrencyType];
ref: https://developer.apple.com/documentation/coredata/nsmanagedobjectcontext#//apple_ref/c/tdef/NSManagedObjectContextConcurrencyType

Related

iOS 8 Core Data Issue: global ID may not be temporary when recording

I'm migrating my App from iOS 7 to iOS 8, and I've been receiving the following error in Xcode when I try to save the Core Data context. This error was not present in iOS 7 and Xcode 5.
This is the code I am trying to run:
[_managedObjectContext save:&error]
This is the exception:
Terminating app due to uncaught exception 'NSInternalInconsistencyException', reason: 'recordChangeSnapshot:forObjectID:: global ID may not be temporary when recording
Here's the code for my managedObjectContext:
- (NSManagedObjectContext *)managedObjectContext {
if (_managedObjectContext != nil) {
return _managedObjectContext;
}
NSPersistentStoreCoordinator *coordinator = [self persistentStoreCoordinator];
if (coordinator != nil) {
NSManagedObjectContext* moc = [[NSManagedObjectContext alloc] initWithConcurrencyType:NSMainQueueConcurrencyType];
[moc performBlockAndWait:^{
[moc setPersistentStoreCoordinator: coordinator];
[[NSNotificationCenter defaultCenter]addObserver:self selector:#selector(mergeChangesFrom_iCloud:) name:NSPersistentStoreDidImportUbiquitousContentChangesNotification object:coordinator];
}];
_managedObjectContext = moc;
}
return _managedObjectContext;
}
- (void)mergeChangesFrom_iCloud:(NSNotification *)notification {
NSLog(#"Merging in changes from iCloud...");
NSManagedObjectContext* moc = [self managedObjectContext];
[moc performBlock:^{
[moc mergeChangesFromContextDidSaveNotification:notification];
NSNotification* refreshNotification = [NSNotification notificationWithName:#"SomethingChanged"
object:self
userInfo:[notification userInfo]];
[[NSNotificationCenter defaultCenter] postNotification:refreshNotification];
}];
}

How do I resolve this deadlock that happen ocassionally?

I have one managedObjectContext with concurency type of NSMainQueueConcurrencyType
+ (NSManagedObjectContext *)managedObjectContextMainThread
{
static NSManagedObjectContext *__managedObjectContext=nil;
#synchronized(self)
{
if (__managedObjectContext != nil)
{
}
else {
NSPersistentStoreCoordinator *coordinator = [self persistentStoreCoordinator];
if (coordinator != nil)
{
__managedObjectContext = [[NSManagedObjectContext alloc] initWithConcurrencyType:NSMainQueueConcurrencyType];
[__managedObjectContext setPersistentStoreCoordinator:coordinator];
}
}
}
return __managedObjectContext;
}
The main managedObjectContext is NEVER accessed outside of the main thread except when setting the other managedObjectContext.parent. So that mainManagedObjectContext is the parent for all threads.
Now when I run the program, sometimes it reach a deadlock. I pause the program and this is what I see:
As we see in the picture, there are 2 threads that seems to be in the deadlock. The first is the main thread.
It deadlock on #synchronize (self). Reasonable.
The other thread is deadlock on:
So it locks when trying to change the persistent store of the static variable that hold the __managedObjectContext.
Let me put the code again to reiterate:
+ (NSManagedObjectContext *)managedObjectContextMainThread
{
static NSManagedObjectContext *__managedObjectContext=nil;
#synchronized(self) //Main thread deadlock here
{
if (__managedObjectContext != nil)
{
}
else {
NSPersistentStoreCoordinator *coordinator = [self persistentStoreCoordinator];
if (coordinator != nil)
{
__managedObjectContext = [[NSManagedObjectContext alloc] initWithConcurrencyType:NSMainQueueConcurrencyType];
[__managedObjectContext setPersistentStoreCoordinator:coordinator]; //Secondary thread dead lock here
}
}
}
return __managedObjectContext;
}
My question is why in the earth the [__managedObjectContext setPersistentStoreCoordinator:coordinator];
NOTHING else is accessing __managedObjectContext. The second thread (the non main one) is trying to set the __managedObjectContext as the parent context. The first thread is just waiting happily in #synchronized. It doesn't do anything.
So why the deadlock and how to solve that?
Oh the child managedObjectContext is created here:
#synchronized(self)
{
if ([managedObjectContexts objectForKey:[self threadKey]] == nil ) {
NSManagedObjectContext *threadContext = [[NSManagedObjectContext alloc] initWithConcurrencyType:NSPrivateQueueConcurrencyType];
threadContext.parentContext = [self managedObjectContextMainThread]; //Stuck here. This goes straight to above function managedObjectContextMainThread where it stucks.
threadContext.mergePolicy = NSMergeByPropertyObjectTrumpMergePolicy;
[managedObjectContexts setObject:threadContext forKey:[self threadKey]];
}
}
The problem is I tried to create the main managed object context on a thread different than the main thread.
For some reason it doesn't work.
I still love lazy loading. So all I need to do is to ensure that the main managedObjectContext is created on
the main thread
before any other managedObjectContexts are created.
I do not want to ensure that my program do not try to access the other managedObjectContexts first
So that looks like a job for dispatch_sync.
I then added this code:
dispatch_sync(dispatch_get_main_queue(),^{
[self managedObjectContextMainThread];//Access it once to make sure it's there
});
before I created all background child managedObjectContexts. That should be fast because once created the function will only return the static variable.
+(NSManagedObjectContext *)managedObjectContext {
NSThread *thread = [NSThread currentThread];
//BadgerNewAppDelegate *delegate = [BNUtilitiesQuick appDelegate];
//NSManagedObjectContext *moc = delegate.managedObjectContext;
if ([thread isMainThread]) {
//NSManagedObjectContext *moc = [self managedObjectContextMainThread];
return [self managedObjectContextMainThread];
}
else{
dispatch_sync(dispatch_get_main_queue(),^{
[self managedObjectContextMainThread];//Access it once to make sure it's there
});
}
// a key to cache the context for the given thread
NSMutableDictionary *managedObjectContexts =[self thread].managedObjectContexts;
#synchronized(self)
{
if ([managedObjectContexts objectForKey:[self threadKey]] == nil ) {
NSManagedObjectContext *threadContext = [[NSManagedObjectContext alloc] initWithConcurrencyType:NSPrivateQueueConcurrencyType];
threadContext.parentContext = [self managedObjectContextMainThread];
//threadContext.persistentStoreCoordinator= [self persistentStoreCoordinator]; //moc.persistentStoreCoordinator;// [moc persistentStoreCoordinator];
threadContext.mergePolicy = NSMergeByPropertyObjectTrumpMergePolicy;
[managedObjectContexts setObject:threadContext forKey:[self threadKey]];
}
}
return [managedObjectContexts objectForKey:[self threadKey]];
}

addObject: to array not working (array still nil)

This app is a table view with a tab bar controller. I am logging the count of the array: arrayOfFavourites and even though i add an object is continues to have a nil value, my relating code, all objects shown are allocated and initialized in the code (previous or present) some are instances and some are properties:
ListViewController.m:
-(void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath {
NSLog(#"TOUCHED CELL!");
// Push the web view controller onto the navigation stack - this implicitly
// creates the web view controller's view the first time through
[[self navigationController] pushViewController:webViewController animated:YES];
// Grab the selected item
entry = [[channel items] objectAtIndex:[indexPath row]];
if (!entry) {
NSLog(#"!entry");
}
// Construct a URL with the link string of the item
NSURL *url = [NSURL URLWithString:[entry link]];
// Construct a request object with that URL
NSURLRequest *req = [NSURLRequest requestWithURL:url];
// Load the request into the web view
[[webViewController webView] loadRequest:req];
// Take the cell we pressed
// IMPORTANT PART
CELL = [tableView cellForRowAtIndexPath:indexPath];
[webViewController setItem:entry];
webViewController = nil;
webViewController = [[WebViewController alloc] init];
[entry release];
}
WebViewController.m:
You shake to favorite a cell
-(void)motionBegan:(UIEventSubtype)motion withEvent:(UIEvent *)event {
cellToPassOn = nil;
NSLog(#"Favouriting"); // YES I KNOW SPELLING
// This is pretty simple, what we do is we take the cell we touched and take its title and link
// then put it inside an array in the Favourites class
Favourites *fav = [[Favourites alloc] init];
ListViewController *list = [[ListViewController alloc] init];
[self setCellToPassOn: [list CELL]];
if (!item) {
NSLog(#"NILLED ITEM");
}
[[fav arrayOfFavourites] addObject:[item autorelease]];
[fav setCell: cellToPassOn];
[fav release];
[list release];
item = nil;
}
Favourites.m:
-(NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
{
arrayOfFavourites = [[NSMutableArray alloc] init];
NSLog(#"ROWS NO.");
NSLog(#"%i", [arrayOfFavourites count]);
return [arrayOfFavourites count];
}
Why are you inializing the array in tableview:numberOfRowsInSection ? This will cause the array to be reset each time table view is reloaded. This could be your issue.
you are allocating your array in -(NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
try to allocate it somewhere else.
You could allocate the arrayOfFavorites in the tableView:numberOfRowsInSectionMethod, but then you first need to check if it is nil.
if( !arrayOfFavorites )
arrayOfFavoriges = [[NSMutableArray alloc] init];
You should release it then in the dealloc method: [arrayOfFavorites release].
-(void)motionBegan:(UIEventSubtype)motion withEvent:(UIEvent *)event {
cellToPassOn = nil;
NSLog(#"Favouriting"); // YES I KNOW SPELLING
// HERE creation of a Brand NEW empty Favourites instance
Favourites *fav = [[Favourites alloc] init];
// HERE creation of a Brand NEW empty ListViewController instance
ListViewController *list = [[ListViewController alloc] init];
// HERE we hope that the ListViewController as CELL other then nil when it is Brand NEW
[self setCellToPassOn: [list CELL]];
if (!item) {
NSLog(#"NILLED ITEM");
}
[[fav arrayOfFavourites] addObject:[item autorelease]];
[fav setCell: cellToPassOn];
[fav release];
// HERE the fav instance get deallocated and don't exist anymore
[list release];
// HERE the list instance get deallocated and don't exist anymore
item = nil;
}
In this code list and fav exist only in the body of this method, attempt to get to the value they have hold done to will failed, because list and fav doesn't exist outside that method.

core data multithread using

Dear community.
When i has a stable version of my application, while i don't start change code to multithread version. What was a difference between previous version:
in (void)applicationDidFinishLaunching:(NSNotification *)aNotification
i do loop to add to queue my code:
NSOperationQueue *opQueueImportUpdateFirstTimeData = [[[NSOperationQueue alloc]init]autorelease];
int i = 0;
for (NSString *carrier in currentCarriers)
{
AppController *operation = [[AppController alloc] initAndUpdateCarrier:carrier identifier:i];
[opQueueImportUpdateFirstTimeData addOperation:operation];
i++;
}
External class have:
- (id)initAndUpdateCarrier:(NSString *)forCarrier
identifier:(NSUInteger)iQuene;
{
[super init];
[self setIdentifierQuene:iQuene];
[self setStartForCarrier:forCarrier];
[self setPercentDone:0.0];
This point is a very important:
[self setDatabase:[[MySQLIXC alloc] init]];
u can't alloc other classes in process of u multithreading, i don't know why, but this is take malloc_error in whole queues
[self setAppDelegate:[[NSApplication sharedApplication] delegate]];
[self setManagedObjectContext:[[NSManagedObjectContext alloc] init]];
return self;
}
And in external class i have:
-(void) main;
{
[self makeUpdatesForCarrier:startForCarrier andTypeOfOperation:#"rates" forDirection:#"incoming"];// mySqlConnector:database];
it's a just some functions which working on local moc.
When i start application, interface didn't place it in background, and start visualization only after all queues will finish.
Before i try to alloc]init] MySQLIXC class inside my external class, but it gives me a lot of malloc_error_break exceptions, like somebody try to freeze memory for me.
Tnx.
Here is moc declaration:
in .h:
#property(retain) NSManagedObjectContext *managedObjectContext;
in .m:
#synthesize managedObjectContext;
Set persistent store coordinator:
[[self managedObjectContext] setUndoManager:nil];
[[self managedObjectContext] setPersistentStoreCoordinator:[appDelegate persistentStoreCoordinator]];
Merge changes for with main moc:
- (void)mergeChanges:(NSNotification *)notification;
{
AppDelegate *appDelegate = [[NSApplication sharedApplication] delegate];
NSManagedObjectContext *mainContext = [appDelegate managedObjectContext];
// Merge changes into the main context on the main thread
[mainContext performSelectorOnMainThread:#selector(mergeChangesFromContextDidSaveNotification:)
withObject:notification
waitUntilDone:YES];
And in one place of my code i'm using a main moc (only for read information, i know that moc not thread safe):
AppDelegate *appDelegate = [[NSApplication sharedApplication] delegate];
NSManagedObjectContext *mainContext = [appDelegate managedObjectContext];
NSFetchRequest *request = [[[NSFetchRequest alloc] init] autorelease];
[request setEntity:[NSEntityDescription entityForName:#"DestinationsListForSale"
inManagedObjectContext:mainContext]];
NSPredicate *predicate = [NSPredicate predicateWithFormat:#"lastUsedProfit > 0"];
[request setPredicate:predicate];
First off, CoreData is NOT thread safe. I would strongly advise that if you do not understand core data in a good deal of detail you keep you application singlethreaded or at the very least ensure that you always access the store from a single thread (probably best to use the main thread).
That said, this will not cause malloc_error_breaks AFAIK. You would see core data merge error exceptions and similar problems.
Could you show the code where you set up the moc further - just allocating and initing a moc is not enough - you have to set it's NSPersistentStoreCoordinator

NSCFArray leak in the NSMutablearray allocation

I am getting the leak at this allocation
filteredListContent = [[NSMutableArray alloc] initWithCapacity:[showList count]];
CODE:
-(void)reloadTable
{
EventListAppDelegate *appDelegate;
UIApplication app = [UIApplication sharedApplication];
appDelegate = (EventListAppDelegate *)[app delegate];
contactList = [appDelegate getAllContactsList];
inviteeList = [appDelegate getInviteeListForEvent:event.primaryKey];
if (isInvited == YES)
{
showList = [appDelegate getInviteeListForEvent:event.primaryKey];
}
else
{
showList = [appDelegate getAllContactsList];
}
filteredListContent = [[NSMutableArray alloc] initWithCapacity:
[showList count]];
[filteredListContent addObjectsFromArray: showList];
[self organizeContactItemsIntoIndexes];
self.title = [event.name capitalizedString];
[self getToolbar];
[theTableView reloadData];
}
- (void)searchBar:(UISearchBar *)searchBar textDidChange:(NSString *)searchText
{
[filteredListContent removeAllObjects];
ContactDTO *currentElement;
NSRange range;
for (currentElement in showList)
{
range = [currentElement.lastName rangeOfString:searchText
options:NSCaseInsensitiveSearch];
if(range.location == 0)
{
[filteredListContent addObject:currentElement];
}
}
[self organizeContactItemsIntoIndexes];
[theTableView reloadData];
}
- (void)dealloc
{
[filteredListContent release];
[super dealloc];
}
Your code will allocate a new instance of filteredListContent every time reloadTable is called, which will usually happen several times during the lifetime of your application. This causes a leak because the old instances are not released.
The best (and easiest) way to fix it would be to make filteredListContent a retain property:
in your class header:
#property (nonatomic, retain) NSMutableArray * filteredListContent;
in your reloadTable method:
self.filteredListContent = [NSMutableArray arrayWithCapacity:[showList count]];
Note the use of self. in the second code snippet. That syntax informs Cocoa that it should use the property accessor to set the value of filteredListContent, which will then send the appropriate retain and release messages for you.
You've posted three nearly-identical questions pertaining to memory leaks. It might be helpful for you to read through Apple's Memory Management Programming Guide.