I'm integrating Realm into my app. I need to know how to store a custom class object in an RLMObject subclass.
The only properties allowed to be saved in Realm are, accoding to the Documentation:
Realm supports the following property types: BOOL, bool, int,
NSInteger, long, long long, float, double, NSString, NSDate, NSData,
and NSNumber tagged with a specific type.
If you need to store another object (you're mentioning an instance of a custom class), the officially supported way to doing so is creating a subclass of RLMObject (assuming you're on Objective-C) and, inside a property in your parent object, create the reference, like mentioned an example in the docs:
#import <Realm/Realm.h>
#class Person;
// Dog model
#interface Dog : RLMObject
#property NSString *name;
#property Person *owner;
#end
RLM_ARRAY_TYPE(Dog) // define RLMArray<Dog>
// Person model
#interface Person : RLMObject
#property NSString *name;
#property NSDate *birthdate;
#property RLMArray<Dog *><Dog> *dogs;
#end
RLM_ARRAY_TYPE(Person) // define RLMArray<Person>
In this example we have a property owner in the Dog model. I think that's what you're looking for.
Related
I have two classes.
GameData.h
#import "TeamData.h"
#property (assign, nonatomic) GameData* teamA;
TeamData.h
#interface TeamData : NSObject
#property (nonatomic, copy) NSString* teamName;
-(void) printTeamData;
A number of questions :
Inside GameData.m I have this code :
TeamData* team = self.teamA;
[team printTeamData];
The first line shows this warning :
Incompatible pointer types from TeamData* with an expression of type TeamData*
In another class, I am including GameData.h and I want to set the teamA name. How do I access that? So I want to fetch the teamA property from the GameData class and set its name property.
In GameData.h, your property points to its own class, not to TeamData
#property (assign, nonatomic) GameData* teamA;
assign is meant for primitive types such as BOOL or NSInteger.
The parent class should hold a strong reference to a child object.
So your property would be better off as
#property (strong, nonatomic) TeamData* teamA;
As for setting the teamA property, you would call setTeamA: on your GameData instance:
[myGameData setTeamA:...];
I'm new to objective-C, so apologies if this is repeated somewhere. I have a category(?) that is something like:
inside SomeClass.h:
#interface SomeClass (SomeCategory) <SomeDelegate>
#property (nonatomic, retain) id somePublicProperty;
#property (nonatomic, retain) id someProperty; // <-- i want to move this to "private"
#end
and now in my SomeClass.m, all i have is:
#implementation SomeClass (SomeCategory)
// dynamic setters/getters here for someProperty.
#end
I think the someProperty is public. how do i make this "private"? (in other words, how do i syntactically put this in the .m file? i tried to use
#interface SomeClass (SomeCategory) {
#property (nonatomic, retain) somePrivateProperty;
}
#end
but it just complains that i have duplicate definition of the category. how do i do this correctly?
In your .h file, you should not give the category. Just use:
#interface SomeClass : SomeBaseClass < SomeDelegate>
#property (nonatomic, retain) id somePublicProperty;
#end
In your .m file, define your private property inside a class extension:
#interface SomeClass ()
#property (nonatomic, retain) id somePrivateProperty;
#end
A class extension is not a like category in that it allows you to extend an interface as well as add new storage to your class.
In a class category, you can define new properties, but no storage will be allocated for it, so you have to do it by hand:
#interface SomeClass (SomeBaseCategory)
#property (nonatomic, retain) id somePrivateProperty;
#end
#implementation SomeClass {
id _somePrivateProperty;
}
- (void)setSomePrivateProperty:(id)property {
_somePrivateProperty = property;
}
- (id)somePrivateProperty {
return _somePrivateProperty;
}
#end
Otherwise your app will crash.
In any case, keep in mind that given the dynamic nature of Objective-C, your property will never be fully private, since you can always send a message to an Objective-C object through objc_msgsend and thus set or read the property value.
EDIT:
If you do not have the source code for a class implementation, you cannot define a class extension (as per source linked above).
In this case, you could use object association to define properties.
Just add the category definition in the .m file OUTSIDE the implementation block
Like so:
#interface MyClass (MyCategory)
#property (assign) BOOL myPrivateProperty;
#end
#implementation MyClass
...
#end
Categories are best used for adding capability to code you do not own and cannot change. Adding properties via categories is not impossible, but is much more difficult.
Class Extensions are best used for keeping properties your object needs, but are not intended to be public.
If you do truly need to add properties to this object, the way to do it is with the Objective-C runtime's associated objects
There's an excellent writeup of when/how to use them here
I made few classes via Core Data. And I need some additional #propertys for one of that classes in runtime. This #propertys are responsible for download progress and I don't want to store them in Core Data DB. I tried to use a separate extension class:
#interface MyClass ()
{
CGFloat _downloadProgress;
NSInteger _downloadErrorCounter;
BOOL _downloadAllStuff;
BOOL _downloadUserCanceled;
}
#property (nonatomic, assign) CGFloat downloadProgress;
#property (nonatomic, assign) NSInteger downloadErrorCounter;
#property (nonatomic, assign) BOOL downloadAllStuff;
#property (nonatomic, assign) BOOL downloadUserCanceled;
#end
But private variables are not visible out of MyClass, and #propertys compile all right, but in runtime i get -[MyClass setDownloadErrorCounter:]: unrecognized selector sent to instance.
Can anyone suggest me some solution?
The easiest solution (if you don't want to modify the Xcode generated class files) is to add the properties to the Core Data model and define the
properties as transient. Transient properties are not saved to the store file.
Another option is to use a tool like "mogenerator", which generates two class files for each
entity, one for the Core Data properties (which is overwritten if the the model changes),
and one for your custom properties (which is not overwritten).
Update: Starting with Xcode 7, Xcode creates both a class and
a category for each managed object subclass, compare NSManagedObject subclass property in category. Custom properties can be added to the class
definition which is not overwritten when the model changes.
Just add
#synthesize downloadErrorCounter = _downloadErrorCounter;
...
in #implementation. Note, not #dynamic.
When trying to use the #synthesize solution i got the error:
#synthesize not allowed in a category's implementation.
Solution was to use associated objects as described in this blog: http://kaspermunck.github.io/2012/11/adding-properties-to-objective-c-categories/
MyManagedObject+Additions.h
#property (strong, nonatomic) NSString *test;
MyManagedObject+Additions.m
NSString const *key = #"my.very.unique.key";
- (void)setTest:(NSString *)test
{
objc_setAssociatedObject(self, &key, test, OBJC_ASSOCIATION_RETAIN_NONATOMIC);
}
- (NSString *)test
{
return objc_getAssociatedObject(self, &key);
}
So here's my dilemma. I'm dealing with legacy code and trying to simplify and reduce a huge amount of redundancy in the code base. Here's the crux of the matter. I'm trying to consolidate two very similar classes into a superclass/subclass relationship. One is a subclass of NSObject the other a subclass of NSManagedObject.
I have a class that contains only variables called InventoryItems.h. It is a subclass of NSObject.
#property (nonatomic, retain) NSString * desc;
#property (nonatomic, retain) NSString * locationInventoryId;
...
InventoryItems.m
#synthesize desc;
#synthesize locationInventoryId;
...
There is another class that is called FavoriteInventoryItems.h that is a subclass of NSManagedObject.
It contains exactly the same variables as InventoryItems with one additional variable.
FavoriteInventoryItems.h
#property (nonatomic, strong) NSString * desc;
#property (nonatomic, strong) NSString * locationInventoryId;
#property (nonatomic, strong) NSString * dateAddedAsFav;
....
FavoriteInventoryItems.m
#dynamic desc;
#dynamic locationInventoryId;
#dynamic dateAddedAsFav;
...
I can successfully make things work by making InventoryItems a subclass of NSManagedObject and then making FavoriteInventoryItems a subclass of InventoryItems. It does work but I get a message indicating the following:
CoreData: error: Failed to call designated initializer on NSManagedObject class 'InventoryItems'
My solution assuredly is a hack that may have negative consequences.
There are multiple places where the code resembles something like the following:
if (InventoryItem)
...many lines of code here
else if(FavoriteInventoryItem)
...exact same code as above based on favorites
I'm not sure how else to consolidate both of these class into superclass/subclass relationship. Or is there a better way to handle this problem that doesn't involve inheritance? Any ideas?
Try to use a protocol to specify what is common between the classes and allow the 'using' code to be generic to the protocol.
The specification of a protocol is the important part, the implementation already exists in the 2 classes you have. The specification would list the common methods (or properties) between the 2 classes. Then, in the duplicated code, instead of saying:
InventoryItem *item = ...
or
FavoriteInventoryItem *item = ...
You would say:
id < InventoryItem > item = ...
I'm duplicating names because I can't know what a better name is, but the protocol is defined as:
#protocol InventoryItem < NSObject >
#property (nonatomic, strong) NSString * desc;
#property (nonatomic, strong) NSString * locationInventoryId;
#end
Then the code using the protocol doesn't care about what the underlying class is, it just cares what the protocol offers:
item.desc = #"Teddies";
item.locationInventoryId = ...
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.