I would like to get all records for the current month, so I stack two predicates for first date of the month and last day of the month. Since I use CoreData the dates are stored actually as NSTimeInterval.
NSCalendar *calendar = [NSCalendar currentCalendar];
//Get beginning of current month
NSDateComponents *beginningOfCurrentMonthComponents = [calendar components:(NSEraCalendarUnit | NSYearCalendarUnit | NSMonthCalendarUnit | NSDayCalendarUnit) fromDate:date];
[beginningOfCurrentMonthComponents setDay:1];
NSDate *beginningOfCurrentMonthDate = [calendar dateFromComponents:beginningOfCurrentMonthComponents];
//Set a single month to be added to the current month
NSDateComponents *oneMonth = [[NSDateComponents alloc] init];
[oneMonth setMonth:1];
//determine the last day of this month
NSDate *beginningOfNextMonthDate = [calendar dateByAddingComponents:oneMonth toDate:beginningOfCurrentMonthDate options:0];
NSMutableArray *parr = [NSMutableArray array];
[parr addObject:[NSPredicate predicateWithFormat:#"recordDate >= %d", [beginningOfCurrentMonthDate timeIntervalSince1970]]];
[parr addObject:[NSPredicate predicateWithFormat:#"recordDate < %d", [beginningOfNextMonthDate timeIntervalSince1970]]];
//Give me everything from beginning of this month until end of this month
NSPredicate *predicate = [NSCompoundPredicate andPredicateWithSubpredicates:parr];
return [allRecords filteredArrayUsingPredicate:predicate];
Upon returning the filtered array, it crashes with this error message:
2013-10-25 19:09:39.702 [3556:a0b] -[__NSCFNumber timeIntervalSinceReferenceDate]: unrecognized selector sent to instance 0x8911440
2013-10-25 19:09:39.704 [3556:a0b] *** Terminating app due to uncaught exception 'NSInvalidArgumentException', reason: '-[__NSCFNumber timeIntervalSinceReferenceDate]: unrecognized selector sent to instance 0x8911440'
*** First throw call stack:
(
0 CoreFoundation 0x01aa85e4 __exceptionPreprocess + 180
1 libobjc.A.dylib 0x0182b8b6 objc_exception_throw + 44
2 CoreFoundation 0x01b45903 -[NSObject(NSObject) doesNotRecognizeSelector:] + 275
3 CoreFoundation 0x01a9890b ___forwarding___ + 1019
4 CoreFoundation 0x01a984ee _CF_forwarding_prep_0 + 14
5 CoreFoundation 0x01a7e3e3 -[NSDate compare:] + 67
6 Foundation 0x014194fe -[NSComparisonPredicateOperator performPrimitiveOperationUsingObject:andObject:] + 408
7 Foundation 0x014b03de -[NSPredicateOperator performOperationUsingObject:andObject:] + 306
8 Foundation 0x014b016c -[NSComparisonPredicate evaluateWithObject:substitutionVariables:] + 347
9 Foundation 0x014299b6 -[NSCompoundPredicateOperator evaluatePredicates:withObject:substitutionVariables:] + 240
10 Foundation 0x01429845 -[NSCompoundPredicate evaluateWithObject:substitutionVariables:] + 294
11 Foundation 0x014b0009 -[NSPredicate evaluateWithObject:] + 48
12 Foundation 0x014aff89 _filterObjectsUsingPredicate + 418
13 Foundation 0x014afd42 -[NSArray(NSPredicateSupport) filteredArrayUsingPredicate:] + 328
I used this loop also to indicate that the recordDate truly exists within the array:
for (FTRecord *r in allRecords) {
NSLog(#"%f", [r recordDate]);
}
2013-10-25 19:09:35.860 [3556:a0b] 1380582000.000000
2013-10-25 19:09:36.556 [3556:a0b] 1380754800.000000
You chose the "Use scalar properties for primitive data types" option when
creating the managed object subclass, so that the recordDate is represented as
NSTimeInterval in the FTRecord class.
But in a predicate like "recordDate >= 123.45", the left-hand side is stored
as a NSKeyPathExpression, and that uses valueForKeyPath:#"recordDate" to access the property, which returns an NSDate object.
The right-hand side of that predicate is stored as NSConstantValueExpression
with a reference to an NSNumber object.
Therefore an NSDate is compared with an NSNumber, which leads exactly to the
exception that you got.
To fix the problem, you have to compare the property with an NSDate
(which is what #rmaddy initially suggested):
[parr addObject:[NSPredicate predicateWithFormat:#"recordDate >= %#", beginningOfCurrentMonthDate]];
[parr addObject:[NSPredicate predicateWithFormat:#"recordDate < %#", beginningOfNextMonthDate]];
NSPredicate *predicate = [NSCompoundPredicate andPredicateWithSubpredicates:parr];
I tested this and it seems to produce the expected result.
Note however that Core Data uses timeIntervalSinceReferenceDate and
dateWithTimeIntervalSinceReferenceDate to convert between the scalar
values and NSDate, not timeIntervalSince1970.
For solution in Swift 3:
let date = NSDate()
let calender = NSCalendar.current
var beginningOfCurrentMonthComponents = calender.dateComponents([.year, .month, .day], from: date as Date)
beginningOfCurrentMonthComponents.day = 1
let beginningOfCurrentMonthDate = calender.date(from: beginningOfCurrentMonthComponents)
let beginningOfNextMonth = calender.date(byAdding: .month, value: 1, to: beginningOfCurrentMonthDate!)
let pred1 = NSPredicate(format: "date >= %#", beginningOfCurrentMonthDate! as NSDate)
let pred2 = NSPredicate(format: "date < %#", beginningOfNextMonth! as NSDate)
let predicate = NSCompoundPredicate.init(andPredicateWithSubpredicates: [pred1, pred2])
Will add timeIntervalSince1970 with in a predicate itself. It woul
NSPredicate* predicate = PREDICATE(#"(lastUpdatedTime == nil OR lastUpdatedTime.timeIntervalSince1970 <= %f)",currentDate.timeIntervalSince1970);
Related
I'm getting a crash reported on my call to date(from:) on my formatter:
guard let date = DateFormatter.longYearFormatter(timeZone: timeZone).date(from: dateString) else { return }
dateString here is non-optional, and looks like: "2021-11-05T14:03:00GMT-04:00"
This is the longYearFormatter:
static func longYearFormatter(timeZone: TimeZone? = .current) -> DateFormatter {
return DateFormatter(timeZone: timeZone,
format: "yyyy-MM-dd'T'HH:mm:ssZZZZ")
}
Here is the stack trace:
Crashed: com.apple.root.default-qos
EXC_BAD_ACCESS KERN_INVALID_ADDRESS 0x000000000000007b
0
libicucore.A.dylib
icu::Calendar::clear() + 120
1
CoreFoundation
__cficu_ucal_clear + 24
2
CoreFoundation
__cficu_ucal_clear + 24
3
CoreFoundation
CFDateFormatterGetAbsoluteTimeFromString + 392
4
CoreFoundation
CFDateFormatterCreateDateFromString + 104
5
Foundation
getObjectValue + 268
6
Foundation
-[NSDateFormatter getObjectValue:forString:errorDescription:] + 196
7
Foundation
-[NSDateFormatter dateFromString:] + 60
Does anyone know what might be causing this issue? It doesn't happen every time this function is called and seems to be relatively rare.
I've tried running my simulator in different languages, regions, time zones, 24 vs 12hr time, etc, and I can't repro it myself.
Thanks a lot in advance!
UPDATE
Here is what the custom DateFormatter init looks like:
public extension DateFormatter {
private convenience init(timeZone: TimeZone?,
format: String) {
self.init()
self.timeZone = timeZone ?? .current
locale = Locale(identifier: "en_US_POSIX")
dateFormat = format
}
Hello I am newbie to ios and objective c. I am working on a demo. In that I am having a for loop for adding values to NSMutableArray. Now this array will be added to NSMutableDictionary. Now inside this for loop i am having an if condition for checking string. I am adding objects to the array in "if" part and want to create a new Array when condition fail. I have tried as below, but in my Dictionary only one array saved. Can anyone help me where i have made mistake?
NSMutableArray *listingCommonArr,sectionTitles,eventDate;
int j=0;
NSString *dt;
NSMutableArray *sectionArray = [[NSMutableArray alloc]init];
sectionTitles = [orderedSet array];
NSLog(#"===listing common array---%d",[listingCommonArr count]);
sectionData = [[NSMutableDictionary alloc]init];
for(int i =0;i<[listingCommonArr count];i++)
{
dt = [[listingCommonArr objectAtIndex:i] objectForKey:#"start_date"];
NSLog(#"====my date at place===%# & my date is====%#",[sectionTitles objectAtIndex:j],dt);
for (int i =0; i < [listingCommonArr count]; i++) {
dt = [[listingCommonArr objectAtIndex:i] objectForKey:#"start_date"];
if ([(NSString *)[sectionTitles objectAtIndex:j] isEqualToString:dt]) {
[sectionArray addObject:listingCommonArr[i]];
//sectionData = #{dt : sectionArray}; // <-- PAY ATTENTION ON THIS LINE, PLEASE
//[sectionData setValue:sectionArray forKey:dt];
sectionData = [#{dt : sectionArray} mutableCopy];
} else {
[sectionData setObject:#"New value" forKey:#"string"];
sectionArray = [[NSMutableArray alloc]init];
j++;
}
}
NSLog(#"====my section DAta is==%#",sectionData);
}
errorTrace
-[__NSDictionaryI setObject:forKey:]: unrecognized selector sent to instance 0x7fc6547832a0
2015-12-18 19:39:41.387 iPhoneExploreCayman[41719:13822557] *** Terminating app due to uncaught exception 'NSInvalidArgumentException', reason: '-[__NSDictionaryI setObject:forKey:]: unrecognized selector sent to instance 0x7fc6547832a0'
*** First throw call stack:
(
0 CoreFoundation 0x000000010a551c65 __exceptionPreprocess + 165
1 libobjc.A.dylib 0x000000010a1e8bb7 objc_exception_throw + 45
2 CoreFoundation 0x000000010a5590ad -[NSObject(NSObject) doesNotRecognizeSelector:] + 205
3 CoreFoundation 0x000000010a4af13c ___forwarding___ + 988
4 CoreFoundation 0x000000010a4aecd8 _CF_forwarding_prep_0 + 120
5 iPhoneExploreCayman 0x000000010604a48c -[iPhoneECListingView GetEventListingData:] + 1884
6 iPhoneExploreCayman 0x000000010605644b -[iPhoneECListingView EventListStart] + 1323
7 iPhoneExploreCayman 0x0000000106055a98 -[iPhoneECListingView EventClicked:] + 2312
8 UIKit 0x0000000108832d62 -[UIApplication sendAction:to:from:forEvent:] + 75
9 UIKit 0x000000010894450a -[UIControl _sendActionsForEvents:withEvent:] + 467
10 UIKit 0x00000001089438d9 -[UIControl touchesEnded:withEvent:] + 522
11 UIKit 0x000000010887f958 -[UIWindow _sendTouchesForEvent:] + 735
12 UIKit 0x0000000108880282 -[UIWindow sendEvent:] + 682
13 UIKit 0x0000000108846541 -[UIApplication sendEvent:] + 246
I'm highly confused about what you actually want to do here, but... after your comments I have completely updated my answer removing the previous assumptions about what actually is supposed to happen.
it seems to me you want(ed) to group a series of words by the first letter. you have posted a list of animals as example, so I worked with those.
you defined the expected output:
NSDictionary *_expectedOutput = #{#"A" :#[#"Affrican cat", #"Assian cat", #"Alsesian fox"], #"B" : #[#"Bear", #"Black Swan", #"Buffalo"], #"C" : #[#"Camel", #"Cockatoo"], #"D" : #[#"Dog", #"Donkey"], #"E" : #[#"Emu"], #"R" : #[#"Rhinoceros"], #"S" : #[#"Seagull"], #"T" : #[#"Tasmania Devil"], #"W" : #[#"Whale", #"Whale Shark", #"Wombat"]};
as you can see my input array is just an unsorted list of random names of animals from your sample output:
NSArray *_input = #[#"Whale Shark", #"Tasmania Devil", #"Affrican cat", #"Bear", #"Seagull", #"Black Swan", #"Whale", #"Wombat", #"Camel", #"Rhinoceros", #"Cockatoo", #"Buffalo", #"Assian cat", #"Alsesian fox", #"Emu"];
I'm working with that input array and I am going to group them by their first letter and build up the output:
NSMutableDictionary *_output = [NSMutableDictionary dictionary];
[[_input sortedArrayUsingSelector:#selector(compare:)] enumerateObjectsUsingBlock:^(NSString * _Nonnull obj, NSUInteger idx, BOOL * _Nonnull stop) {
NSString *_firstLetter = (obj.length > 0 ? [obj substringToIndex:1] : nil);
if (_firstLetter) {
NSMutableArray *_list = [_output valueForKey:_firstLetter];
if (_list == nil) {
_list = [NSMutableArray arrayWithObject:obj];
[_output setValue:_list forKey:_firstLetter];
} else {
[_list addObject:obj];
}
}
}];
that is pretty much it, if you'd log the _output, you can see on the console that is identical to your _expectedOutput collection.
you have sectionData = #{dt : sectionArray}; outside of the if section.
So on each iteration you dictionary will be recreated.
As I think you need the Mutable dictionary for sectionData, and move that string under "else" section
1 initialize mutable dictionary somewhere before the loop:
sectionData = [NSMutableDictionary new];
2 adding current array to dictionary must be under "else" block, also as I understand, you should create new sectionArray:
else
{
[sectionData setObject: sectionArray forKey:dt];
sectionArray = [[NSMutableArray alloc]init];
}
Every time you call sectionData = #{dt : sectionArray}; or anything sectionData is set other values are remove from dictionary and only last pair stays.(you are initializing dictionary every time you call setObject: forKey:)
If you want to create mutable dictionary and add objects to it you should do:
NSMutableDictionary *d2 = [NSMutableDictionary new];//sectionData in your case
[d2 addEntriesFromDictionary:#{#"b":#"c"}];//new key value pair added to d2
[d2 addEntriesFromDictionary:#{#"c":#"d"}];//new key value pair added to d2
[d2 addEntriesFromDictionary:#{#"d":#"e"}];//new key value pair added to d2
While validating for null values ,the crashes with
-[__NSCFDictionary setObject:forKey:]: mutating method sent to immutable object
When I am using all mutable type.(crashing only in iOS 9,other versions of my app in Appstore are working fine)
Can anyone suggest me how to handle this at null condition to setValue for key.
NSMutableArray *tempelementsArray=[[NSMutableArray alloc]init];
if(![[catDic objectForKey:#"menuElementList"] isEqual:#""]){
tempelementsArray = [catDic objectForKey:#"menuElementList"];
if(tempelementsArray != nil && [tempelementsArray count]>0)
{
for (NSInteger j=0; j<tempelementsArray.count; j++) {
NSMutableDictionary *elementDic = [[NSMutableDictionary alloc]init];
elementDic = [tempelementsArray objectAtIndex:j];
[elementDic enumerateKeysAndObjectsUsingBlock:^(id key, id obj, BOOL *stop) {
if ([obj isKindOfClass:[NSNull class]])
{
[elementDic setValue:#"" forKey:key];//App crashes here when one of the value is NULL
}
} ];
}
with below crash:
*** Terminating app due to uncaught exception NSInternalInconsistencyException', reason: '-[__NSCFDictionary setObject:forKey:]: mutating method sent to immutable object'
*** First throw call stack:
(
0 CoreFoundation 0x00df3a94 __exceptionPreprocess + 180
1 libobjc.A.dylib 0x0051be02 objc_exception_throw + 50
2 CoreFoundation 0x00df39bd +[NSException raise:format:] + 141
3 CoreFoundation 0x00d0ed68 -[__NSCFDictionary setObject:forKey:] + 104
4 Foundation 0x001051ba -[NSMutableDictionary(NSKeyValueCoding) setValue:forKey:] + 68
5 coreDataMenuSample 0x000481d9 __33-[ViewController SaveToCoredata:]_block_invoke188 + 217
6 CoreFoundation 0x00df2849 ____NSDictionaryEnumerate_block_invoke417 + 41
7 CoreFoundation 0x00cd5452 CFBasicHashApply + 130
8 CoreFoundation 0x00d12481 __NSDictionaryEnumerate + 273
9 CoreFoundation 0x00d122ed -[NSDictionary enumerateKeysAndObjectsWithOptions:usingBlock:] + 45
10 CoreFoundation 0x00d12235 -[NSDictionary enumerateKeysAndObjectsUsingBlock:] + 53
11 coreDataMenuSample 0x00043e71 -[ViewController SaveToCoredata:] + 6481
12 coreDataMenuSample 0x0004239d -[ViewController viewDidLoad] + 893
13 UIKit 0x0133fd74 -[UIViewController _sendViewDidLoadWithAppearanceProxyObjectTaggingEnabled] + 44
14 UIKit 0x013448c2 -[UIViewController loadViewIfRequired] + 1556
)
libc++abi.dylib: terminating with uncaught exception of type NSException
I have even checked this similar issue Strange “mutating method sent to immutable object” error when adding an object to a mutable array
Saving CLLocation error: Mutating method sent to immutable object
Look at your code, I think the problem is this line
NSMutableDictionary *elementDic = [[NSMutableDictionary alloc]init];
elementDic = [tempelementsArray objectAtIndex:j];
tempelementsArray contains an instance of NSDictionary instead of NSMutableDictionary. Change to this code will help:
NSMutableDictionary *elementDic = [[NSMutableDictionary alloc]initWithDictionary:tempelementsArray[j]];
There are several things wrong with your code :
First it's not because you initialise a variable with a mutable object that subsequent initialisations will be converted to mutable objects. So when you do :
NSMutableDictionary *elementDic = [[NSMutableDictionary alloc]init];
elementDic = [tempelementsArray objectAtIndex:j];
elementDic contains whatever was in the array at index j, so in this case probably an immutable object. You have to make mutable copies of your objects if you want them mutable.
Second, you can't mutate a dictionary while you enumerate it (which is what you are trying to do here). You have to make a mutable copy and mutate the copy while you enumerate the original.
Third, if you expect [catDic objectForKey:#"menuElementList"] to be an array, why do you test if it's equal to an empty string ?!
Here is a fixed version of your code (with modern obj-C syntax which is much easier to read by the way)
NSDictionary *catDic = ...
NSArray *tempElementsArray = catDic[#"menuElementList"];
NSMutableArray *mutableTempElementsArray = [NSMutableArray arrayWithCapacity:tempElementsArray.count];
if (![tempElementsArray isEqual:#""] && tempElementsArray != nil && tempElementsArray.count > 0)
{
for (NSUInteger j = 0; j < tempElementsArray.count; j++)
{
NSDictionary *elementsDic = tempElementsArray[j];
NSMutableDictionary *mutableElementsDic = [NSMutableDictionary dictionaryWithCapacity:elementsDic.count];
[elementsDic enumerateKeysAndObjectsUsingBlock:^(id key, id obj, BOOL *stop) {
if (obj == [NSNull null]) {
obj = #"";
}
mutableElementsDic[key] = obj;
}];
[mutableTempElementsArray addObject:mutableElementsDic];
}
}
NSMutableDictionary *mutableCatDic = [NSMutableDictionary dictionaryWithDictionary:catDic];
mutableCatDic[#"menuElementList"] = mutableTempElementsArray;
I'm curious to know why I'm receiving an exception on this code and as to what this exception means. When I remove the for loop, as indicated below, it works fine. When I include it, I get an exception.
-(void) loadVenues {
NSString *latLon = #"34.0500, -118.2500"; // approximate latLon of The Mothership (a.k.a Apple headquarters)
NSString *clientID = kCLIENTID;
NSString *clientSecret = kCLIENTSECRET;
//34.0500° N, 118.2500
NSDictionary *queryParams = #{#"ll" : latLon,
#"client_id" : clientID,
#"client_secret" : clientSecret,
#"categoryId" : #"4bf58dd8d48988d1e0931735",
#"v" : #"20140118"};
[[RKObjectManager sharedManager] getObjectsAtPath:#"/v2/venues/search"
parameters:queryParams
success:^(RKObjectRequestOperation *operation, RKMappingResult *mappingResult) {
_venues = mappingResult.array;
NSMutableArray *temp = [(NSArray*)_venues mutableCopy];
for (int i =0; i < _venues.count; i++)
{
NSUInteger index = 0;
for (int j = 0; j <_venues.count; j++)
{
//((MyObjectType *) [myArray objectAtIndex:0]).intProperty = 12345;
if(((Venue *) [temp objectAtIndex:index]).location.distance > 0)
{
Venue *holder= [temp objectAtIndex:index];
[temp removeObjectAtIndex:index];
NSUInteger indexer = index +1;
[temp addObject:[temp objectAtIndex:indexer]];
//replace a with a+1
[temp replaceObjectAtIndex:indexer withObject:holder];
//[temp addObject:holder atIndex: index+1];
//a[i+1]=holder;
index++;
}
else {index++;}
}
}
_venues = temp;
[self.tableView reloadData];
}
failure:^(RKObjectRequestOperation *operation, NSError *error) {
NSLog(#"What do you mean by 'there is no coffee?': %#", error);
}
];
}
I'm getting an exception at this line:
[temp addObject:[temp objectAtIndex:indexer]];
And I'm not sure why.
Here is the error:
2014-07-02 21:43:12.536 CoffeeKit[63581:60b] I restkit:RKLog.m:33 RestKit logging initialized...
2014-07-02 21:43:12.655 CoffeeKit[63581:60b] I restkit.network:RKObjectRequestOperation.m:150 GET 'https://api.foursquare.com/v2/venues/search?categoryId=4bf58dd8d48988d1e0931735&client_id=ZE5VJOPEO1PP3NDFVCM3O2ZUXWDDJRB20XDDGH3OETBKOVZB&client_secret=5LGY2CEASBQZQS5P0LYFICWDKMDOHJJ00F3G24LT4J4DX4X3&ll=34.0500%2C%20-118.2500&v=20140118'
2014-07-02 21:43:13.085 CoffeeKit[63581:f03] I restkit.network:RKObjectRequestOperation.m:220 GET 'https://api.foursquare.com/v2/venues/search?categoryId=4bf58dd8d48988d1e0931735&client_id=CLIENT_ID&client_secret=CLIENT_SECRET&ll=34.0500%2C%20-118.2500&v=20140118' (200 OK / 30 objects) [request=0.4171s mapping=0.0128s total=0.4486s]
2014-07-02 21:43:13.087 CoffeeKit[63581:60b] *** Terminating app due to uncaught exception 'NSRangeException', reason: '*** -[__NSArrayM objectAtIndex:]: index 29 beyond bounds [0 .. 28]'
*** First throw call stack:
(
0 CoreFoundation 0x00000001023a0495 __exceptionPreprocess + 165
1 libobjc.A.dylib 0x00000001020ff99e objc_exception_throw + 43
2 CoreFoundation 0x0000000102346745 -[__NSArrayM objectAtIndex:] + 213
3 CoffeeKit 0x0000000100002efa __34-[MasterViewController loadVenues]_block_invoke + 602
4 CoffeeKit 0x000000010009310b __66-[RKObjectRequestOperation setCompletionBlockWithSuccess:failure:]_block_invoke244 + 91
5 libdispatch.dylib 0x0000000102c9a851 _dispatch_call_block_and_release + 12
6 libdispatch.dylib 0x0000000102cad72d _dispatch_client_callout + 8
7 libdispatch.dylib 0x0000000102c9d3fc _dispatch_main_queue_callback_4CF + 354
8 CoreFoundation 0x00000001023fe289 __CFRUNLOOP_IS_SERVICING_THE_MAIN_DISPATCH_QUEUE__ + 9
9 CoreFoundation 0x000000010234b854 __CFRunLoopRun + 1764
10 CoreFoundation 0x000000010234ad83 CFRunLoopRunSpecific + 467
11 GraphicsServices 0x00000001033e2f04 GSEventRunModal + 161
12 UIKit 0x0000000100cace33 UIApplicationMain + 1010
13 CoffeeKit 0x0000000100003733 main + 115
14 libdyld.dylib 0x0000000102efe5fd start + 1
15 ??? 0x0000000000000001 0x0 + 1
)
libc++abi.dylib: terminating with uncaught exception of type NSException
(lldb)
As you can see from your stack, your error is occurring because you attempt to access the object at index 29, which is outside of the array's bounds:
* -[__NSArrayM objectAtIndex:]: index 29 beyond bounds [0 .. 28]
In your case, it's probably arising from this line:
NSUInteger indexer = index +1;
[temp addObject:[temp objectAtIndex:indexer]];
Which I'm assuming you're using to get the value of the next object. Once the array reaches the 28th object, however, you attempt to access the imaginary 29th object.
Bottom line: refactor your code so that you only access objects that exist.
You are getting out of bounds by accessing the NEXT element in the array, 2 things. you can directly use 'j' to access the element (instead of index, so u dont have to increment it urself) and you can change your for from
for (int j = 0; j <_venues.count; j++)
to
for (int j = 0; j <_venues.count-1; j++)
By the way if all you need is sorting, NSMutableArray offers many options:
Rearranging Content
– exchangeObjectAtIndex:withObjectAtIndex:
– sortUsingDescriptors:
– sortUsingComparator:
– sortWithOptions:usingComparator:
– sortUsingFunction:context:
– sortUsingSelector:
I have a bit of code in my application that generates a Core Data Model and populates it with a set of NSEntityDescriptions. Each of these entity descriptions then have an arbitrary number of NSPropertyDescriptions allocated for them. These properties are a combination of NSAttributeDescriptions and NSRelationshipDescriptions. All relationships are matched with an existing relationship and they are set as inverse of one another using setInverseRelationship:.
The attribute properties work fine, and to-many relationships work fine; I have tested that thoroughly. The issue seems to be with NSRelationshipDescriptions that have a maxCount value of 1, meaning the property description returns a isToMany value of NO. When the inverseRelationship property is set for these type of relationship, Core Data crashes when I try to save any object that utilizes that relationship with the error:
2013-11-09 11:17:15.068 Directory[1344:5c03] -[NSManagedObject count]: unrecognized selector sent to instance 0x9643820
2013-11-09 11:17:15.074 Directory[1344:5c03] *** Terminating app due to uncaught exception 'NSInvalidArgumentException', reason: '-[NSManagedObject count]: unrecognized selector sent to instance 0x9643820'
*** First throw call stack:
(
0 CoreFoundation 0x01f9f5e4 __exceptionPreprocess + 180
1 libobjc.A.dylib 0x01d228b6 objc_exception_throw + 44
2 CoreFoundation 0x0203c903 -[NSObject(NSObject) doesNotRecognizeSelector:] + 275
3 CoreFoundation 0x01f8f90b ___forwarding___ + 1019
4 CoreFoundation 0x01f8f4ee _CF_forwarding_prep_0 + 14
5 CoreData 0x0083c128 -[NSSQLCore _knownOrderKeyForObject:from:inverseToMany:] + 200
6 CoreData 0x00773080 -[NSSQLCore _populateRowForOp:withObject:] + 1120
7 CoreData 0x00789157 -[NSSQLCore recordValuesForInsertedObject:] + 71
8 CoreData 0x00771e8d -[NSSQLCore recordChangesInContext:] + 685
9 CoreData 0x00770c55 -[NSSQLCore saveChanges:] + 565
10 CoreData 0x0073d88c -[NSSQLCore executeRequest:withContext:error:] + 412
11 CoreData 0x0073d380 -[NSPersistentStoreCoordinator executeRequest:withContext:error:] + 4704
12 CoreData 0x00769ffc -[NSManagedObjectContext save:] + 764
13 Directory 0x0002f2bf -[ETDatabaseController save] + 111
14 Directory 0x0002ba9c -[ETDataLoader performImportWithData:] + 10284
The insinuation I am gathering from this is it is considering the inverse relationship to be a to-many relationship when that is not the case. According to my assertions, what I know about each of my relationships is:
relationDescription.inverseRelationship != nil
[relationDescription.inverseRelationship.inverseRelationship isEqual:relationDescription]
I have tested this by creating the model and populating it with a small set of sample data. Currently, objects that have any sort of attributes, to-many relationships (with/without inverse), and to-one relationship (without inverse) work consistently. The issues comes when I try to have a to-one relationship with an inverse relationship.
This seems a bit convoluted so let me know if I need to clarify anything better. Thanks!
Edit 1:
The relationship creation is done in two steps, first it creates all the relationships, then it establishes the inverse of each relationship by using a lookup.
NSRelationshipDescription *description = [[NSRelationshipDescription alloc] init];
[description setName:self.name];
[description setDestinationEntity:entity];
[description setMaxCount:(isToMany ? 0 : 1)];
[description setMinCount:0];
[description setOrdered:YES]; // See you in a minute
later...
NSEntityDescription *inverseEntity = newRelationship.destinationEntity;
NSRelationshipDescription *inverseRelationDescription = [inverseEntity relationshipsByName][inverse.name];
if (inverseRelationDescription) {
inverseRelationDescription.inverseRelationship = newRelationship;
newRelationship.inverseRelationship = inverseRelationDescription;
} else if ([inverse.name isEqualToString:relation.name]) {
newRelationship.inverseRelationship = newRelationship;
}
Well, I guess writing Edit 1 made something click. So from what it looks like, if you call setOrdered: on a NSRelationDescription that is meant to be a to-one relationship, the internals of Core Data automatically considers it a to-many relationship, despite the conflict with the maxCount being one.
I fixed this by changing the creation code for the relationship from:
NSRelationshipDescription *description = [[NSRelationshipDescription alloc] init];
[description setName:self.name];
[description setDestinationEntity:entity];
[description setMaxCount:(isToMany ? 0 : 1)];
[description setMinCount:0];
[description setOrdered:YES];
to:
NSRelationshipDescription *description = [[NSRelationshipDescription alloc] init];
[description setName:self.name];
[description setDestinationEntity:entity];
[description setMaxCount:(isToMany ? 0 : 1)];
[description setMinCount:0];
if (isToMany)
[description setOrdered:YES];