I landed into a weird problem where i cannot use array.count that crashes my app.
#interface LAMasterViewController ()
NSMutableArray * claimReports
#end
-(void) ViewDidLoad
{
claimReports = [[NSMutableArray alloc] init];
[claimReports addObjectsFromArray:[[LADataModelController getSingleton] getClaimReportsOrderedByIncidentDate] ];
}
-(NSArray *) getClaimReportsOrderedByIncidentDate
{ // it returns one record
NSManagedObjectContext *context = [self managedObjectContext];
NSError *error;
NSFetchRequest *request = [[NSFetchRequest alloc] init];
NSEntityDescription *entity = [ NSEntityDescription entityForName:#"ClaimReport" inManagedObjectContext:context];
NSSortDescriptor *sortByIncidentDate = [[NSSortDescriptor alloc] initWithKey:#"dateOfIncident" ascending:NO];
[request setEntity:entity];
[request setSortDescriptors: [NSArray arrayWithObject: sortByIncidentDate]];
NSArray *array = [context executeFetchRequest:request error:&error];
NSLog(#"Array Count %i" ,array.count);
return array;
}
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
{
return claimReports.count; //crashes here
}
error:
-[LSClaimReport count]: unrecognized selector sent to instance 0xa54afc0
2014-04-01 14:56:29.022 LossAdjusting[6956:70b] * Terminating app due to uncaught exception 'NSInvalidArgumentException', reason: '-[LSClaimReport count]: unrecognized selector sent to instance 0xa54afc0'
What have i missed here. Seems a silly one. please guide.
Thanks
Long-story-short, you are treating an instance of LSClaimReport as if it was an NSMutableArray instance.
The end.
EDIT OK, flippany aside, you are confused about instance variable and local variables and have confused the types of one of your instance variables.
In ViewDidLoad (case incorrect, so if that's copied verbatim then it won't even be called), you reference a local version of claimReports which is created and then thrown away:
-(void)ViewDidLoad
{
NSMutableArray *claimReports = [[NSMutableArray alloc] init];
[claimReports addObjectsFromArray:[[LADataModelController getSingleton] getClaimReportsOrderedByIncidentDate] ];
}
and later you refer to the instance variable version:
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
{
return claimReports.count; //crashes here
}
Which is clearly a LSClaimReport instance and not a NSMutableArray.
So it looks like:
You have declared the wrong type in your #interface.
You are not initializing it correctly (you already fixed that bit).
please remove the local Arraydeclaration:
NSMutableArray * claimReports = [[NSMutableArray alloc] init];
instead use:
claimReports = [[NSMutableArray alloc] init];
In case this is no Class variable yet, add it to your #interface-declaration:
#interface LAMasterViewController ()
{
NSMutableArray * claimReports
}
#end
#implementation LAMasterViewController
Write this in your .h file
NSMutableArray * claimReports;
and in your .m
-(void)viewDidLoad{
claimReports = [[NSMutableArray alloc] init];
[claimReports addObjectsFromArray:[[LADataModelController getSingleton] getClaimReportsOrderedByIncidentDate] ];
}
You have few things wrong with your code which cleaning them up will help resolve the issue, the main issue '-[LSClaimReport count]: unrecognized selector sent to instance 0xa54afc0' is because you are calling count on an instance of LSClaimReport which clearly shouldn't be happening. Your code seems to think that claimReports is an instance of LSClaimReport and not an instance of NSMutableArray.
As for your code may I recommend changing to (Please see comments in code marked issue)
#interface LAMasterViewController ()
// Clearly you want this as a private property so why would you want to change
// to having this in the .h file which would make it public
// But Issue 1 is here you are missing the semi-colon (`;`) of the end.
#property (nonatomic, strong) NSMutableArray *claimReports;
#end
// Issue two you are missing the `#implementation LAMasterViewController`
#implementation LAMasterViewController
// The synthesize is done automatically so getters/setters/ivar are created automatically
// Issue 3: ViewDidLoad isn't a valid selector so ViewDidLoad will never be called
// It is viewDidLoad
-(void)viewDidLoad
{
[super viewDidLoad]; // Issue 4: you missed the call to super
claimReports = [[NSMutableArray alloc] init];
[claimReports addObjectsFromArray:[[LADataModelController getSingleton] getClaimReportsOrderedByIncidentDate]];
}
-(NSArray *)getClaimReportsOrderedByIncidentDate
{ // it returns one record
NSManagedObjectContext *context = [self managedObjectContext];
NSError *error;
NSFetchRequest *request = [[NSFetchRequest alloc] init];
NSEntityDescription *entity = [ NSEntityDescription entityForName:#"ClaimReport" inManagedObjectContext:context];
NSSortDescriptor *sortByIncidentDate = [[NSSortDescriptor alloc] initWithKey:#"dateOfIncident" ascending:NO];
[request setEntity:entity];
[request setSortDescriptors: [NSArray arrayWithObject: sortByIncidentDate]];
NSArray *array = [context executeFetchRequest:request error:&error];
NSLog(#"Array Count %i" ,array.count);
return array;
}
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
{
return claimReports.count;
}
#end
Related
Here, m try to sort the array when refersh the tableview but is crash the application when refresh tableview.
Terminating app due to uncaught exception
'NSInvalidArgumentException', reason: '-[_PFArray
sortUsingDescriptors:]: unrecognized selector sent to instance
0x6080000550c0'
#interface TableViewController () {
NSMutableArray *returnArray;
}
#end
- (void)viewDidAppear:(BOOL)animated {
AppDelegate *appDelegate = (AppDelegate *)[[UIApplication sharedApplication] delegate];
NSManagedObjectContext *context = [appDelegate manageObjectContext];
NSError *error = nil;
NSEntityDescription *entityDescription = [NSEntityDescription entityForName:#"Resgistration" inManagedObjectContext:context];
NSFetchRequest *request = [[NSFetchRequest alloc] init];
[request setEntity:entityDescription];
[request setRelationshipKeyPathsForPrefetching:[NSArray arrayWithObjects:#"Unique",nil]];
returnArray = [[NSMutableArray alloc] init];
returnArray = [context executeFetchRequest:request error:&error];
for(Resgistration* reg in returnArray) {
NSLog(#"%#", reg);
NSLog(#"%#", reg.roshan);
}
[self.tableView reloadData];
UIColor *gray = [UIColor colorWithRed:234/255.0 green:234/255.0 blue:234/255.0 alpha:1.0];
UIRefreshControl *refreshControl = [[UIRefreshControl alloc] init];
refreshControl.attributedTitle = [[NSAttributedString alloc] initWithString:#"Please Wait"];
[refreshControl setBackgroundColor:gray];
[refreshControl addTarget:self action:#selector(sortArray) forControlEvents:UIControlEventValueChanged];
self.refreshControl = refreshControl;
}
- (void)sortArray
{
NSSortDescriptor *sort = [[NSSortDescriptor alloc] initWithKey:#"name" ascending:YES selector:#selector(localizedStandardCompare:)];
NSMutableArray *sortDescriptior = [[NSMutableArray alloc] initWithObjects:sort, nil];
[returnArray sortUsingDescriptors:sortDescriptior]; // crash the app
[self.tableView reloadData];
[self.refreshControl endRefreshing];
}
Please help me to fix this problem. Thank you.
_PFArray is a subclass of NSArray and is immutable. You can use sortUsingDescriptors: on mutable array only, hence the crash.
It looks like somewhere along the code you have assigned instance of CoreData array inside returnArray variable. There you must use mutableCopy method to convert the immutable array to its mutable version, something like this:
returnArray = [<data array fetched from core data> mutableCopy];
Edit after looking at complete code
returnArray = [[context executeFetchRequest:request error:&error] mutableCopy];
Hate to ask this question because there seems to be a bunch of other similar ones and but I still can't see what is wrong with mine after going through most of the answers.
I have two VCs, VC1 contains a list of songs fetched from a music service. VC2 contains a list of songs picked from VC1; VC2 is basically a playlist.
I've set up my entity name and class to match: SCTrack. Here is my SCTrack.h:
#interface SCTrack : NSManagedObject
#property (strong, nonatomic) NSString* title;
#property (strong, nonatomic) NSString* stream_url;
SCTrack.m:
#import "SCTrack.h"
#implementation SCTrack
#dynamic title;
#dynamic stream_url;
-(instancetype) initWithDictionary: (NSDictionary*) SCTrackDict {
self = [self init];
if (self) {
self.title = SCTrackDict[#"title"];
self.stream_url = SCTrackDict[#"stream_url"];
}
return self;
}
+(NSMutableArray *) parseJSONData: (NSData *) JSONData {
NSError* error;
NSMutableArray* SCTrackArray = [NSMutableArray new];
NSArray *JSONArray= [NSJSONSerialization JSONObjectWithData:JSONData options:0 error: &error];
if ([JSONArray isKindOfClass:[NSArray class]]) {
for (NSDictionary* trackDict in JSONArray) {
SCTrack* trackObject = [[SCTrack alloc]initWithDictionary:trackDict];
[SCTrackArray addObject:trackObject];
}
}
return SCTrackArray;
}
#end
My AppDelegate.m
UITabBarController* tabBar = (UITabBarController*) self.window.rootViewController;
SCTrackListVC *SCVC = (SCTrackListVC*) [[tabBar viewControllers]objectAtIndex:2];
SCVC.managedObjectContext = self.managedObjectContext;
return YES;
In my VC1.m
-(void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath {
self.selectedTrack = self.SCTrackList[indexPath.row];
self.selectedTrackRow = indexPath.row;
self.selectedTrack = [[SCTrack alloc]initWithEntity:[NSEntityDescription entityForName:#"SCTrack" inManagedObjectContext:self.managedObjectContext]insertIntoManagedObjectContext:self.managedObjectContext];
NSError* error;
NSManagedObjectContext *context = self.managedObjectContext;
if (![context save:&error]) {
NSLog(#"Error! %#", error);
}
//This is the other way I tried it _selectedTrack = [NSEntityDescription insertNewObjectForEntityForName:#"SCTrack" inManagedObjectContext:self.managedObjectContext];
In my VC2.m
-(NSFetchedResultsController*) fetchedResultController {
if (_fetchedResultController != nil) {
return _fetchedResultController;
}
NSFetchRequest *fetchRequest = [[NSFetchRequest alloc] init];
NSEntityDescription *SCTrackEntity = [NSEntityDescription entityForName:#"SCTrack" inManagedObjectContext:[self managedObjectContext]];
[fetchRequest setEntity:SCTrackEntity];
// NSSortDescriptor *sortDescriptor = [[NSSortDescriptor alloc] initWithKey:#"title" ascending:YES];
// [fetchRequest setSortDescriptors:[NSArray arrayWithObjects:sortDescriptor, nil]];
_fetchedResultController = [[NSFetchedResultsController alloc] initWithFetchRequest:fetchRequest managedObjectContext:[self managedObjectContext] sectionNameKeyPath:nil cacheName:nil];
_fetchedResultController.delegate = self;
return _fetchedResultController;
}
This is the error I get:
CoreData: error: Failed to call designated initializer on NSManagedObject class 'SCTrack'
Very new to Core Data. Been at this for a few hours now. Thanks for pointing me in the right direction.
The designated initialiser for NSManaged Object is initWithEntity: insertIntoManagedObjectContext:.
Change
SCTrack* trackObject = [[SCTrack alloc]initWithDictionary:trackDict];
to
SCTrack *trackObject = [[SCTrack alloc]initWithEntity:entity insertIntoManagedObjectContext:context];
I have a class called SLCollectionViewModel that, if initiated, will fetch the latest entry with Core Data. So far there is no problem with the initial view. The Collection View will display the visible entries correctly (and also shows them in log).
But the problem occurs when I try to scroll the view, or try to call [self.collectionView reloadData] explicitly. The error log is:
-[_PFArray retain]: message sent to deallocated instance 0x8de9ab0
I think my self.viewModel object is deallocated somewhere, but I can’t pinpoint what’s wrong with my approach. Here is my - (void)viewDidLoad method:
- (void)viewDidLoad
{
[super viewDidLoad];
self.viewModel = [SLCollectionViewModel new];
[self.collectionView reloadData];
}
And here is my SLCollectionViewModel class:
- (instancetype) init {
self = [super init];
if(!self) return nil;
_managedObjectContext = [[SLCoreDataStack defaultStack] managedObjectContext];
NSFetchRequest *fetchRequest = [[NSFetchRequest alloc]init];
NSEntityDescription *entity = [NSEntityDescription
entityForName:#"SLPost"
inManagedObjectContext:_managedObjectContext];
[fetchRequest setEntity:entity];
// The _posts in an NSArray that saves all my NSManageObject
// which will be used to populate the collection view.
_posts = [_managedObjectContext executeFetchRequest:fetchRequest
error:nil];
return self;
}
I realize this is my own mistake for not creating the property properly. In my SLCollectionViewModel, the #property for NSArray *posts should use retain instead of assign.
Before:
#property (nonatomic, assign) NSArray *posts;
After:
#property (nonatomic, retain) NSArray *posts;
I am trying to work with core data and already am able to store records of my class "Event.m". An event has a timestamp an a note.
But as i try to read all entries in my table view to display them, I see that every timestamp and note is nil (debug). Strange thing is, that my array I get has the right size (number of entries) and also the table rows are created (only no note si visible).
Here some of my code:
- (void)fetchRecords {
// Define our table/entity to use
NSEntityDescription *entity = [NSEntityDescription entityForName:#"Event" inManagedObjectContext:self.managedObjectContext];
// Setup the fetch request
NSFetchRequest *request = [[NSFetchRequest alloc] init];
[request setEntity:entity];
// Define how we will sort the records
NSSortDescriptor *sortDescriptor = [[NSSortDescriptor alloc] initWithKey:#"timeStamp" ascending:NO];
NSArray *sortDescriptors = [NSArray arrayWithObject:sortDescriptor];
[request setSortDescriptors:sortDescriptors];
// Fetch the records and handle an error
NSError *error;
NSMutableArray *mutableFetchResults = [[self.managedObjectContext executeFetchRequest:request error:&error] mutableCopy];
if (!mutableFetchResults) {
NSLog(#"Error fetching data.");
}
// Save our fetched data to an array
[self setEventArray: mutableFetchResults];
NSLog(#"Records found: %i", [eventArray count]);
}
And this is where I save my object:
- (void) saveEntry: (NSString *) note
{
Event *event = (Event *)[NSEntityDescription insertNewObjectForEntityForName:#"Event" inManagedObjectContext:managedObjectContext];
[event setTimeStamp: [NSDate date]];
[event setNote:#"test"];
NSError *error;
if (![managedObjectContext save:&error]) {
NSLog(#"Could not save entry.");
}
NSLog(#"Entry saved.");
[eventArray insertObject:event atIndex:0];
}
Note that there are two different views, from which I access Core Data.
And this is where I get my MOC in this views:
- (void)viewDidLoad
{
[super viewDidLoad];
AppDelegate *appDelegate = (AppDelegate *)[[UIApplication sharedApplication] delegate];
managedObjectContext = appDelegate.managedObjectContext;
}
Here I was reading, that it could come frome the MOC itself. This is how I hold my variable in SecondViewController.h
#import "Event.h"
#import <CoreData/CoreData.h>
#import "AppDelegate.h"
#interface SecondViewController : UITableViewController {
NSManagedObjectContext *managedObjectContext;
NSMutableArray *eventArray;
}
#property (nonatomic, strong) NSManagedObjectContext *managedObjectContext;
#property (nonatomic, strong) NSMutableArray *eventArray;
- (void) fetchRecords;
#end
And in my .m file I have:
#synthesize managedObjectContext, eventArray;
Then I call fetchRecords.
Any ideas why I have this problem?
I was missing one line of code, concerning the request:
NSFetchRequest *request = [[NSFetchRequest alloc] init];
[request setEntity:entity];
[request setReturnsObjectsAsFaults:NO];
After adding the last line I was able to access all attributes.
I've been following this tutorial to a T, but when I run my app, it fails on launch every time with the following error:
* Terminating app due to uncaught exception 'NSInvalidArgumentException', reason: '+entityForName: nil is not a
legal NSManagedObjectContext parameter searching for entity name
'ArticleInfo''
But I can't figure out what I'm doing wrong that would cause that. As a background for what I've done, I have the datamodel file with an entity called ArticleInfo with a bunch of attributes of different types (some transient, if that's of note). Following the advice of this article I subclassed NSManagedObject as ArticleInfo.
If it's worth noting, I made the preview, wordsInBody and progress transient in the datamodel (and I gave position a default value of 0 in the Data Model Inspector). So in the subclass I made custom getters as follows:
- (NSString *)preview {
[self willAccessValueForKey:#"preview"];
NSString *preview = [self primitiveValueForKey:#"preview"];
[self didAccessValueForKey:#"preview"];
if (self.body.length < 200) {
preview = self.body;
}
else {
preview = [self.body substringWithRange:NSMakeRange(0, 200)];
}
return preview;
}
- (NSNumber *)progress {
[self willAccessValueForKey:#"progress"];
NSNumber *progress = [self primitiveValueForKey:#"progress"];
[self didAccessValueForKey:#"progress"];
if (self.body.length == 0) {
progress = #100;
}
else {
progress = #(100 * [self.position intValue] / [self.wordsInBody intValue]);
}
return progress;
}
- (NSNumber *)wordsInBody {
[self willAccessValueForKey:#"wordsInBody"];
NSNumber *wordsInBody = [self primitiveValueForKey:#"wordsInBody"];
[self didAccessValueForKey:#"wordsInBody"];
if (!wordsInBody) {
__block int numberOfWordsInBody = 0;
NSRange range = {0, self.body.length};
[self.body enumerateSubstringsInRange:range options:NSStringEnumerationByWords usingBlock:^(NSString *substring, NSRange substringRange, NSRange enclosingRange, BOOL *stop) {
numberOfWordsInBody++;
}];
wordsInBody = #(numberOfWordsInBody);
}
return wordsInBody;
}
(Again, not sure if that's relevant.)
Now in my main viewcontroller class (the one that has the tableview I'm using NSFetchedResultsController for) I declared the property, and overrode its getter:
In .h:
#property (nonatomic, strong) NSFetchedResultsController *fetchedResultsController;
In .m:
- (NSFetchedResultsController *)fetchedResultsController {
if (!_fetchedResultsController) {
NSManagedObjectContext *context = [(AppDelegate *)[[UIApplication sharedApplication] delegate] managedObjectContext];
NSEntityDescription *entity = [NSEntityDescription entityForName:#"ArticleInfo" inManagedObjectContext:context];
NSFetchRequest *request = [[NSFetchRequest alloc] init];
request.entity = entity;
NSSortDescriptor *descriptor = [[NSSortDescriptor alloc] initWithKey:#"timeStamp" ascending:NO];
request.sortDescriptors = [NSArray arrayWithObject:descriptor];
request.fetchBatchSize = 20;
NSFetchedResultsController *fetchedResultsController = [[NSFetchedResultsController alloc] initWithFetchRequest:request managedObjectContext:context sectionNameKeyPath:nil cacheName:#"Root"];
_fetchedResultsController = fetchedResultsController;
_fetchedResultsController.delegate = self;
}
return _fetchedResultsController;
}
But again, it keeps giving me that error on app launch. What exactly am I doing wrong that's causing that error?
NSEntityDescription *entity = [NSEntityDescription entityForName:#"ArticleInfo" inManagedObjectContext:context]
Something here is nil. Log context and entity to see which one. Maybe your entity name in your model isn't "ArticleInfo" (typo?) or your app delegate has a problem creating the context.