So I have this problem in Objective-C. Let me set the stage of the problem.
I have a constraint, that doesn't allow me to modify the properties of an object. As it will assert that I am modifying that object without putting it between a write and commit transaction
ObjectA* constrainedObject = ...; // Retrieved from source with constraint applied.
constrainedObject.name = #"John"; // Assert tripped
[db writeTransaction];
constrainedObject.name = #"John"; // Does not assert
[db commitTransaction];
However if I create a new object, I can work around this limitation and modify the object by assigning the references to the unconstrainedObject.
ObjectA* constrainedObject = ...; // Retrieved from source with constraint applied.
ObjectA* unconstrainedObject = [[ObjectA alloc] init];
unconstrainedObject.id = contrainedObject.id;
// Change name here
unconstrainedObject.name = #"John";
unconstrainedObject.home = #"Mansion";
unconstrainedObject.age = constrainedObject.age; // Keep old property
// This illustrates the problem.
// I still want to keep some of the properties of the old
// object but have to manually type it in.
So my question is how do retrieve all the property references from the constrainedObject without manually typing that all out?
Is there a way to inspect and map the key value properties of an NSObject over to another NSObject of the same type ?
Related: https://github.com/realm/realm-cocoa/issues/1992
Related
i try to add new object to my nsmutablearray but every time it replace all object
-(void)addToStack:(Coordinate *)coord{
Coordinate*c = [[Coordinate alloc] init];
c.x=coord.x;
c.y = coord.y;
if (coord.x==0 && coord.y==0) {
c.x=coord.x+1;
[_stack addObject:c];
c.x=coord.x;
c.y=coord.y+1;
[_stack addObject:c];
c.y=coord.y;
}
}
You are not adding a new object but you are changing the old object where the reference will remain the same.
NSMutableArray addObject will not add it because it already exists in the array.
So, when trying to add a new object, first create a copy of the one that you want to change, like this:
Coordinate *newCoordinate = [Coordinate mutableCopy];
// change attributes
// add it to the array
Everybody who said that adding the same object twice deletes the first instance and replaces it, is wrong.
Arrays can contain duplicate references to the same object. However, it's more like saving the same street address in an rolodex twice. If you look up the address in the first entry, go break all the windows in that house, then go back, look up the address in the second slot in your rolodex, and drive to THAT address, you'll find the house has broken windows (Because both addresses point to the same house.)
Similarly, when you add the same object to an array twice, it's two pointers to the same object. When you change values of the object at index 0, you see those changes reflected in the object in index 1 because it's a second pointer to the same object.
Despite saying the wrong thing about what goes wrong with your code, #Shashi3456643 gave you the correct solution, which is to create a new, unique object for every entry in your array.
Make sure to initiate the array:
NSMutableArray *stack=[[NSMutableArray alloc] init];
This is because every time you init the array. Initialize the array once.
For example:
in .h
Coordinate*c;
in .m
- (void)viewDidLoad
{
c = [[Coordinate alloc] init];
}
-(void)addToStack:(Coordinate *)coord{
c.x=coord.x;
c.y = coord.y;
if (coord.x==0 && coord.y==0) {
c.x=coord.x+1;
[_stack addObject:c];
c.x=coord.x;
c.y=coord.y+1;
[_stack addObject:c];
c.y=coord.y;
}
}
Feel lost after reading this section: A Non-Object Attribute
According to the Basic-Approach also contained in above link, I should have 2 attributes in my custom-code when handling "transient properties":
1st attribute, for the actually-wanted (un-supported) custom type => transient attribute
2nd attribute, for shadow-representation (concrete supported) type => persistent attribute
......
My reading was very enjoyable, until reached "A Non-Object Attribute" section, which puzzle me deeply, as quoted below:
...When you implement the entity’s custom class, you typically add an instance variable for the attribute. ...
《 OK, I can follow this...make an iVar is no big deal》
If you use an instance variable to hold an attribute, you must also implement primitive get and set accessors
《 OK, I know how to do primitive-accessor. why need them? because internal-optimized-storage inside MO can be efficiently used, I guess.》
#interface MyManagedObject : NSManagedObject
{
 NSRect myBounds; // I assume this suppose to be the **transient attribute**
}
#property (nonatomic, assign) NSRect bounds; // I assume this is the **persistent attribute**
#property (nonatomic, assign) NSRect primitiveBounds; // because complier forces me to implement below primitive-accessors ?
#end
- (NSRect)primitiveBounds
{
return myBounds; // accessing iVAR storage for **transient attribute**? I hope so
}
- (void)setPrimitiveBounds:(NSRect)aRect
myBounds = aRect; // accessing iVAR storage for **transient attribute**? I hope so
}
From here down below, I have... too many ???????????? unsolved
- (NSRect)bounds
{
[self willAccessValueForKey:#"bounds"]; //KVO notice of access **persistent attribute**, I guess
NSRect aRect = bounds; //will this invoke primitive-Getter ???
[self didAccessValueForKey:#"bounds"];
if (aRect.size.width == 0) //bounds has not yet been unarchived, Apple explained
 {
NSString *boundsAsString = [self boundsAsString]; // unarchiving pseudo method, I guess
if (boundsAsString != nil) //if that value is not nil, transform it into the appropriate type and cache it...Apple explained.
{
bounds = NSRectFromString(boundsAsString); //will this invoke primitive-Setter???
}
}
return bounds;
}
I put my final question list here:
1, do I STILL need to have 2 attributes to handle NON-Object-Attribute, transient attribute and persistent attribute?
2, how can iVar "myBounds" be represented/connected with "#property bounds"? Is this "#property bounds" the modeled-property in a MOM?
3, what is the purpose of implementation of primitive-accessor here? for enforcing me write KVO (will...did...) methods pair? for transferring values (in and out) between iVar "myBounds"and "#property bounds"?
4, in this line of code
bounds = NSRectFromString(boundsAsString); //will this invoke primitive-Setter???
is primitive-Setter called OR public/standard-Setter gets called? Why?
In iOS, there are the very convenient NSStringFromCGRect and CGRectFromNSString functions. Why not just use those and store a string?
Your questions:
Yes, you need the 2 attributes, as explained in the documentation.
Yes, this is based on the managed object model. The primitiveX name for x is generated / interpreted automatically.
You need the primitive accessor methods here to make it KVC - which is not the case with primitives.
I have a table view that has a data source in sync with Core Data. However, I'm having a problem. Whenever I edit or delete a tableview cell, and I reload the view, I see a copy of the tableview cell that was there before it was edited. Here's some code to make it clearer.
When the view first loads, it tries to get all the "SOCommands" from "SOModule" which has a one-to-many relationship. Then, it converts it into "SOCommandTemp", so that I can work with them without altering the database.
_serverModuleCommands = [[NSMutableArray alloc]initWithArray:[self.serverModule.socommand allObjects]];
for(int i=0;i<[_serverModuleCommands count];i++)
{
SOCommandTemp* newTemp = [[SOCommandTemp alloc]init];
newTemp.commandName = ((SOCommand*)[_serverModuleCommands objectAtIndex:i]).commandName;
newTemp.sshCommand = ((SOCommand*)[_serverModuleCommands objectAtIndex:i]).sshCommand;
[_serverModuleCommands replaceObjectAtIndex:i withObject:newTemp];
}
Then, when I'm editing cells, I call methods such as these:
[_serverModuleCommands addObject:commandValues]; //commandValues is in the form of SOCommandTemp
[_serverModuleCommands replaceObjectAtIndex:_selectedCommandCell.row withObject:commandValues]; //_selectedCommandCell is an ivar that is cleared immediately after use
Then, when saving, I convert the array into SOCommand by doing this:
for(int j=0; j<[_serverModuleCommands count]; j++){
SOCommand* newCommand = [NSEntityDescription insertNewObjectForEntityForName:#"SOCommand" inManagedObjectContext:self.managedObjectContext];
newCommand.commandName = ((SOCommandTemp*)[_serverModuleCommands objectAtIndex:j]).commandName;
newCommand.sshCommand = ((SOCommandTemp*)[_serverModuleCommands objectAtIndex:j]).sshCommand;
newCommand.somodule = newModule;
}
However, before this is called, I want to make sure that I'm saving only one array item, since I added and editing one cell, so I do this:
NSLog(#"Going to Save: %#",[_serverModuleCommands description]);
And sure enough, I get only 1 array item. Then, I do save it, and exit the view controller. But when the first line:
_serverModuleCommands = [[NSMutableArray alloc]initWithArray:[self.serverModule.socommand allObjects]];
is called again, I'm getting two values in its description, one for the original and one for the edited.
Any help would be great!
~Carpetfizz
In your saving segment, you create a new SOCommand object no matter if it already exist.
Why not just use the actual objects (SOCommand) and edit them, this will not alter your DB information until you save the context.
It will save you some grieve swapping back and forth between your objects.
If you cannot edit in context, you should pass the existing item objectID to your "temp" objects and if it exist, fetch this object from DB and make the update to the existing item:
NSManagedObjectID* oID = ((SOCommandTemp*)[_serverModuleCommands objectAtIndex:j]).objectID;
if(oID) {
SOCommand* cmd = (SOCommand*)[context existingObjectWithID:oID error:nil];
if (cmd) { //no error fetching the object
//update `cmd` with your new values
}
}
I'm using a NSMutableArray to store an object called poligonon. This object has a NSMutableArray as property to store coordinates X.
But when I add the second object, the first object on the array transform itself. All objects on the array must be differents, but they are equal the last object in the array.
Exemple:
coordArrayX is a property (NSMutableArray)
-saveDataIntoArray
{
poligonon.coorArrayX = coordArrayX;
arrayPoligonon addObject: poligonon;
}
The first time that the user clicks to save, the first coordinate is 74.
Now, he creates a new poligonon that has coordinate 45, and add to the array.
When the user checks, the first poligonon has coordinate = 45.
How to solve?
Each and every time you are adding the same poligonon to the array and overwriting the value of coorArrayX.
Instead of,
{
poligonon.coorArrayX = coordArrayX;
arrayPoligonon addObject: poligonon;
}
Try this,
{
poligonon = [[Poligonon alloc] init];
poligonon.coorArrayX = coordArrayX;
[arrayPoligonon addObject:poligonon];
}
I have a 2D array of pointers to Objective-C instances to keep track of game objects on a map grid.
Now I am transitioning my code to ARC, and Xcode pointed the error. I knew pointers to objects aren't allowed as struct members, but this one caught me (almost) off guard.
I understand the rationale behind the ARC constrains, but:
I can't afford the overhead of objective-C arrays when looking up objects in the grid, and
The objects themselves are already owned by an NSArray ivar defined in the same class that has the C-style grid as an ivar; the c-style array is only a conveniently structured shortcut. Futhermore, when objects are removed from the owning NSArray, I set the corresponding grid slot to NULL.
That is, the 2D array (grid) is just a collection of fast (but dumb) pointers to objects safely retained somewhere else (the NSArray ivar).
Is there a way to get away with this using casts? For example, define and alloc my grid as:
void*** _grid;
instead of
MyMapObjectClass*** _grid
and use (appropriately bridged) casts between void* <-> MyMapObjectClass* when setting or getting the pointers in each slot?
EDIT: So here is how I solved it
I changed the ivar declaration as described above. In addition, when setting an entry of my look-up grid, I did this:
// (Done **Only Once** at map initialization)
// _objectArray is an instance of NSMutableArray
MyMapObjectClass* mapObject = [[MyMapObjectClass alloc] init];
// ...configure map object, etc...
// Add to Obj-C array:
[_objectArray addObject:mapObject];
// Add pointer to 2D C array:
_grid[i][j] = (__bridge void*)mapObject;
When accessing the object at (x,y), I do the opposite:
MyMapObjectClass* object = (__bridge MyMapObjectClass*) _grid[x][y];
[object performSomeMethod];
// etc...
When removing the object from the map, I do this:
MyMapObjectClass* object = (__bridge MyMapObjectClass*) _grid[x][y];
[_objectArray removeObject:object];
_grid[x][y] = NULL;
Map objects are created once at the beginning of the game, and removed according to game progress. If I need to replace a map object for another, I would do this:
MyMapObjectClass* oldObject = (__bridge MyMapObjectClass*) _grid[x][y];
// (should mark as weak?)
[_objectArray removeObject:oldObject];
_grid[x][y] = NULL;
MyMapObjectClass* newObject = [[MyMapObjectClass alloc] init];
[_objectArray addObject:newObject];
_grid[x][y] = (__bridge void*)newObject;
Circumventing ARC using casts is generally a bad idea. The better way would be to disable ARC for your map.m (or break out just the lookup part into a separate class).Then do manual memory management inside it with retain / release and the C structures you like, as long as you do it correctly it will work fine and you will be able to call it from other classes, avoiding the overhead of nested NSArrays etc..