Realm RLMArray is nil - objective-c

For some reason my RLMArray's are nil when I run my program.
I am able to see the data in the RLM browser, and it links appropriatley.
Is there something I could be missing here?
#interface HMFAlbum : RLMObject
#property NSInteger persistentId;
#property RLMArray<HMFTrack> *tracks;
#property RLMArray<HMFRange> *ranges;
#end
#interface HMFTrack : RLMObject
#property NSInteger persistentId;
#property HMFAlbum *album;
#end
RLM_ARRAY_TYPE(HMFTrack)
#interface HMFRange : RLMObject
#property NSInteger persistentId;
#property (readonly) RLMLinkingObjects *albums;
#end
RLM_ARRAY_TYPE(HMFRange)

It's expected that instance variables of persisted RLMObject instances will be nil as the property getters read values directly from the Realm file. The instance variables are only used for objects prior to being saved to the Realm, and remain nil after that point.
The Debugging section of the Realm documentation touches on this topic and mentions an LLDB script that can be used to show property values of persisted objects when debugging in Xcode. The -description method on the model classes, used by NSLog when formatting objects using the %# format specifier, will also show the property values as expected.

Related

Hiding implementation details with .h and .m in objc

I'm confused about something. If in your .h file you have:
#property (nonatomic, strong, readonly) NSArray *categories;
and then in the .m you have:
#interface MyClass ()
#property (nonatomic, strong, readwrite) NSMutableArray *categories;
#end
If I want to later set categories in the .m file, I can do:
[self setCategories:[NSArray arrayWithArray:categories]];
But then Xcode complains that incompatible pointer types sending NSArray to NSMutableArray. I'm basically trying to hide the implementation details and have the .m use a NSMutableArray and to a consumer
use an NSArray.
[self setCategories:[NSMutableArray arrayWithArray:categories]]; // this gives no Xcode warning
By using the [NSMutableAray arrayWithArray:] method, does it still prevent the consumer of my Class from mutating my categories array?
You've done all you can in objc.
Your internal readwrite declaration needs a mutable array, so Xcode's complaining is right, you have to use a mutable for the setter.
The consumer can do everything with that object, even if it's declared as NSArray you can find out it's a NSMutableArray in reality and change it.
You can't prevent that. But your public declaration shows it should be assumed immutable. There's nothing more you can do.

RLMArray properties in unmanaged RLMObjects in Objective-C

I cannot find a good example code of this anywhere....but the information I find is contradictory and confusing...
#interface DAORealmMetadata : RLMObject
#property (nonatomic, copy) NSString* id;
#end
RLM_ARRAY_TYPE(DAORealmMetadata)
#interface DAORealmBase : RLMObject
#property (nonatomic, copy) NSString* id;
#property (nonatomic, copy) RLMArray<DAORealmMetadata*><DAORealmMetadata>* metadata;
#end
RLM_ARRAY_TYPE(DAORealmBase)
Question:
Am I supposed to add #dynamic metadata in the DAORealmBase implementation...or not?
I've tried it with and without and have the same end result...a crash.
I create the unmanaged object with this code:
DAORealmBase* baseObj = [[DAORealmBase alloc] init];
DAORealmMetadata* metadataObj = [[DAORealmMetadata alloc] init];
[baseObj.metadata addObject:metadataObj];
Question:
Why does the last line cause a crash/exception?
I can only assume that I"m doing something wrong, but I cannot find any specifics as to what I did.
Thanks!
Well, I tracked the problem down, and through some trial and error, determined that the problem was the property attributes on the RLMArray properties.
Changing
#property (nonatomic, copy) RLMArray<DAORealmMetadata*><DAORealmMetadata>* metadata;
to
#property RLMArray<DAORealmMetadata*><DAORealmMetadata>* metadata;
seems to have resolved the problem. I believe specifically the 'copy' attribute.
Now, I know that the Realm docs say that the attributes are ignored and not needed, but the lint checker I'm using wants them there...and since they are "ignored", what's the harm?
Well, they are ignored on normal Realm properties, but on the RLMArray properties they aren't ignored, and problems ensue.
Hopefully this will help someone else in the future and save them some time.

RLMObject with Array of NSStrings

I've been upgrading a project to use Realm as the persistence store and I'm not able to find any documentation on how to use an array of strings in one of my models.
The implementation of an Array for a RLMObject is to use an RLMArray where T inherits RLMObject
I could make an object that inherits.. property inside which is string... but that seems like quite some overhead to replace an NSArray of strings.
Does anyone know the recommended best practice to do this?
As of Realm Cocoa 3.0 you can simply do RLMArray<RLMString> *array; and no longer need the wrapper object type.
In older versions of Realm you need an RLMObject which contains the string:
#interface StringObject : RLMObject
#property NSString *value;
#end
RLM_ARRAY_TYPE(StringObject)
#implementation StringObject
#end
#interface Object : RLMObject
#property RLMArray<StringObject> *array;
#end

