ARC. Object from array points to nil - ios

I have one ViewController that takes objects from CoreData and build with them a UITableView.
When a user press a row, I get the reportage object and pass it to the next view controller using:
Reportage *reportage = [self.reportages objectAtIndex:indexPath.row];
[self.tableView deselectRowAtIndexPath:indexPath animated:YES];
ReportageTeaserPanelViewController *rightController = (ReportageTeaserPanelViewController*)self.menuContainerViewController.rightMenuViewController;
rightController.reportage = reportage;
[self.menuContainerViewController toggleRightSideMenuCompletion:nil];
The variable reportage is declared as strong in the controller ReportageTeaserPanelViewController.
The problem is the following. If I have to reload asynchronously the objects in the array of the parent view controller because of there is an update in my web service, the variable reportage gets nil in the controller ReportageTeaserPanelViewController. I thought that using a strong reference, the controller ReportageTeaserPanelViewController should be keep the "old" reportage object although it disappears from the array.
Is the any explanation for this behaviour?
Thanks

From what I can gather, you start with self.reportages not being nil, but since it's reloaded asynchronously after that initial load, when the user selects the table during an asynchronous reload, you're in danger of self.reportages being nil or potentially incomplete.
To prevent initializing your local reportage variable with a nil value, I'd recommend NOT directly reloading your self.reportages array in the asynchronously block in your parent view controller, but instead creating a local array within that block and then, once that array contains the full and proper contents, setting self.reportages to contain the contents of that local array. That way as long as your web service returns the proper "reportage," self.reportages will never equal nil.

I thought that using a strong reference, the controller ReportageTeaserPanelViewController should be keep the "old" reportage object.
Strong references are irrevelant. Nothing in this code is "kept" - these are all local, automatic variables, so they go out of existence as soon as the code runs. Only persistent references can make an object persist. An instance variable / property is an example of a persistent reference; it lives as long as the instance itself, unless of course you change its value. So the place to look is you instance variable, reportages.
You are saying:
Reportage *reportage = [self.reportages objectAtIndex:indexPath.row];
If there is no object at that index, there is no object at that index. It has nothing do with strong references. You need to think about where reportages is supposed to get its value.
It sounds from your use of the word "asynchronously" as if you might be setting reportages on a different thread from, and possibly actually later than, this code is running. That would be the issue. You need to get your threading and order of events sorted out.

Related

Crash when deleting object from Core Data and toggling view controllers

I am making a NSFetchRequest for a NSManaged Object on my initial screen. I sometimes have a crash in a scenario when I :
switch to another view controller within my tab bar controller
make another fetch request with the same managed object type
delete a common managed objects which also appears in my initial VC's fetchrequest. The VC contains a table view.
save the managed context
toggle to the first VC, and reload the data
I am not using NSFetchResutltsController to manage these returned objects. The crash happens when my tableview reloads. I do make another request, and expected the deleted objects not be returned, but it does. When my cells are trying to read a property of the deleted object, it reads uninitialized and crashes. This happens about 1 out of 5 times when toggling between the 2 VCs. I am using performAndWait in all of my CoreData functions.
Is there a way to decouple the the relationship of the Managed Objects between the two screens? If not, how can I get my fetch request in the first VC, not return the objects that were deleted in the second VC, keeping them in sync?
A NSManagedObject is not like other other objects. It does not contain any information itself. It has a pointer to its context and an objectID. When you access it's properties it forwards the request to the context to get the information that it needs. So when an entity is deleted from the context the managedObject stops working and causes a crash. This is why in general I think it is a bad practice to EVER keep a pointer to a managedObject and ALWAYS access them using a fetchedResultsController even if only for one object, and only do a fetch if the managedObjects results are discards right afterwards.
There are two possible solutions, which you hinted to in your question. Either you can copy the values out of the managedObject, or you can use a fetchedResultsController. If you copy the values then it will appears as normal even after the entity is deleted. If you use a fetchedResultsController then the fetchedObjects property will be never contain deleted object, and the object will be inaccessible after it is deleted.
I would recommend using a fetchedResultsController. You don't need to be afraid of it. It is not a large overhead and it reasonable to use even if you are fetching only one object.

NSFetchedResultsController notifies its Delegate of delete changes when a managed object is modified, and never notifies for Insert or Update

