I have an AMContact object where one of its strong properties is an array of AMEmailAddress objects. One contact can have many email addresses. Can I create a strong property on my email address object that points to the contact object?
I feel like if its a strong reference there could be a retain cycle. If I make it weak, when I do my query to get all of the email address objects, at some point the contact object for each one is becoming nil.
- (NSArray*)allEmailAddresses
{
NSArray *allContacts = [self allContacts];
NSMutableArray *emailAddresses = [NSMutableArray array];
for (AMContact *contact in allContacts) {
if (contact.emailAddresses) {
for (AMEmailAddress *address in contact.emailAddresses) {
[emailAddresses addObject:address];
}
}
}
if (emailAddresses.count > 0) {
return emailAddresses;
}
return nil;
}
#interface AMContact : NSObject
#property (nonatomic, strong) NSString *firstName;
#property (nonatomic, strong) NSString *lastName;
// arrays of AMEmailAddress, AMPhoneNumber objects
#property (nonatomic, strong) NSArray *emailAddresses;
#property (nonatomic, strong) NSArray *phoneNumbers;
#end
#interface AMEmailAddress : NSObject
#property (nonatomic, strong) NSString *label;
#property (nonatomic, strong) NSString *email;
#property (nonatomic, strong) AMContact *contact; // IS THIS OK OR A RETAIN CYCLE?
#end
A short and simple answer to your question: Can I create a strong property on my email address object that points to the contact object?
Can you? Yes. Should you? Absolutely not. Your model design already hints at what the relationship should be - the AMContact objects owns the email addresses, not the other way around. Having email addresses have a strong reference to the parent object is not a good way to go about this. The reference from child back to owner should be weak. The fact that your AMContact instance is going to nil is an issue with scoping and design and you shouldn't use a strong reference to get around this.
As for why your AMContact instance is going nil - It's a bit odd because if the AMContact object goes to nil, its child objects should as well. Is it going nil in the code you posted, or at some other point in some other method? Seeing the code where you're trying to refer to it, and it's nil, would be good.
UPDATE:
If you want a list of contacts that have email addresses, you should change the method to return that. However I'm guessing what you mean is that you want a list of their email addresses with their associated contacts. In this case you would have to flip the relationship around and consider the email to be the parent, and the contact to be the child. Note that if a contact has multiple email addresses then you'll have multiple references to the same contact. Assuming you have a contact property of type AMContact in your AMEmailAddress object, I would add a line as follows:
for (AMEmailAddress *address in contact.emailAddresses) {
//Assign the contact as a property of the email address
//Make sure the contact property is strong
address.contact = contact;
[emailAddresses addObject:address];
}
//Nil out the emailAddresses array
contact.emailAddresses = nil;
UPDATE:
Ok so now you should have a flipped object - emailAddress as the parent and contact as the child, with no strong reference from contact to emailAddress anymore. This means you won't be able to re-use the contact object to get email addresses again though. If you need to maintain the original contact object as-is then you're right, you'll have to store it as a property in the View Controller or in another class - for approaches like this where I have to deal with people objects (for allowing users to invite friends, etc) I use a 'People Manager' singleton and keep all the original objects there.
Well in your case, I think it's very clear that Contact is the owner (parent) of his email addresses, so you will avoid any cycles by just designing this relation in that way, i.e. using weak reference back to contact. If you use a strong ref, then you will have a retain cycle because NSArray retains it's children in emailAdresses.
Now, I would check your program structure to see how and why you lose contacts in that weak reference, something must be releasing them, perhaps, your contact leaves scope prematurely?
You can get more info and advice in this great article on retain cycles, there are some workarounds as well, but I would encourage you to stick to a design that makes more sense.
Related
I have a bunch of properties on my model object: ModelObject declared as follows:
#property (atomic, strong, readonly) LotsOfProps *lotsOfProps;
Inside this object there are properties like this:
LotsOfProps {
#property (nonatomic, strong) AProp prop;
#property (nonatomic, strong) MyProp myProp;
}
I do some navigation between different collectionViewCells each of which is backed by a model: Model. On first navigation the properties are present on LotsOfProps and when I navigate away and back to collection view backed-model and check the properties on LotsOfProps I see that they're all nil. I've declared encodeWithCoder, decodeWIthCoder on Model and LotsOfProps and from my understanding atomic already auto generates synchronize methods so even if it's accessed on different threads it should be thread safe.
Any ideas why all of the properties are nil on my object LotsOfProps? I've never seen an issue like this before hence if there's any more information I can provide please let me know!
I'm using dbaccess for my iOS project.
How can I pass an array to dbaccess object? For example:
I have dbobject like:
#interface Member : DBObject
#property (strong) NSString* firstname;
#property (strong) NSString* lastName;
#end
#interface Group : DBObject
#property (strong) NSString* groupName;
#property (strong) NSString* adminName;
#property (strong) Member* members;
#end
For this group, it has 4 member than How can I store all group members and group detail in one object and also how to retrieve them?
Thanx in adv.
To answer the question I have re-modelled and provided an example below of how you would go about creating a one-to-one relationship between these two objects.
This problem stems from the fact that there is no such thing as a typed array in Objective-c. When there is, we will look at re-implementing how the interface works.
I have moved the Group object into the member, as a member belongs to a group, and then added a members method to the Group object to look backwards to the child objects.
#interface Member : DBObject
#property (strong) NSString* firstname;
#property (strong) NSString* lastName;
#property (strong) Group* group;
#end
#interface Group : DBObject
#property (strong) NSString* groupName;
#property (strong) NSString* adminName;
- (DBResultSet*)members;
#end
And the implementations now look like this.
#implementation Member
#dynamic firstname,lastName, group;
#end
#implementation Group
#dynamic adminName, groupName;
- (DBResultSet *)members {
/* we achieve one-to-many relationships by querying the objects in the
manner a relational database would commonly be structured */
return [[[Member query] whereWithFormat:#"group = %#", self] fetch];
}
- (void)entityDidDelete {
/* when you create tight bonds, you may well wish to create 'trigger' methods
to clean data and keep the integrity in good order */
for (Member* member in [[[Member query] whereWithFormat:#"group = %#", self] fetch]) {
member.group = nil;
[member commit];
}
}
#end
Now there is nothing stopping you creating an array as a property type, and storing the Id values within it. But that is not as clean, and requires you to maintain it, whereas if you a looking for FK values, this requires no maintenance and you can create lovely logic to stop the deletion of objects if it is related to others, without having to hydrate lots of objects and then look inside arrays.
Plus you get the beautiful option of using the object dot notation to navigate the strongly typed relationships from the Person object.
NSString* admin = person.group.adminName;
Also, when you added the Group object into Member:
#property (strong) Group* group;
DBAccess automatically created an index in SQLite for the group property, and prioritises its importance within the cache, as objects which are linked this way are more likely to be accessed.
Hope this helps,
Adrian
I am downloading a list of objects from an API to display to a user. The list has a mix of two types of objects. Imagine that they are combined books and authors, and the class definitions look like this:
#interface Book : NSObject
#property (nonatomic, strong) NSString *title;
#property (nonatomic, strong) Author *author;
#end
#interface Author : NSObject
#property (nonatomic, strong) NSString *fullName;
#property (nonatomic, weak) Book *book;
#end
Every Book can download its Author information from the API, and vice versa.
If the API gives me a Book, I can set its author property once I download it. The Author object points back to the Book through the book property, but this doesn't create an ARC Retain Cycle because the book property is weak.
However, if the API gives me an Author first, and I download its Book, the object will be deallocated once the method in which I set it returns, because the same property is weak.
I thought of a few ways around this:
Create a Content object that stores both (not viable for many-to-many relationships)
Create separate strongBook and weakBook properties, and then make a readonly property called book which checks which is set and returns that one
Those both seem messy to me, although the second option is preferable.
Is there a way to dynamically change a property from weak to strong (and vice-versa) using the Objective-C runtime?
UPDATE: I'm getting a few suggestions on how to work around the issue, which I don't have trouble coming up with myself. This question is specifically about whether there is a way to either (a) dynamically redefine #properties for a specific instance of a class, or (b) override ARC's retain/release behavior in specific circumstances (since this issue wouldn't exist in MRC).
Just a shot in the dark, but you could create the property and not specify and then use dynamic with the runtime apis. I didn't test it, but i think it should work:
//.h file
#import <Foundation/Foundation.h>
#interface SomeObject : NSObject
#property(nonatomic) NSObject *object;
#end
//.m file
#import "SomeObject.h"
#import <objc/runtime.h>
#implementation SomeObject
#dynamic object;
-(void)setObject:(NSObject *)object
{
BOOL isWeak = NO;
if(isWeak)
{
objc_setAssociatedObject(self, "object", object, OBJC_ASSOCIATION_ASSIGN);
}
else
{
objc_setAssociatedObject(self, "object", object, OBJC_ASSOCIATION_RETAIN);
}
}
-(NSObject *)object
{
return objc_getAssociatedObject(self, "object");
}
#end
For the period of the download, create a mutable dictionary to temporarily store author objects that arrive prior to the book. When a book is received, look in that array and see if the author info is there, if so attach it. When you are finished clean out the mutable array.
Does anybody have an example on how to model and code a transient to-one relationship in CoreData? For example, I have 2 entities with a one-to-many relationship. Doctor and Appointment. Now I want an transient relationship called mostRecentAppointment on the doctor entity. It's straightforward to model in the xcode designer, but I'm not sure about the implementation side. Also should I implement an inverse? Seems silly.
Have a look at this code I wrote recently, to cache an image in an NSManagedObject:
First you define a transient property in your model (notice that if your transient property points to an object type other than those supported by CoreData you'll leave as "Undefined" in the model)
Then, you re-generate your NSManagedObject subclass for that entity or just add the new property manually, the header file should look like this:
#interface Card : NSManagedObject
#property (nonatomic, retain) NSString * imagePath;
#property (nonatomic, retain) NSString * name;
#property (nonatomic, retain) NSNumber * order;
#property (nonatomic, retain) NSString * displayName;
#property (nonatomic, retain) UIImage *displayImage;
#end
Here we change the class of the transient property to the actual class type
e.g. displayImage type here is UIImage.
In the implementation file (or an extension class) you implement the getter/setter for your transient property:
-(UIImage*)displayImage{
//Get Value
[self willAccessValueForKey:#"displayImage"];
UIImage *img = (UIImage*)[self primitiveValueForKey:#"displayImage"];
[self didAccessValueForKey:#"displayImage"];
if (img == nil) {
if ([self imagePath]) { //That is a non-transient property on the object
img = [UIImage imageWithContentsOfFile:self.imagePath];
//Set Value
[self setPrimitiveValue:img forKey:#"displayImage"];
}
}
return img;
}
Hope that helps you.
What you need to do is add an entity of type Appointment called newAppointment and set this each time you create a new appointment for a given doctor. Its that simple.
Always implement an inverse as apple recommend this for validation and core data efficiency.
Alternatively you could timestamp the appointments and use NSPredicates to search for the latest appointment in a given Doctor's linked appointments.
In this case, the appropriate method to override is -awakeFromFetch in the Doctor entity, for example like so:
- (void)awakeFromFetch {
[super awakeFromFetch];// important: call this first!
self.mostRecentAppointment = <something>; // normal relationship
self.mostRecentAppointment.doctor = self; // inverse relationship
}
In the model designer, mark both the normal and the inverse relationship as transient. That should be it.
Well, you'll just have to try out, in your own sample program that can be no more than an hour to set up correctly.
My guess is --- no extra coding will be needed. If Apple's documentation on CoreData is correct, the only difference between a normal attribute/relationship and a "transient" one is that the latter is not persisted, meaning, when you "save" it does not update the persistent-store.
I would guess that otherwise all the aspects of it are complete, together with KVO/KVC compliance, Undo support, validation, and automatic update by delete rules. The only thing is that after a fresh Fetch of the entity --- the transient relationship will always be nil.
For that --- I would of course NOT RECOMMEND setting up a transient relationship as "non-optional", because it is very likely to be null most of the time for most of the entities.
I would set up a reverse relationship (transient as well and named wisely) and have both delete rules be "Nullify".
So far is for transient relation.
But here is an alternative I came up with, trying to solve almost-the-same problem. My "appointment" is one of the related appointments, but not just the "latest", but the first "unfinished" one. Very similar logic.
Instead of a transient relationship, I added a new calculated property to my "Doctor" entitys generated NSManagedObject subclass, in a category, like this:
#interface XXDoctor (XXExtensions)
/**
#brief Needs manual KVO triggering as it is dependent on a collection.
Alternatively, you can observe insertions and deletions of the appointments, and trigger KVO on this propertyOtherwise it can be auto-
#return the latest of the to-many appointments relation.
**/
#property (readonly) XXAppointment *latestAppointment; // defined as the
#end
Implementation:
#import "XXDoctor".h"
#import "XXAppointment.h"
#implementation XXDoctor (XXExtensions)
// this won't work because "appointments" is a to-many relation.
//+ (NSSet *)keyPathsForValuesAffectingLatestAppointment {
// return [NSSet setWithObjects:#"appointments", nil];
//}
- (XXAppointment *) latestAppointment {
NSInteger latestAppointmentIndex = [self.appointments indexOfObjectPassingTest:^BOOL(XXAppointment *appointment, NSUInteger idx, BOOL *stop) {
*stop = (appointment.dateFinished == nil);
return *stop;
}];
return (latestAppointmentIndex == NSNotFound) ? nil : [self.appointments objectAtIndex: latestAppointmentIndex];
}
#end
I would like to store a zeroing weak reference to an object in a NSDictionary. This is for a reference to a parent NSDictionary, so I can crawl back up a large structure without searching.
I can not use __weak here; even if my local reference is weak, the NSDictionary will store a strong reference to the object that was weakly referenced. And, of course, NSDictionary can't have nil objects.
I'm on iOS, not Mac, so NSHashTable isn't available. And I only want one object to be weak; the rest should still be strong.
(I'm going to post my answer, so I have something to mark as accepted if there's no better answer. But I'm hoping someone has a better answer.)
In iOS 6+ you can use NSMapTable and choose if you want objects and/or keys to be weak or strong.
I've settled on defining a "container" class with a single member, like this:
#interface Parent : NSObject
#property (nonatomic, weak) id parent;
#end
#implementation Parent
#synthesize parent = _parent;
- (id)initWithParent: (id)parent;
{
if (( self = [super init] )) {
_parent = parent;
}
return self;
}
#end
And using it:
id parentRef = [[Parent alloc] initWithParent: parent];
[newElement setObject: parentRef forKey: ParentKey];
I think this will work for me, but it seems crazy that there's no better way built in to Foundation.
There is a built-in way; just use:
[array addObject:[NSValue valueWithNonretainedObject:object]];
Then to access the object later, use:
id object = [[array objectAtIndex:i] nonretainedObjectValue];
If the value has been released since you added it then "object" will be an invalid pointer under iOS 4.x, and (I'm assuming) will be nil under 5.x with ARC enabled.
EDIT: my assumption was wrong - it's not weak under ARC (see discussion below).