How to get object from a object chain - iphone

I have an object list according to a tree structure.Here is the sample code.
Model model1 = [[Model alloc]init];
model1.name = #"ABC";
Model *model2 = [[Model alloc]init];
model2.name = #"DEF";
Model *model3 = [[Model alloc]init];
model3.name = #"GHI";
Model *model4 = [[Model alloc]init];
model4.name = #"JKL";
[model3.arr addObject:model4];
[model2.arr addObject:model3];
[model1.arr addObject:model2];
Is there any way to get a particular object without looping each and every other object.
Thanks in advance.

Rather than using a array (I assume that is what Model.arr is) to hold the sub-models, use a dictionary as this provides fast look-up of objects without the need to trawl through the complete collection.
However in order to use a dictionary, you are going to need to supply a unique key in order to access the object.
I would also recommend not exposing arr as that means implementation changes will break dependent code; instead provide methods to add, fetch and remove sub-models:
- (void)addModel:(Model *)model forKey:(NSString *)key;
- (Model *)modelForKey:(NSString *)key;
- (void)removeModelWithKey:(NSString *)key;

Related

Instantiating Custom Object with Core Data

Currently whenever I want to save a custom object called List I use
(Core Date + Magical Record)
List *list = [List MR_createInContext:_managedObjectContext];
Now I'm wondering whether I could instantiate a List item like this
List *localList = [[List alloc] init];
// set some properties
localList.name = #"foobar";
List *newList = [List MR_createInContext:_managedObjectContext];
newList = locaList
Will this cause any problems with Core Data/memory issues?
No, for a couple of reasons:
You can't use init with managed objects. The designated initializer is initWithEntity:insertIntoManagedObjectContext:. There's also a convenience constructor on NSEntityDescription called insertNewObjectForEntityForName:inManagedObjectContext:. If you don't use one of those, you'll have problems.
When you assign newList = localList, you throw away the previous object in localList with all of its data. All that's left after this point is the one you originally assigned to newList. Your assignment to localList.name, for example, disappears with the localList object.
Since you've defined List it's hard to say whether you're creating a List instance correctly in both cases. At the very least your code leaks the second List that you create, and it seems unlikely that that code does what you think it does.
Your code is equivalent to:
List *localList = [[List alloc] init];
// set some properties
localList.name = #"foobar";
List *newList = localList;

How to get an object at an index of a to-many NSSet in NSMangedObject category