What does this objective-c property synthesis warning mean?

Since upgrading to Xcode 5.1, I'm starting to see the following warning in some code my project uses. I'm trying to figure out what it means.
Warning: Auto property synthesis will not synthesize property 'responseHeader' because it is 'readwrite' but it will be synthesized 'readonly' via another property
The code where it's occurring, in the .m file:
#interface S3Response ()
#property (nonatomic, readwrite, retain) NSDictionary *responseHeader;
#end
The previous declaration of the property, in the .h file:
#property (nonatomic, readonly) NSDictionary *responseHeader;
There is no #synthesize statement for that property, nor are responseHeader or setResponseHeader defined as methods. There is however an explicit definition of an ivar named responseHeader.
Seems pretty straightforward to me: property is declared as read-only for users of the class, but read-write locally so the class can set it.
What does this warning mean, and what should I do about it?
That code seems to be from the AWS SDK for iOS,
and S3Response is a subclass of AmazonServiceResponse.
The public AmazonServiceResponse interface defines a read-only property
#interface AmazonServiceResponse:NSObject
// ...
#property (nonatomic, readonly) NSDictionary *responseHeader;
#end
which is redefined as read-write in a class extension in the implementation file:
#interface AmazonServiceResponse ()
#property (nonatomic, readwrite, retain) NSDictionary *responseHeader;
#end
Now the subclass S3Response also wants read-write access to this property,
and therefore also defines in the class extension of its implementation file:
#interface S3Response ()
#property (nonatomic, readwrite, retain) NSDictionary *responseHeader;
#end
The compiler complains because – when compiling "S3Response.m" – it does not know
that a setter for the property exists in the superclass (it does not read
the implementation file of the superclass at that point). Also the compiler cannot
simply synthesize a setter in the subclass, because it cannot not know that the
property is backed-up by an instance variable in the superclass.
But you know that a setter will be generated, so you can remove the warning by
adding a #dynamic declaration to the subclass implementation:
#implementation S3Response
#dynamic responseHeader;
...
#dynamic is a "promise" to the compiler that all necessary accessor methods will
be available at runtime.
The problem here is as follows.
By default, if don't write ownership (weak/retain/strong/assign) explicitly, xCode will check the type automatically. So in case of NSDictionary it will be strong. Thus, in interface you will have
#property (nonatomic, readonly, strong) NSDictionary *responseHeader;
Then it will be contradict you private implementation definition
#property (nonatomic, readwrite, retain) NSDictionary *responseHeader;
Compilator doesn't match strong and retain under property synthesizing though it is formally the same thing.
To cure situation you can write retain in both cases, or more correct, you should not write retain at all. It will be strong by default in both definitions.

XCode: Unrecognized selector sent to instance

I am getting the following error:
"-[Order items]: unrecognized selector sent to instance 0x6b5f240"
I do have a class called Order, which looks like this:
Order.h
#import <Foundation/Foundation.h>
#import <CoreData/CoreData.h>
#class OrderItem;
#interface Order : NSManagedObject {
#private
}
#property (nonatomic, retain) NSNumber * orderID;
#property (nonatomic, retain) NSDate * date;
#property (nonatomic, retain) NSNumber * orderCode;
#property (nonatomic, retain) NSSet* items;
#end
Order.m
#import "Order.h"
#import "OrderItem.h"
#implementation Order
#dynamic orderID;
#dynamic date;
#dynamic orderCode;
#dynamic items;
...
It doesn't extend any sort of class which has an "items" method, if I'm reading that correctly?
Is there any other reason I would be getting such an error. To add to the madness, this project is copied directly from a previous project, with some minor edits. I've done text comparisons on every single class in both projects and there are no differences other than the cosmetic changes I've made.
#dynamic items tells the compiler that you will be providing the methods for items.
Since this was working in a previous project, it must have had the following method somewhere in the .m file:
- (NSSet *)items {
// Appropriate code
}
If you do not want to provide your own custom getter like this, then change #dynamic items to #synthesize items and the compiler will generate one for you.
For more details, see the Declared Properties section of The Objective-C Programming Language provided by Apple here: https://developer.apple.com/library/mac/#documentation/Cocoa/Conceptual/ObjectiveC/Chapters/ocProperties.html
EDIT
While everything above still applies to a normal object (and may still apply here), I just noticed that this is a subclass of NSManagedObject.
In your old data model there was probably a relationship called items and therefore the appropriate methods were provided by NSManagedObject and #dynamic was appropriate to prevent compiler warnings.
If in your new data model there is no relationship named items, then the methods will not be generated and it will cause the problem that you are getting here.