I have a UITableViewController, which is a delegate for an NSFetchedResultsController. My NSFetchedResultsControllerDelegate functions are set up as per "Typical Use" in Apple's documentation, with the table view controller as the fetched result controller's delegate property.
I also have some view controllers which are presented on top of the table view where the managed objects can be modified. These modifications are done on the same managed object context. There is only one managed object context constructed in my application, and it is accessed globally. (I have put some print statements in my object context construction just to be sure I am not accidentally re-constructing it elsewhere.)
When I modify one of the managed objects, the delegate function controller:didChangeObject is called, but the NSFetchedResultsChangeType is always .Delete. When I create a managed object, the delegate function does not fire at all.
However, when I manually call call performFetch() and tableView.reloadData(), the cells are restored to the correct state: the removed row comes back, any not-inserted rows are created.
The result is that deleting an object works as expected (the cell is removed), but updates to an object cause its cell to be removed, and object creations do not trigger cell inserts.
I tried to create a simple demo of this behaviour, but when I re-created the situation from a blank application, I don't see this behaviour. So something within my application is causing this strange behaviour, but I can't figure out what. Any ideas?
Extra Info:
The actual construction of the predicate and sort descriptors are done over several different classes, but printing them via print(resultsController.fetchRequest.predicate) and print(resultsController.fetchRequest.sortDescriptors) gives the following:
Optional(readState == "2" OR readState == "1")
Optional([(readState, ascending, compare:), (title, ascending, compare:)])
I have put a print statement in my controller:didChangeObject: method, and I can see that this only gets called with type.rawValue = 2 (i.e. .Delete), and only when I modify objects, not when I create them.
It's an inconsistency with how NSFetchedResultsController handles its NSPredicate.
If the NSFetchedResultsController is constructed with a fetch request which has a predicate which does a comparison between an integer and a string like follows:
let predicate = NSPredicate(format: "integerAttribute == %#", String(1))
this will lead to the predicate string being:
integerAttribute == "1"
When this is the case, initial fetches work fine: calling the function performFetch() on the fetched results controller returns all objects where the integerAttribute is equal to 1 (where integerAttribute is of type Int32).
However, the notifications to NSFetchedResultsControllerDelegate do not work fine. Modifications of managed objects result in the delegate being notified of a NSFetchedResultsChangeType.Delete change. Creations of managed objects do not invoke the delegate at all.
To make all this weirdness go away, fix the predicate format string as follows:
let predicate = NSPredicate(format: "integerAttribute == %d", 1)

updates to global object not reflecting by changing value of local object in iOS

Today I faced a very strange issue in Objective C, if anybody can help me figure out, i would really appreciate it. Let me explain the scenario.
Below is the case:
I make a webservice call and get NSArray of Objects and cached in Global class.
I have a local NSArray in view controller which is nothing but assigned from Global class array. Like this
self.listOfObjects = [GlobalCache sharedInstance].listOfObjects
self.listOfObjects is strong and nonatomic.
In a scenario I updated an object in self.listofobjects, lets say nameProperty of the object in array. Its updated in self.listOfObjects and when i print object using command
po ((ObjectType*)self.listOfObjects[0]).name
its fine and printing the updated value.
But when i see the value in GlobalCache of the same objects, Its not reflecting sometimes its still having old values.
self.listOfObjects is just reference to the GlobalCache listOfobject. (No where it is copied from global cache to local object)
Could it be a memory problem?

Sending NSNotifications to all objects of a class

I have an object that can be selected by a user click. With the current requirements of the app, at any time, there is no more than one of these items selected at any point during app execution.
I implemented a mechanism to enforce this, as follows:
Each of these objects has a unique identifier as a property.
When each object is created, it subscribes to the NSNotificationCenter listening for the MY_OBJECT_SELECTED notification.
When each object is selected, it posts the MY_OBJECT_SELECTED notification, with its unique Id as part of the userInfo dictionary.
Then, when each object receives the notification, it checks to see if its id is the same as the one in the userInfo. If it is, it does nothing, but if it isn't, it sets itself to unselected.
Is this a decent approach to the problem? If not, how would you do it?
It is a decent way of doing it, although it is not very efficient. The more objects you have, the more time you spend comparing IDs. The easiest way is to store your object pointers and IDs in a map table (or similar) and remember the last selected object. Whenever you select a new object, you clear the selection flag of the last selected object, then look up the new object and set its selection flag. It requires you to keep a collection of your objects, though.
The time required to update selections with this approach is independent of the number of objects you have.
If the object is spread all over the app,i.e. if it is a member in various classes. You can have a global object of same type and assign it to only that object which has been touched. In steps it will be like:
Have a global variable of object type.
At any object touch assign globalObject = currentObject;
do all operations on globalObject throughout app like calling methods and modifying object members(have a check for nil to ensure safety).
Reassign to different object with new touch.

NSFetchedResultsChangeInsert gets called twice for 1 insert with different object IDs

I'm losing my mind around this question.
So I have a Core Data setup in my iOS app done this way:
http://www.cocoanetics.com/2012/07/multi-context-coredata/
I then insert an object by creating a temporary MOC (as explained in the blog post) and perform saves on all 3 contexts in performBlock: methods.
In a view controller I have an NSFetchedResultsController and it gets notified that I did indeed insert a new object. The problem is that the NSFetchedResultsChangeInsert is fired twice and each time the object that is passed trough has a different objectID (it also is a different object instance in memory). What happens is that I then have 2 rows inserted in my table view but un the SQL database there is only one new. It then of course crashes when I scroll to the bottom of the table view.
If I also perform some updates on the object I get NSFetchedResultsChangeUpdate called only once and with the objectID that was passed in the second NSFetchedResultsChangeInsert call.
The first ID looks like this:
<x-coredata:///ReceivedMessage/t605BB9A7-A04E-4B89-B568-65B12E8C259A2>
The second (and all consequent ones) like this:
<x-coredata://02A917C5-850F-4C67-B8E4-1C5790CF3919/ReceivedMessage/p28>
What could this be? Am I missing out something obvious?
PS: I also checked if the notification comes from the same context, thread, etc. It does.
The two IDs you are seeing may very well represent one object. The difference between them is just that the first one is a temporary object ID, assigned to the object on creation, and the second one is the permanent object ID, assigned to the object when it gets stored to the managed object store (see NSManagedObjectID's isTemporaryID).
To work around this issue you could call NSManagedObjectContext's obtainPermanentIDsForObjects:error: just before you save the temporary MOC. This way the inserted object will have just one ID during the save propagation and the NSFetchedResultsControllerDelegate methods should get called just once.

Resources