Here is my sitch. Let's say that I have Company and Employee object types, with company having to-many Employee's. I create a category for Company and add a method
-(Employee *)getNextEmployee {
return (Employee *)self.employees[self.currentEmployeeIndex++];
}
I realize that I can't access an NSSet by an index, but since it's a category I can't add an NSArray instance variable either. This method is being called frequently, so creating an NSArray for each call would be highly inefficient.
Again, please ignore specifics here, it's just an example.
Edit: Scratch my previous answer suggesting the use of the allObjects property of NSSet. Thinking about it made me realize allObjects is not guaranteed to return the array on the same order every time.
Instead I suggest you use a linked list. Create a previousEmployee property for your Employee class and every time you create a new Employee, using some logic you assign the previous employee. It could be based on the date of entry:
NSFetchRequest *request = [NSFetchRequest fetchRequestWithEntityName:#"Employee"];
request.fetchLimit = 1;
NSSortedDescriptor *sortDescriptor = [sortDescriptorWithKey:#"entryDate" ascending:NO];
// Do further setup...
Employee *latestEmployee = // fetch result...
Employee *newEmployee = // create new employee
newEmployee.previousEmployee = latestEmployee;
There's no getting around having an array if you want ordering (implied by next). The way to have an array economically is to make a NSManagedObject subclass and add an array property to it. Update it when you do adds/removes from the set.
Both #danh and #pedro.m gave great answers. I ended doing kind of a mixture of the two. I created a new class to manage my process instead of a subclass, and I pulled the employees straight from the relationship attribute of the company. Something like this:
#implementation CompanyProcessRunner
-(void)setCompany:(Company *)newCompany
{
company = newCompany;
employees = [company.employees sortedArrayUsingDescriptors:[NSSortDescriptor descriptorWithKey:#"order" ascending:YES]];
}
-(Employee *)nextEmployee
{
company.currentEmployeeIndex += 1;
return (Employee *)[employees objectAtIndex:company.currentEmployeeIndex];
}
#end
Thanks for the help everyone!

How to iterate an NSSet (Objective-C) - To-Many relationship representation in Core Data - efficiently?

To-Many relationships in Core Data are represented by NSSet (as automatically generated by using the Editor... Create NSManagedObject Subclass.. menu.
Which is the most efficient way to iterate an NSSet* ?
NSSet* groups = [contact groups];
for(Group* group in groups) {
NSString* groupName = [group name];
}
or
NSSet* groups2 = [contact groups];
NSArray* groupsArray = [groups2 allObjects];
for(Group* group in groupsArray) {
NSString* groupName = [group name];
}
or another way?
The first is probably more efficient. If the latter were more efficient then Apple would simply use that route to implement the former.
That being said, if you're asking because performance seems to be an issue it's probably more that you're spending a lot of time on the Core Data stuff of faulting objects. A smart move would be to do a fetch on self in %# with groups; set the fetch request's returnsObjectsAsFaults to NO and ensure any appropriate relationshipKeyPathsForPrefetching are specified. The net effect of that will be that all the data you're about to iterate through is fetched in a single trip to the backing store rather than each individual piece of it being fetched on demand in a large number of separate trips.

copying object from NSMutableArray to another using IF statement

OK, maybe I'm not seeing clear anymore and hope you can help.
I'm trying to select an Object from a NSMutableArray using:
if([car.seat isEqualToString:#"fancyOne"]){
fancyThings = [[NSMUtableArray]init];
[fancyThings addObjects: car];
}
Now I forgot to tell you I'm new at this Objective-C, so maybe I'm thinking the wrong way.
What I'm basically trying to do is to get an Object from one array by selecting a value of it's components.
This is the way to do it, I am however keep having trouble with my if-statement.
If I leave out the IF-statement it does fill my other NSMutableArray with the exact same object (thisCar) but if I put in the IF-statement it doesn't pick up that the string is the same in thisCar.seat.
I next example it puts everything in the normalThings but there are some aCar.seats which contain the string FANCYONE. I checked the XML file on spaces and that sort of things but everything is in order as far as I can see.
Shall I build it using NSScanner instead of IsEqualToString?
- (void)viewDidLoad {
appDelegate = (XMLAppDelegate *)[[UIApplication sharedApplication] delegate];
appDelegate.fancyThings = [[NSMutableArray alloc]init];
for (CARS *aCar in appDelegate.someCars) {
if ([aCar.seats isEqualToString:#"FANCYONE"]){
[appDelegate.fancyThings addObject:aCar];
}
else {
[appDelegate.normalThings addObject:aCar];
}
}
self.title = #"Cars";
super viewDidLoad];
}
EDIT:
My BAD!! The code supplied was in fact in order!
There was a mistake in my XMLParser, which added blank lines to the strings, so I couldn't get an equal string!
Hopefully this will give you some guidance:
//init new array
NSMutableArray *fancyThings = [[NSMutableArray alloc] init];
//walk your array
for (SomeCarObject *thisCar in arrayOfCars) {
//is thisCar a qualifying object
if ([thisCar.seat isEqualToString:#"fancyOne"]) {
//yes, add thisCar object
[fancyThings addObject:thisCar];
}
}
You'll want to create that NSMutableArray outside of the for loop (assuming you're iterating through a collection). Then you can add to that NSMutableArray like you did.
Hope this helps!
BTW, you should edit your question with the comment you made to elaborate on it..
It's depends from volume of objects, which u deal with. If there is 1000 objects or less, this method looks good. But if there is more objects, u have risk to freeze u application and have a big memory leaks.
Also if u will need concurrency code later, u have to keep in u mind some
other solutions.
U can using not just a string objects in u array, u can try to fill u array after application startup in objects, which response if string is same or not. Or using nsdictionary with appropriate keys.
Please read my post multithread search design

Good practices for multiple language data in Core Data

i need a multilingual coredata db in my iphone app. I could create different database for each language but i hope that in iphone sdk exist an automatically way to manage data in different language core data like for resources and string.
Someone have some hints?
I've done something similar to Shortseller, but without the use of categories.
InternationalBook and LocalizedBook are both custom managed objects with a one-to-many relationship (one international book to many localised books).
In the implementation of InternationalBook, I've added a custom accessor for title:
- (NSString *)title {
[self willAccessValueForKey:#"title"];
NSString *locTitle = nil;
NSPredicate *predicate = [NSPredicate predicateWithFormat:#"locale==%#", [DataManager localeString]];
NSSet *localizedSet = [self.localizedBook filteredSetUsingPredicate:predicate];
if ([localizedSet count] > 0) {
locTitle = [[localizedSet valueForKey:#"localizedTitle"] anyObject];
}
[self didAccessValueForKey:#"title"];
return locTitle;
}
[DataManager localeString] is a class method which returns the user's language and country code: en_US, fr_FR, etc. See documentation on NSLocale for details.
See the "Custom Attribute and To-One Relationship Accessor Methods" section of the Core Data Programming Guide for an explanation of willAccessValueForKey: and didAccessValueForKey:.
When populating the data, I grab a string representing the user's current locale ([DataManager localeString]), and store that along the with localised book title in a new LocalizedBook object. Each LocalizedBook instance is added to an NSMutableSet, which represents the one-to-many relationship.
NSMutableSet *bookLocalizations = [internationalBook mutableSetValueForKey:#"localizedBook"]; // internationalBook is an instance of InternationalBook
// set the values for locale and localizedTitle
LocalizedBook *localizedBook = (LocalizedBook *)[NSEntityDescription insertnNewObjectEntityForName:#"LocalizedBook" inManagedObjectContext:self.bookMOC];
localizedBook.locale = [DataManager localeString];
localizedBook.localizedTitle = theLocalizedTitle; // assume theLocalizedTitle has been defined.
[bookLocalizations addObject:localizedBook];
[bookLocalizations setValue:localizedBook forKey:#"localizedBook"];
Since the localised titles are being stored in the LocalizedBook managed object, you can make the title attribute a transient, but if you do that you can't use title in a predicate.
The nice thing about this approach is that the implementation of the to-many relationship is transparent to any consumers. You simply request internationalBook.title and the custom accessor returns the appropriate value based on the user's locale behind the scenes.
I have generated model classes for the core data entities.
Then I defined category helper classes with functions to set and get the multilanguage properties (e.g. name).
So I have a Product (with e.g. code and price) and a ProductLanguage (with language and name properties) Entity.
I never directly access ProductLanguage, but always use the name function defined on the the Product model (via category). This has worked well for me sofar.
Like Gordon, I use quite similar code, but not written files generated by model. I use this code in my .m file where I want to show the data.
As I start from Apple template, I put this code exactly in
- (void)configureCell:(UITableViewCell *)cell atIndexPath:(NSIndexPath *)indexPath
of my TableViewController.m
P.S: Just to understand, I use these prefixes: tbl_ for tables (entities), rel_ for relationships, fld_ for fields (attributes).
Hope this helps.
NSSet *sourceSet = [NSSet setWithArray:[[tbl_MainTable rel_Localization]allObjects]];
NSPredicate *predicate = [NSPredicate predicateWithFormat:#"fld_Locale == %#", [[NSLocale preferredLanguages] objectAtIndex:0]];
NSSet *filteredSet = [sourceSet filteredSetUsingPredicate:predicate];
//NSLog(#"%#", filteredSet); NSLog(#"%#", [[filteredSet valueForKey:#"fld_Name"] anyObject]);
if ([filteredSet count] > 0)
{
[cell.detailTextLabel setText:[[filteredSet valueForKey:#"fld_Name"] anyObject]];
}