From my understanding of Core Data, all that is necessary for primitive accessors to work is the #dynamic directive for the property name (as well as declaring primitive accessors for that property within the entity implementation).
For some reason, when using the generated primitive accessor the setState: method is not modifying the state property:
- (int)state
{
NSNumber * tmpValue;
[self willAccessValueForKey:#"state"];
tmpValue = [self primitiveState];
[self didAccessValueForKey:#"state"];
return [tmpValue intValue];
}
- (void)setState:(int)value
{
[self willChangeValueForKey:#"state"];
[self setPrimitiveState:[NSNumber numberWithInt:value]];
[self didChangeValueForKey:#"state"];
}
while using the key-value-coding version does modify the state property
- (int)state
{
NSNumber * tmpValue;
[self willAccessValueForKey:#"state"];
tmpValue = [self primitiveValueForKey:#"state"];
[self didAccessValueForKey:#"state"];
return [tmpValue intValue];
}
- (void)setState:(int)value
{
[self willChangeValueForKey:#"state"];
[self setPrimitiveValue:[NSNumber numberWithInt:value] forKey:#"state"];
[self didChangeValueForKey:#"state"];
}
in both cases, I primitive accessors are declared as follows (and as per Apple's example and code generation):
#interface Post (CoreDataGeneratedPrimitiveAccessors)
- (NSNumber *)primitiveState;
- (void)setPrimitiveState:(NSNumber *)value;
#end
I'm a bit at a loss to why this would be. Any help would be greatly appreciated!
After tremendous amounts of head-scratching, debugging, fiddling and guess-and-check, I've finally figured out what the problem is: Core Data primitive accessors AREN'T dynamically generated if you define those attributes as instance variables. I had defined them for debugging purposes (as GBD cannot see the values of properties without defined ivars, it seems), and this prevented primitive accessors from being generated correctly. This is something that Apple should really document in some form. As it's very difficult to discover on one's own. I hope this helps others who've been having the same issue!
I've been looking into this and one of the things discovered is that, contrary to docs, the implementation file generated from the data model does NOT list the primitive dynamic accessors. Other places state that you have to add them yourself. Could that be the issue?
Are you using/modifying the code of an NSManagedObject generated by Xcode? I believe that by default these are generated as "commented" out by an #if 0 directive.
Just wanted to say that I am having the same problem and had to switch to setPrimitiveValue and primitiveValueForKey based on your comment here. It bothers me that the default implementation does not work. Of note in my case is that I am subclassing another NSManagedObject. Not sure if that's your case as well.
Related
I have a question similar to this one:
CGLayerRef in NSValue - when to call retain() or release()?
I am drawing 24 circles as radial gradients in a view. To speed it up I am drawing the gradient into a layer and then drawing the layer 24 times. This worked really well to speed up the rendering. On subsequent drawRect calls some of the circles may need to be redrawn with a different hue, while others remain the same.
Every time through drawRect I recalculate a new gradient with the new hue and draw it into a new layer. I then loop through the circles, drawing them with the original layer/gradient or new layer/gradient as appropriate. I have a 24 element NSMutableArray that stores a CGLayerRef for each circle.
I think this is the answer provided in the question I linked above, however it is not working for me. The second time through drawRect, any circle that is drawn using the CGLayerRef that was stored in the array causes the program to crash when calling CGContextDrawLayerAtPoint. In the debugger I have verified that the actual hex value of the original CGLayerRef is stored properly into the array, and in the second time through drawRect that the same hex value is passed to CGContextDrawLayerAtPoint.
Further, I find that if I don't CGLayerRelease the layer then the program doesn't crash, it works fine. This tells me that something is going wrong with the memory management of the layer. It's my understanding that storing an object into an NSArray will increment it's reference count, and it won't be deallocated until the array releases it.
Anyway, here is the relevant code from drawRect. Down at the bottom you can see that I commented out CGLayerRelease. In this configuration the app doesn't crash although I think this is a resource leak. If I uncomment that release then the app crashes the second time though drawRect (between the first and second calls one of the circles has it's led_info.selected property cleared, indicating that it should use the saved layer rather than the new layer:
NSLog(#"ledView drawing hue=%4f sat=%4f num=%d size=%d",hue_slider_value,sat_slider_value,self.num_leds,self.led_size);
rgb_color = [UIColor colorWithHue:1.0 saturation:1.0 brightness:1.0 alpha:1.0];
end_color = [UIColor colorWithHue:1.0 saturation:1.0 brightness:1.0 alpha:0.0];
NSArray *colors = [NSArray arrayWithObjects:
(id)rgb_color.CGColor, (id)end_color.CGColor, nil];
CGColorSpaceRef colorSpace = CGColorSpaceCreateDeviceRGB();
CGGradientRef gradient = CGGradientCreateWithColors(colorSpace,(__bridge CFArrayRef) colors, NULL);
CGLayerRef layer = CGLayerCreateWithContext(context, (CGSize){self.led_size,self.led_size}, /*auxiliaryInfo*/ NULL);
if (layer) {
CGContextRef layer_context = CGLayerGetContext(layer);
CGContextDrawRadialGradient(layer_context, gradient, led_ctr,self.led_size/8,led_ctr, self.led_size/2,kCGGradientDrawsBeforeStartLocation);
} else {
NSLog(#"didn't get a layer");
}
for (int led=0;led<[self.led_info_array count];led++) {
led_info=[self.led_info_array objectAtIndex:led];
// the first time through selected=1 and led_info.cg_layer=nil for all circles,
// so this branch is taken.
if (led_info.selected || led_info.cg_layer==nil) {
CGPoint startPoint=led_info.rect.origin;
CGContextDrawLayerAtPoint(context, startPoint, layer);
CGContextAddRect(context, led_info.rect);
led_info.cg_layer=layer;
// the second time through drawRect one or more circles have been deselected.
// They take this path through the if/else
} else {
CGPoint startPoint=led_info.rect.origin;
// app crashes on this call to CGContextDrawLayerAtPoint
CGContextDrawLayerAtPoint(context, startPoint, led_info.cg_layer);
}
}
// with this commented out the app doesn't crash.
//CGLayerRelease(layer);
Here is the declaration of led_info:
#interface ledInfo : NSObject
#property CGFloat hue;
#property CGFloat saturation;
#property CGFloat brightness;
#property int selected;
#property CGRect rect;
#property CGPoint center;
#property unsigned index;
#property CGLayerRef cg_layer;
- (NSString *)description;
#end
led_info_array is the NSMutableArray of ledInfo objects, the array itself is a property of the view:
#interface ledView : UIView
#property float hue_slider_value;
#property float sat_slider_value;
#property unsigned num_leds;
#property unsigned led_size;
#property unsigned init_has_been_done;
#property NSMutableArray *led_info_array;
//#property layerPool *layer_pool;
#end
The array is initialized like this:
self.led_info_array = [[NSMutableArray alloc] init];
Edit: since I posted I have found that if I put retain/release around the assignemt into the NSMutableArray then I can also leave in the original CGLayerRelease and the app works. So I guess this is how it is supposed to work, although I'd like to know why the retain/release is necessary. In the objective C book I am reading (and the answer to the question linked above) I thought assigning into NSArray implicitly did retain/release. The new working code looks like this:
if (led_info.selected || led_info.cg_layer==nil) {
CGPoint startPoint=led_info.rect.origin;
CGContextDrawLayerAtPoint(context, startPoint, layer);
CGContextAddRect(context, led_info.rect);
if (led_info.cg_layer) CGLayerRelease(led_info.cg_layer);
led_info.cg_layer=layer;
CGLayerRetain(layer);
} else {
CGPoint startPoint=led_info.rect.origin;
CGContextDrawLayerAtPoint(context, startPoint, led_info.cg_layer);
}
You can probably tell that I'm brand new to Objective C and iOS programming, and I realize that I'm not really sticking to convention regarding case and probably other things. I'll clean that up but right now I want to solve this memory management problem.
Rob, thanks for the help. I could use a little further clarification. I think from what you are saying that there are two problems:
1) Reference counting doesn't work with CGLayerRef. OK, but it would be nice to know that while writing code rather than after debugging. What is my indication that when using "things" in Objective C/cocoa that resource counting doesn't work?
2) You say that I'm storing to a property, not an NSArray. True, but the destination of the store is the NSArray via the property, which is a pointer. The value does make it into the array and back out. Does resource counting not work like this? ie instead of CGLayerRef, if I were storing some NSObject into NSArray using the code above would resource counting work? If not, then would getting rid of the intermediate led_info property and accessing the array directly from within the loop work?
You're not storing the layer directly in an NSArray. You're storing it in a property of your ledInfo object.
The problem is that a CGLayer is not really an Objective-C object, so neither ARC nor the compiler-generated (“synthesized”) property setter will take care of retaining and releasing it for you. Suppose you do this:
CGLayerRef layer = CGLayerCreateWithContext(...);
led_info.cg_layer = layer;
CGLayerRelease(layer);
The cg_layer setter method generated by the compiler just stores the pointer in an instance variable and nothing else, because CGLayerRef isn't an Objective-C object reference. So when you then release the layer, its reference count goes to zero and it's deallocated. Now you have a dangling pointer in your cg_layer property, and when you use it later you crash.
The fix is to write the setter manually, like this:
- (void)setCg_layer:(CGLayerRef)layer {
CGLayerRetain(layer);
CGLayerRelease(_cg_layer);
_cg_layer = layer;
}
Note that it's important to retain the new value before releasing the old one. If you release the old one before retaining the new one, and the new one happens to be the same as the old one, you might deallocate the layer right in the middle!
UPDATE
In response to your edits:
Reference counting works with CGLayerRef. Automatic reference counting (ARC) doesn't. ARC only works with things that it thinks are Objective-C objects ARC does not automatically retain and release a CGLayerRef, because ARC doesn't think a CGLayerRef is a reference to an Objective-C object. An Objective-C object is (generally speaking) an instance of a class declared with #interface, or a block.
The CGLayer Reference says that CGLayer is derived from CFType, the basic type for all Core Foundation objects. (As far as ARC is concerned, a Core Foundation object is not an Objective-C object.) You need to read about “Ownership Policy” and “ Core Foundation Object Lifecycle Management” in the Memory Management Programming Guide for Core Foundation.
The “destination of the store” is an instance variable in your ledInfo object. It's not “the NSArray via the property”. The value doesn't ”make it into the array and back out.” The array gets a pointer to your ledInfo object. The array retains and releases the ledInfo object. The array never sees or does anything with the CGLayerRef. Your ledInfo object is responsible for retaining and releasing any Core Foundation objects it wants to own, like the layer in its cg_layer property.
As I mentioned, if ledInfo doesn't retain the layer (with CFRetain or CGLayerRetain) in its cg_layer setter, it risks the layer being deallocated, leaving the ledInfo with a dangling pointer. Do you understand what a dangling pointer is?
So.. CALayer KVC "extensions" handle all the weirdo's... such as..
+ (id) defaultValueForKey: (NSString*)key {
return [key isEqualToString:#"borderColor"]
? (id)cgPINK
: [super defaultValueForKey:key]; }
And Apple's Docs allude to how best to encode a struct, when they refer to the default value if you DON'T provide a value...
For example, if the value for key is a CGSize struct, the method returns a size struct containing (0.0,0.0) wrapped in an NSValue object.
However, I can't figure out how to provide normal, stinking, primitives, i.e. CGFloat
I've tried #VAL-type NSNumber encoding,
return ![key isEqualToString:#"lineWidth"] ?: #3;
and with no viable NSValue methods, even a desperate attempt to cast them to id
return ![key isEqualToString:#"zPosition"] ?: (id)262453;// lol
What's the deal? Is this just an "oh-well, ya can't" situation due to CALayer's odd-ball nature? An obvious solution I have embarrassingly overlooked? Or is it just a shady API - that refuses to document it's own shortcomings?
You should be able to return an NSNumber.
I haven't yet tried using Objective C literals. Skip that for now to limit the complications. Just use code like this:
return [NSNumber numberWithFloat: 3.0];
or numberWithBool, or numberWithInt, or whatever is appropriate.
I am making an iPhone application that goes through a set of viewcontrollers that collect data from user input. At the end of the views I encapsulate all of the data in an customized object called GELObject. Now I need to save this data in a NSMutableArray somewhere so that it can be accessed by a tableviewcontroller that is another branch off of the rootviewcontroller. I was thinking a global variable from the tableviewcontroller, but I did some research and I am reading about singleton's in the appdelegate. Some guidance would be greatly appreciated and if you're feeling extra generous a quick explanation of how to make and use singleton's because it is intriguing me.
Thanks!
To manage a singleton you create a global variable (can be restricted to one file's scope with static) with an initial sentinel value of nil, and use a class method to create the singleton on the first call.
For example:
static Something* globalSomething = nil;
#implementation Something
+ (id)
sharedSomething
{
if (! globalSomething)
{
/* can use different initializer if necessary */
globalSomething = [[[self class] allocWithZone:NULL] init];
}
return globalSomething;
}
. . .
#end
I'm using OCMock to mock some Core Data objects. Previously, I had the properties implemented with Objective-C 1.0 style explicit accessors:
// -- Old Core Data object header
#interface MyItem : NSManagedObject {}
- (NSString *) PDFName;
- (void) setPDFName:(NSString *)pdfName;
#end
// -- implementation provides generated implementations for both getter and setter
Now I've moved the code to Objective-C 2.0 and want to take advantage of the new #property syntax, and the dynamically-generated method implementations for Core Data objects:
// -- New Core Data object header
#interface MyItem : NSManagedObject {}
#property (nonatomic, retain) NSString *PDFName;
#end
// -- Core Data object implementation
#implementation MyItem
#dynamic PDFName;
#end
However, now when I create a mock item, it doesn't seem to handle the dynamic properties:
// -- creating the mock item
id mockItem = [OCMockObject mockForClass:[MyItem class]];
[[[mockItem stub] andReturn:#"fakepath.pdf"] PDFName]; // <-- throws exception here
The error looks like this:
Test Case '-[MyItem_Test testMyItem]' started.
2009-12-09 11:47:39.044 MyApp[82120:903] NSExceptionHandler has recorded the following exception:
NSInvalidArgumentException -- *** -[NSProxy doesNotRecognizeSelector:PDFName] called!
Stack trace: 0x916a4d24 0x92115509 0x97879138 0x978790aa 0x9090cb09 0x97820db6 0x97820982 0x10d97ff 0x10d9834 0x9782005d 0x9781ffc8 0x20103d66 0x20103e8c 0x20103642 0x20107024 0x20103642 0x20107024 0x20103642 0x20105bfe 0x907fead9 0x977e4edb 0x977e2864 0x977e2691 0x90877ad9 0xbf565 0xbf154 0x107715 0x1076c3 0x1082e4 0x89d9b 0x8a1e5 0x894eb 0x907e81c7 0x978019a9 0x978013da 0x907dd094 0x907ea471 0x9478c7bd 0x9478c1b9 0x94784535 0x5ede 0x326a 0x5
Unknown.m:0: error: -[MyItem_Test testMyItem] : *** -[NSProxy doesNotRecognizeSelector:PDFName] called!
Am doing something wrong? Is there another way to mock a Core Data / object with #dynamic prooperties?
Also responded to your cross-post on the OCMock Forum
Check out http://iamleeg.blogspot.com/2009/09/unit-testing-core-data-driven-apps.html.
Basically he suggests abstracting out your Core Data object's interface to a protocol, and using that protocol instead of the class where you pass instances of your core data object around.
I do this for my core data objects. Then you can use mockForProtocol:
id mockItem = [OCMockObject mockForProtocol:#protocol(MyItemInterface)];
[[[mockItem expect] andReturn:#"fakepath.pdf"] PDFName];
Works great! He also suggests creating a non-core data mock implementation of the interface which just synthesizes the properties:
#implementation MockMyItem
#synthesize PDFName;
#end
...
id <MyItemInterface> myItemStub = [[MockMyItem alloc] init] autorelease];
[myItem setPDFName:#"fakepath.pdf"];
I've used this as well, but I'm not sure it adds anything over the mockForProtocol:/stub: approach, and it's one more thing to maintain.
The above answer didn't satisfy me, because I didn't like to create a protocol for that. So I found out that there is an easier way to do that.
Instead of
[[[mockItem stub] andReturn:#"fakepath.pdf"] PDFName]; // <-- throws exception here
Just write
[[[mockItem stub] andReturn:#"fakepath.pdf"] valueForKey:#"PDFName"];
One of solutions is using a protocol, which is intended to substitute it's original interface, but it could be a bit heavy and leads to significant amount of code you should duplicate.
Personally, I found a way to make it lightweight:
Create a simple category, for instance, inside your unit testing file, just before your unit testing class:
#implementation MyItem(UnitTesing)
- (NSString *)PDFName{return nil;};
#end
Also, you can keep it in separate file, but make sure, that this file is not a part of your production target. That is why I prefer to keep it in the same test file, where I want to use it.
The huge advantage of this method, is that you should not copy methods, that are created by XCode to support relationships. Also you can put to this category only methods you are going to call inside your tests.
There are some caveats, though, for example, you should add another methods inside the category, to support setters, when you are going to check, how correct your code changes the properties of your managed object:
- (void)setPDFName:(NSString *)name{};
I'm Delphi programmer and very new to Cocoa.
at first I tried this :
-(void)awakeFromNib
{
int i;
NSString *mystr;
for (i=1;i<=24;i++)
{
[comboHour addItemWithObjectValue:i];
}
}
But it didn't work. Then I tried to search on Google but no luck.
After experimenting about 30 min, I come with this:
-(void)awakeFromNib
{
int i;
NSString *mystr;
for (i=1;i<=24;i++)
{
mystr = [[NSString alloc]initWithFormat:#"%d",i];
[comboHour addItemWithObjectValue:mystr];
//[mystr dealloc];
}
}
My questions are:
Is this the right way to do that ?
Do I always need to alloc new
NSString to change its value from
integer ?
When I uncomment [mystr dealloc],
why it won't run ?
Does it cause memory leak to alloc
without dealloc ?
Where can I find basic tutorial like
this on internet ?
Thanks in advance
Do I always need to alloc new NSString to change its value from integer ?
Generally yes; however, there are more convenient ways to create strings (and many other types of objects) than using alloc and init (see autorelease pools below)
You can pass any Objective-C object type to addItemWithObjectValue:, including NSString and NSNumber objects. Both classes have a number of convenient class methods you can use to create new instances, for example:
for (int i = 0; i < 24; ++i)
{
[comboHour addItemWithObjectValue:[NSNumber numberWithInt:i]];
}
When I uncomment [mystr dealloc], why it won't run ?
Never call dealloc. Use release instead.
Cocoa objects are reference counted, like COM objects in Delphi. Like COM, you call release when you're finished with an object. When an object has no more references it is automatically deallocated.
Unlike COM, Cocoa has "autorelease pools", which allows you to, for example, create a new NSString instance without having to worry about calling release on it.
For example: [NSString stringWithFormat:#"%d", 123] creates an "autoreleased" string instance. You don't have to release it when you're done. This is true of all methods that return an object, except new and init methods.
Does it cause memory leak to alloc without dealloc ?
Yes, unless you're using garbage collection.
Where can I find basic tutorial like this on internet ?
See Practical Memory Management
The correct way is:
-(void)awakeFromNib
{
int i;
for (i=1;i<=24;i++)
{
NSString *mystr = [[NSString alloc]initWithFormat:#"%d",i];
[comboHour addItemWithObjectValue:mystr];
[mystr release];
}
}
You can use NSNumber instead of NSString, which might be preferable depending on your context.
You do need to create a new object everytime, because addItemWithObjectValue: is expecting an object rather than a primitive.
You can create a new object (e.g. `NSString), via two methods:
Using alloc/init, like how you did it initially. Such initializations require the release of the object once it isn't required anymore in the allocation scope, using release rather than dealloc.
Using stringWithFormat: factory methods that use auto release pool to release themselves "automatically". The code would look like:
-(void)awakeFromNib
{
int i;
for (i=1; i <= 24; i++) {
NSString *s = [NSString stringWithFormat:#"%d", i];
[comboHour addItemWithObjectValue:s];
}
}
However, it is recommended not to use such construction within loops.
For memory issues, check out the Memory Management Programming Guide for Cocoa
Based on the code you posted and your stated experience level, I recommend going through Apple's Currency Converter tutorial if you haven't already. It's the standard Cocoa tutorial every beginner should read. Fundamentals like interacting with IBOutlets are covered.