I have an existing Parse database, with several PFObject subclasses.
If I want to add a new property to a subclass, an array for example, I add it to the PFObject subclass as an #property and declare it #dynamic in the implementation. I initialise it as empty for new objects.
#dynamic newArray;
+ (instancetype)object
{
MyObject *myObject = [super object];
myObject.newArray = [NSMutableArray array];
return myObject;
}
But how can I ensure this is initialised for already existing objects, as accessing this property for old objects causes a crash.
I figured I could override the getter, but I would lose all the Parse boilerplate code, and not sure that's wise. Currently I have to empty my db to ensure all new objects have the right properties. But this obviously won't work in the real world.
This is a problem that I faced when developing my Parse application as well, but I did not have the problem of preexisting instances of the object that did not have a value for the specified key.
One solution to this problem would be to set up a cloud code job that runs through all the preexisting objects and assigns default values for the required fields. This would enable you to keep the complexity of the client to a minimum, allowing for easy updates and possibly porting to other systems.
You should also set up a beforeSave function that makes sure that all fields have a default value, and then either rejecting ones that don't or silently assigning them a default value.
My third and final recommendation (which you seem to be doing already) is to make sure the +object method properly initializes the object with all default values, in order to ensure that pinned objects (if you're using LDS) have the correct default value.
Related
I have a NSMutaleArray that contains instances of class X. The tableView cell at row i is populated using contents of array at index i.
The contents of the array are created based on incoming JSON data from the network. So, I have a helper function that converts JSON data into these objects and stores them in the array. The object may be modified after storing in the array (for instance image belonging to cell gets downloaded and file pointer is added to the object).
Currently, I am making the reads and writes thread safe putting them inside dispatch_sync & dispatch_barrier_async. Some issues I see
To create the tableview cell I need to do a read. This is also going through the dispatch_sync. Hence, it looks like there is chance of it getting blocked because data for a non-visible but close enough cell is being updated. Any best practices to avoid this?
Is there some smart way for the block submitted through dispatch_sync and disaptch_barrier_sync to only wait if another block is either modifying, removing the same index of the NSMUtableArray or value associated withs are key of NSMUtableDictionary?
To make the code more readable, I am thinking of sub-classing NSMutableArray and creating a THreadSafeMutableArray class that over-rides the objectForIndex and subscript methods. Any issues with this approach?
Your NSMutableArray should be an atomic property (the default value is this) maybe you changed to nonatomic in the declaration.
Look the documentation: https://developer.apple.com/library/mac/documentation/Cocoa/Conceptual/ProgrammingWithObjectiveC/EncapsulatingData/EncapsulatingData.html
Apple documentation:
This means that the synthesized accessors ensure that a value is always fully retrieved by the getter method or fully set via the setter method, even if the accessors are called simultaneously from different threads.
#interface XYZObject : NSObject
#property NSObject *implicitAtomicObject; // atomic by default
#property (atomic) NSObject *explicitAtomicObject; // explicitly marked atomic
#end
I have some Core Data functionality that was working fine until some recent (seemingly unrelated) changes were made. Now I'm getting problems where all the attributes belonging to a particular NSManagedObject subclass instance are suddenly returning nil.
Let's say my NSManagedObject subclass is called Foo and it has only one attribute called value. Once I realised value was somehow becoming nil I went and setup the following category to monitor changes to value.
#implementation Foo (Debug)
- (void)setValue:(NSDate *)value
{
[self willChangeValueForKey:#"value"];
[self setPrimitiveValue:value forKey:#"value"];
[self didChangeValueForKey:#"value"];
}
- (NSDate *)value
{
[self willAccessValueForKey:#"value"];
NSDate *value = [self primitiveValueForKey:#"value"];
[self didAccessValueForKey:#"value"];
return value;
}
#end
setValue: is called for my object and the argument passed in is a non-nil NSDate. Then the value is retrieved (in another method). The same value that was specified is retrieved correctly.
However when another method tries to read value, the value accessor is called and a nil value is returned by primitiveValueForKey:.
In between the two reads setValue: is not called and the Foo object itself is still valid (non-nil). In fact no other Core Data operations are performed between the two reads on any Core Data object or the context as a whole.
We're using ARC in our project. Is it possible ARC is somehow messing with my Core Data variables and deallocating them? If so does anybody have any suggestions for debugging ARC deallocations? Or better yet, does anyone know a way to ensure ARC doesn't deallocate my variable.
This may not even be ARC related, however I'm at a bit of a loss as to what is going on. Any suggestions would be very much appreciated.
This is very likely because the NSManagedObjectContext that these objects belong to, is going away. When you have NSManagedObject instances around but you're not holding on to the context yourself, those managed objects will start returning nil.
Under ARC, make sure you store the context in a strong variable, i.e. an instance variable that's not weak or a static global.
Non-ARC, i.e. retain-release code, make sure you're retaining the context.
As others mentioned (it was my case also), be sure that you haven't reset your managed object context because if you do, all Entities stored as properties will have data: <fault>.
If you do reset your managed object context, you will also have to re-fetch the Entity itself.
check the viewDidLoad-Method
profile = [NSEntityDescription insertNewObjectForEntityForName:#"MyProfile" inManagedObjectContext:profileContext];
hope this works
In my controller class, I initialize two instances of a model class (whose header is properly imported into controller class) with an NSButton. The model is really simple, just 4 members and one method - attack(). Making a silly text game!
- (IBAction)startGame:(id)sender {
Combatant *hero = [[Combatant alloc] init];
Combatant *enemy = [[Combatant alloc] init];
[console insertText:#"You have created a hero! An enemy approaches...\n"];
}
So now I have these two objects sitting there. Or do I? Because this other button, the one that's supposed to make them fight, has no idea what hero and enemy are, or that they have a class method that makes em' fight!
- (IBAction)attack:(id)sender{
[hero attack:enemy]; //Use of undeclared identifier, blah blah.
[console insertText:#"You attack the enemy! Woah!\n"];}
I get that if I initialized those objects in the attack method, then I could use them, so I gather this is something to do with scope. But I don't like the idea of sending model objects to controller methods, that seems silly.
Let me apologize: yes, this is a stupid, high-level question about the structure of Cocoa. Sorry. But I figure one of you will know exactly what I am not doing and tell me to do it!
In short, what is the Cocoa way of doing things in this situation? Thanks in advance.
-Alec
When you declare a variable in a method, it is a local variable, which means it only exists in that method. The same goes for variables you declare in functions.
If you want the variable to exist in all instance methods in the class, you need to make it an instance variable, which you do by declaring it in that { … } section in the class's #interface.
Note that any objects you store in instance variables, the instance should own. This means three things:
You'll need to either retain the object (and thereby own it) or make a copy (which you will then own) before assigning it to the instance variable.
Since you own it, you'll need to release it in the instance's dealloc method.
If you decide to replace it with a different object, you'll need to release the former object (since you still own it) and retain or copy the new object (in order to own it).
See the Objective-C Programming Language and the Memory Management Programming Guide for more information.
I have a pretty standard setup where I have an array in my controller that I'm binding to an NSTableView. When I add an object to this array with the UI, I add elements by copying the array to a mutable array, adding the contents, then copying it back. I saw this idiom somewhere, but I'm not really sure where, or whether this is actually a sane thing to do. I t works fine on Snow Leopard, but it crashes really badly on Leopard. GDB tells me it happens right at the marked line, where I copy the new array back.
#interface MyAppDelegate : NSObject {
NSArray * urls;
//other declarations
}
#property (nonatomic, retain) NSArray *urls;
#implementation MyAppDelegate
#synthesize urls;
- (void)addThing:(id)sender {
NSMutableArray *newUrls = [NSMutableArray arrayWithArray: [self urls]];
[newUrls addObject: newurlimadding];
[self setUrls: [NSArray arrayWithArray: newUrl]];
}
I'm pretty sure I must be doing something boneheaded here. Is there a better way to do this? Why am I crashing?
NSMutableArray *newUrls = [NSMutableArray arrayWithArray: [self urls]];
[newUrls addObject: newurlimadding];
[self setUrls: [NSArray arrayWithArray: newUrl]];
What did you create newUrls for if not to set it as the new value of urls?
Besides that, there are a couple of things you're doing wrong:
No model objects. Bindings hates this. Unless your table view exists solely to display the different parts of the URL (scheme, host, path, etc.), each in one column, you're not giving Bindings what it wants.
Pay attention to the fields in the Bindings Inspector. Note that there are two: Controller Key and Model Key Path. They're exactly what they say on their tins: The Controller Key is the key for a property of the controller, which should provide model objects. The Model Key Path is the key path for one or more properties in the model, which usually should provide value objects, such as strings and numbers.
You probably need to create a model class and make the URL a property of that class. I'd guess that you have other objects, perhaps in parallel arrays, that you can move to properties of the model class. Do that, so that you're working with Bindings instead of against it.
Not using array accessors. You're just setting the whole array at once, which is inefficient and may cause display problems (such as selection highlights disappearing). Implement and use array accessors for this array property. Accessorizer can help you here.
I actually have no idea whether this will fix your crash because you haven't told us anything about it. You should edit your question to include any exception messages or other crash-describing output, which you'll find in the Run Log/Debugger Console.
Also, since the type of urls can be mutable, you should set it to copy, not retain.
NSArray * things; - since this can be modified you better represent using NSMutableArray instead NSArray.
When ever you need to add some new element to the list just use 'addObject' and insert element to the list.
Your binding will make sure that UI is updated using KVO and KVC.
It looks like the problem was that I had NSURLs as my object type. Once I changed the object in my array to a custom-made KVC compliant object, I wasn't getting any more crashes.
Maybe NSURL isn't KVC-compliant on 10.5, but it is on 10.6?
I'm getting sublayers of a CALayer with this property accessor:
// #property(copy) NSArray *sublayers
NSArray* layer = mylayer.layer.sublayers;
Since this property uses "copy", every time I simply execute:
mylayer.layer.sublayers
is an entire copy of the sublayers array being made for me? If so that might be bad, because I could have one hundred+ layers, and definitely don't want to create a huge copy of them. I'm just looking to get a count and iterate through the existing layers, just a pointer to the existing layers,
Thank you
When you use the 'copy' declaration and you #synthesize the property, then copy is used when the property is set.
In your example, you are only accessing the value which just gives you a pointer to the sublayers array.
Here's a link to the ADC documentation on this point.
Update
IIRC, the runtime is smart enough to know if the object being set is mutable. If an immutable object is being passed in to the property it is retained and not copied.
In some cases, if you are passing in a mutable object that you want to be able to modify, then you should write your own setter that calls mutableCopy on the object. This is shown in the documentation link that I provided.
I'm not sure I understand your answer Abizern so let me say this:
If you use (copy) on a property you will be making a whole new copy of that object so would be wasting a lot of memory. I'm not sure why they are doing that, they could just use (readonly) instead if they want to protect the values from change. Plus remember most value classes are immutable in Obj-C so they can't change the value anyway.