In the process of teaching myself objective C and the iOS SDK. I've gotten to a point where I think I understand what I'm doing yet I'm hitting a roadblock that I don't see a way past. I've created a core data model with multiple entities, with one master entity having a couple many to one relationships with a couple other entities. And I'm able to work with the main entity just fine, as well as any object that are on a one to one relationship with the main entity. However, when I try to add an entity to one of the NS Set based entities using the core data generated accessors, I am getting an unrecognized selector error: Here's some code to confuse things even more:
Weapon *tempWeapon = [NSEntityDescription insertNewObjectForEntityForName:#"Weapon" inManagedObjectContext:inputContext];
NSArray *tempWeaponStats = [inputMech getMechWeaponStats:tempEquipName];
tempWeapon.weaponName = tempEquipName;
tempWeapon.weaponDisplayName = tempEquipDisplayName;
tempWeapon.weaponLocation = tempEquipLocation;
tempWeapon.weaponType = tempEquipType;
tempWeapon.weaponCritSize = tempEquipSize;
tempWeapon.weaponHeat = [[NSNumber alloc] initWithInt:[[tempWeaponStats objectAtIndex:0] intValue]];
tempWeapon.weaponDamage = [[NSNumber alloc] initWithInt:[[tempWeaponStats objectAtIndex:1] intValue]];
tempWeapon.weaponRangeMin = [[NSNumber alloc] initWithInt:[[tempWeaponStats objectAtIndex:2] intValue]];
tempWeapon.weaponRangeShort = [[NSNumber alloc] initWithInt:[[tempWeaponStats objectAtIndex:3] intValue]];
tempWeapon.weaponRangeMed = [[NSNumber alloc] initWithInt:[[tempWeaponStats objectAtIndex:4] intValue]];
tempWeapon.weaponRangeLong = [[NSNumber alloc] initWithInt:[[tempWeaponStats objectAtIndex:5] intValue]];
NSLog(#"Adding to the weapon list %#", tempWeapon.weaponName);
[inputMech insertObject:tempWeapon inWeaponListAtIndex:(NSUInteger)0];
When running this code, I get the following error:
2011-08-24 01:49:52.643 DigitalRecordSheet[12947:f203] -[Mech insertObject:inWeaponListAtIndex:]: unrecognized selector sent to instance 0x718bbe0
Now, inputMech is of the master entity type I mentioned earlier. Here's the core data generated selector from Mech.h:
(void)insertObject:(Equipment *)value inEquipmentListAtIndex:(NSUInteger)idx;
As far as I can tell, I am sending the message properly, but it doesn't work. Basically, I want to have one Mech that has a list of multiple weapons. Now, am I wrong in how I am doing this? I am assuming that I have to create a new weapon object first, set it up how I want, then add it to the Mech's weaponList NSSet object. But the core data selectors aren't working so I have to assume I am doing something wrong. Any advice here would be appreciated as I've dug through multiple books and guides and none of them really go in to depth about the process of handling these types of relationships... Thanks in advance for any advice. Cheers,
J^2
This error is not caused by the configuration of your data model. The:
unrecognized selector sent to instance
… error always results from having the wrong class in a variable for some reason.
In this case, the class Mech in the inputMech variable doesn't have a:
insertObject:inEquipmentListAtIndex:
...method.
The most likely cause of your error is that in the course of writing all this, you've created multiple copies of the Mech class files. One is configured for a ordered relationships and the other isn't. You are actually compiling with the one that does not.
In your second problem, you are simply sending a NSSet object to a method parameter that expects a Weapon object instead. You are confusing addWeaponListObject with addWeaponListObjects:
take the following steps
1) created a new version of the Core Data Model via Xcode.
2) Fix the relationship (added a new relationship between the two.enter link description here)
3) re-created the NSManagedObject subclass
Related
I want to send a PFObject directly over a push notification. I send the Parse object directly inside the push (e.g. with a custom key "arg") but I couldn't figure out how to construct a real PFObject from the received data. Received data is (obviously) an NSDictionary, with all the keys (object ID, created at, ACLs etc) available. How do I convert it to a PFObject instance?
I need a real way to construct a PFObject with the available data, so don't come with obvious solutions like "send the object ID and then fetch that object at client with Parse's methods." etc. I already know that obvious solution, but it's time/bandwidth/quota inefficient as it requires a new query, while I can have everything I need in that query anyway.
I'm looking for an automatic way, if any. I am targeting iOS 8 so maximum push payload size is also not an issue (2KB is more than enough for my case).
UPDATE: I've tried [PFObject objectWithClassName:#"MyClassName" dictionary:receivedDictionaryObject]; but no avail. It just does not work, the fields are nil even though the dictionary has all the data directly from Parse itself.
I think you can use something like this
+ (PFObject *)objectFromDictionary:(NSDictionary *)dictionaryFromPush{
PFObject *theObject = [[PFObject alloc] initWithClassName:#"MyClassName"];
for( NSString *keys in [dictionaryFromPush allKeys] )
{
[theObject setObject:[dictionaryFromPush objectForKey:keys] forKey:keys];
}
return theObject;
}
This is an untested code but im pretty sure will give you and idea of my point, to get the NSDcitionary from the Push and sent it to this method to be able to convert it to a PFObject
Hope this help
this is my first stack post so please be constructive when reviewing my posting technique!
Basically, my problem is that I have an array of custom objects that I need to share with a today extension. The objects represent tasks in a to-do list, and their properties are used to store info about each task (name, location, dueDate, thumbnail, etc). The objects are stored in an array which is used to populate my to-do list. All I want to do is pass this array to my widget so that I can populate a second tableview which will act as a condensed version of the first (for the widget view).
I should point out that my widget is properly set up, as in I have properly linked it and the containing app together in 'groups'. I have also successfully used NSUserDefaults to pass an array of NSStrings to the widget, however, when I try to pass the array of objects to the widget, it crashes and my log reads:
*** Terminating app due to uncaught exception 'NSInvalidUnarchiveOperationException', reason: '*** -[NSKeyedUnarchiver decodeObjectForKey:]: cannot decode object of class (Xitem)'
I understand that this crash is related to archiving the object (Xitem), which seems to be a necessary step towards saving custom objects in NSUserDefaults. However, I have tested saving/loading the array within the same class of the containing app, and that works fine! (code below)
NSData *encodedObject = [NSKeyedArchiver archivedDataWithRootObject:self.Xitems];
NSUserDefaults *defaults = [[NSUserDefaults alloc] initWithSuiteName:#"group.AaronTest"];
[defaults setObject:encodedObject forKey:#"myArray"];
[defaults synchronize];
NSUserDefaults *defaults2 = [[NSUserDefaults alloc] initWithSuiteName:#"group.AaronTest"];
NSData *encodedObject2 = [defaults2 objectForKey:#"myArray"];
NSArray *array2 = [NSKeyedUnarchiver unarchiveObjectWithData:encodedObject2];
for (Xitem *t in array2){
NSLog(#"*****%#*****", t.itemName);
}
Okay so as explained, the above code works as expected. However, when i insert the second 'unarchiver' half of this code into my today widget, i get the aforementioned error. Below is my code to show how I encode/decode the object (it may be worth noting that this object was created for the simplicity of my debugging and only contains a NSString property):
Xitem.h
#import <UIKit/UIKit.h>
#import <Foundation/Foundation.h>
#interface Xitem : NSObject <NSCoding>
{
NSString *itemName;
}
-(void)encodeWithCoder:(NSCoder*)encoder;
-(id)initWithCoder:(NSCoder*)decoder;
#property NSString *itemName;
#end
Xitem.m
#import "Xitem.h"
#implementation Xitem
#synthesize itemName;
-(void)encodeWithCoder:(NSCoder*)encoder
{
[encoder encodeObject:self.itemName forKey:#"iName"];
}
-(id)initWithCoder:(NSCoder*)decoder
{
self = [super init];
self.itemName = [decoder decodeObjectForKey:#"iName"];
return self;
}
#end
I could also post my widget and containing app code, but it doesn't differ from the first set of code i posted (apart from the renamed variables such as 'defaults2'). I should point out that I really have exhausted resources while trying to solve this problem, but the fact that using NSKeyedArchiver solely in the containing app works, has left me stumped.
I realise that this post is very similar to my own problem, but the author decides to opt for a workaround, whereas I would actually like to know why this doesn't work. I'm a new developer and I'm doing my best to pickup on the best working practices so any advice would be greatly appreciated.
I think it's also possible to replace my object (class) with an NSDictionary? However I would like to avoid this if possible because it would cause many conflicts in the main app, but obviously if that is the correct method I will tackle that problem. On a side note, if a dictionary would be better than an object for my requirements (to-do list with properties of UIImage, CLLocation, etc) for any other reasons (memory or accessibility for example) please do elaborate and help me to understand why!
Many thanks for anyones time :)
Okay so I just fixed this. Incase anyone has the same problem, go to: 'Targets' > 'Widget' > 'Build Phases' > 'Compile Sources' > add custom class there (Xitem.m)
This question already has an answer here:
Checking for duplicates when importing to CoreData
(1 answer)
Closed 8 years ago.
I have a bunch of NSManagedObjects that are created from a JSON file online. Currently, I am creating them all each time the app launches (not ideal).
What is the best way to check to see if the objects are already there before I try to create them?
if I do [self saveContext] it seems to work, but as I don't know how to check if they are already loaded, it ends up duplicating everything.
Obviously, I am relatively new to Core Data and seem to be missing a key concept.
[EDIT] After reading more and more about where and when to load this many objects into Core Data, it looks like pre-loading the data is the best option for me (the data is static and will likely only be update a few times per year).
I chose not to use the "find or create pattern" as I assumed it would be more expensive given the number of objects that need to be checked/created and would like to save learning about background queues for next time ;)
I was then having trouble getting the sqlite file to work, and solved it by saving the context after each object was created, rather than once after all the objects were loaded.
The way this is handled usually in my experience is via one of the two options:
You first check if the item exists, and if it does, then you update it, else insert it. Here's a sample of what I have used in the past for a vouchers model:
Voucher *newObject = nil;
NSFetchRequest *request = [NSFetchRequest fetchRequestWithEntityName:#"Voucher"];
request.predicate = [NSPredicate predicateWithFormat:#"voucher_id = %#",[dictionary objectForKey:#"voucher_id"]];
NSError *error = nil;
NSArray *matches = [context executeFetchRequest:request error:&error];
if ([matches count] == 0 ){
newObject = [NSEntityDescription insertNewObjectForEntityForName:#"Voucher" inManagedObjectContext:context];
newObject.number = [json_dictionary objectForKey:#"number"];
newObject.valid_from = [json_dictionary objectForKey:#"valid_from"];
newObject.valid_to = [json_dictionary objectForKey:#"valid_to"];
}
else {
newObject = [matches lastObject];
newObject.number = [json_dictionary objectForKey:#"number"];
newObject.valid_from = [json_dictionary objectForKey:#"valid_from"];
newObject.valid_to = [json_dictionary objectForKey:#"valid_to"];
newObject.voucher_id = [json_dictionary objectForKey:#"voucher_id"];
}
return newObject;
The other way is to select all, put into an NSOrderedSet, and then run a comparison, and only insert if not in the set.
If you look at "Core Data Performance Optimization and Debugging" on this page https://developer.apple.com/wwdc/videos/ , it's got a great explanation of this
If you haven't worked on it before, the learning curve might be a bit steep. But one good way is to use RestKit.
https://github.com/RestKit/RestKit/wiki/Object-mapping#core-data
Ray Wenderlich has a detailed tutorial on Core Data that show you how to do it step by step: (make sure to turn on Google Translate)
In response to your question under comments, here it is:
create a new file and choose to create datamodel (under Core Data)
add your entities - entities are what you declared as class data models. Note that I have Location, Marker, and Village because I have created those as classes (Location.m/.h, etc)]
Add attributes (properties) associated with those entities.
http://i.stack.imgur.com/wOUvF.png
http://i.stack.imgur.com/5AJGZ.png
im having a hard time with what it seems a very simple task. Im trying to create an entity (LVUser) and calling a custom method implemented in the class previously mentioned. Every attempt i made returns me an unrecognised selector sent to instance error. Im using MagicalRecord in my project but also tried with CoreData raw methods without luck anyway.
First Attempt
LVUser* user = [LVUser MR_createEntity];
[user signInWithDelegete:self];
Second Attempt
LVUser* user = [LVUser MR_createInContext:[RKObjectManager sharedManager].managedObjectStore.mainQueueManagedObjectContext];
[user signInWithDelegete:self];
Third Attempt
LVUser* user = [[NSManagedObject alloc] initWithEntity: [LVUser MR_entityDescription] insertIntoManagedObjectContext:[RKObjectManager sharedManager].managedObjectStore.mainQueueManagedObjectContext];
[user signInWithDelegete:self];
The last example gives me this warning
Incompatible pointer types initializing 'LVUser *' with an expression of type 'NSManagedObject *'
I don't get what i'm doing wrong, they are compatible pointer types indeed.
I tried deleting Xcode Derived Data and clean & build.
I found a strange behavior with NSString. I tried to run the below code and noticed this.
NSString *str = [[NSString alloc] initwithstring : #"hello"];
[str release];
NSLog(#" Print the value : %#", str);
Here, in the third line app should crash because we are accessing an object which is released. But it is printing the value of str. It is not crashing. But with NSArray i observed different behavior.
NSArray *array = [[NSArray alloc] initwithobjects : #"1", #"2", nil];
[array release];
NSLog(#"Print : %#", [array objectatindex : 0]);
NSLog(#"Print : %#", [array objectatindex : 0]);
The code has two NSLog statements used for NSArray. Here after releasing when the first NSLog is executed, it is printing value. But when second NSLog is executed, app crashes. App crash is acceptable because the array accessed was released already. But it should crash when the first NSLog is executed. Not the second one.
Help me with this behaviors. How release works in these cases.
Thanks
Jithen
The first example doesn't crash because string literals are never released. The code is really:
NSString *str = #"hello";
[str release];
People get burned with string literals on memory management and mistakenly using == to compare them instead of isEqualToString:. The compiler does some optimizations that lead to misleading results.
Update:
The following code proves my point:
NSString *literal = #"foo";
NSString *second = [NSString stringWithString:literal];
NSString *third = [NSString stringWithString:#"foo"]; // <-- this gives a compiler warning for being redundant
NSLog(#"literal = %p", literal);
NSLog(#"second = %p", second);
NSLog(#"third = %p", third);
This code gives the following output:
2013-02-28 22:03:35.663 SelCast[85617:11303] literal = 0x359c
2013-02-28 22:03:35.666 SelCast[85617:11303] second = 0x359c
2013-02-28 22:03:35.668 SelCast[85617:11303] third = 0x359c
Notice that all three variable point to the same memory.
Your second example crashes at the second NSLog because at the first log, the memory where array was hasn't been re-used, but that first log causes enough activity on the heap to cause the memory to become used by something else. Then, when you try to access it again, you get a crash.
Whenever an object is deallocated and its memory marked as free, there is going to be some period of time where that memory still stores what's left of that object. During this time you can still call methods on such objects and so forth, without crashing. This time is extremely short, and if you're running a lot of threads it may not even be enough to get your method call in. So clearly, don't rely on this implementation detail for any behavior.
As others have said, regarding your first question, NSString literals aren't going to be deallocated. This is true for some other Foundation classes (NSNumber comes to mind) but is an implementation detail as well. If you need to do experiments on memory management, use an NSObject instance instead, as it will not show the unusual behaviors.
When you send a release message on an object, the object is actually not being removed from the memory. The release message simply decrements the reference count by one only. If the reference count is zero the object is marked as free. Then the system remove it from the memory. Until this deallocation happens you can access your object. Even if you release the object your object pointer still points to the object unless you are assigning nil to the pointer.
The first example doesn't crash because string literals are never released. Where the second totally depends on release and retain counter.
Read this article. Its contains short-and-sweet explanation for your query
You Should read this apple guideline
You seem to assume that release should destroy the object immediately. I don't think that's the guarantee that the language makes. What release means is: I have finished using this object and I promise not to use it again. From that point onwards it's up to the system to decide when to actually deallocate the memory.
Any behaviour you see beyond that is not defined and may change from one version of the Objective C runtime to the next.
That's to say that the other answers that suggest the difference is string literals and re-use of memory are currently correct but assuming that the behaviour will always be like this would likely be a mistake.