Array Values in Cocoa App - Initializer not constant error - cocoa

Im trying to set an array for a timer program im writing in xcode. the values are in seconds, and what i want is to have a button in the interface builder that starts a timer with that number of seconds. This is the struct im trying to declare to provide the times in a .h header file. its just an array with 2 arrays in it, that i could call with #collegeTimes.constructive or something similar.
Thanks in advance!
- (NSDictionary *)debateTimes;
id debateTimes = [[NSDictionary alloc] initWithObjectsAndKeys:
[NSDictionary dictionaryWithObjectsAndKeys:
#"540", #"constructive",
#"360", #"rebuttal",
#"180", #"cx",
#"600", #"prep",
nil], #"collegeTimes",
[NSDictionary dictionaryWithObjectsAndKeys:
#"480", #"constructive",
#"300", #"rebuttal",
#"180", #"cx",
#"480", #"prep",
nil], #"hsTimes",
nil]; \\error is called here.

This is the struct im trying to declare to provide the times in a .h header file
This is the problem. You can not create constant NSDictionary objects (or most other NS objects, for that matter) outside of a function. One way to do what wou want would be as follows:
SomeThing.h
#interface SomeThing : NSObject
{
...
}
+ (NSDictionary *)debateTimes;
#end
SomeThing.m
static NSDictionary * staticDebateTimes = nil;
#implementation SomeThing
...
+ (NSDictionary *)debateTimes
{
if (staticDebateTimes == nil)
{
staticDebateTimes = [[NSDictionary alloc] initWithObjectsAndKeys:
[NSDictionary dictionaryWithObjects:...
}
return staticDebateTimes;
}
#end
This code would now be used externally as follows:
NSDictionary * debateTimes = [SomeThing debateTimes];

You cannot assign an objective-c object to a variable outside of a function. When a variable is assigned outside a function, its value becomes part of the executable. Since the value of the pointer to a object is not known until runtime, you cannot assign the object until it is created. (Constant NSStrings are an exception to this as they are also part of the executable)
The best way to store a structure like this would be to use an array of c structures.
typedef struct {
char *name;
NSTimeInterval constructive;
NSTimeInterval rebuttal;
NSTimeInterval cx;
NSTimeInterval prep;
} DebateTime;
DebateTime[2] = {{"collegeTimes", 540, 360, 180, 600},
{"hsTimes", 480, 300, 180, 480}};
You can also change the name and time intervals to constant strings if you wish.

Related

Filtering with NSPredicate - Data Type?

I wish to filter an array using a predicate but I'm being curved balled with the correct handling of data types.
First off, I have a list I want to filter with:
NSMutableArray *HospitalIDs = [[NSMutableArray alloc]init];
[HospitalIDs addObject: #"1"]; // Must be "id", int & NSInteger not accepted by XCode
[HospitalIDs addObject: #"2"]; // Must be "id", int & NSInteger not accepted by XCode
[HospitalIDs addObject: #"3"]; // Must be "id", int & NSInteger not accepted by XCode
Secondly, I have a list of objects that I want to filter using the array above:
(It's defined as NSMutableArray *HospitalObjects and is pre-populated with HospitalObjects)
NSPredicate *predicate = [NSPredicate predicateWithFormat:#"SELF.HospitalID IN %#", HospitalIDs];
[HospitalObjects filterUsingPredicate:predicate];
NSLog(#"%i",[HospitalObjects count]);
//The expected result is 3, but it prints 0.
WHY?
Because by first array contains NSString values, but my HospitalObject.HospitalID is of type int.
I don't really want to change the data type of HospitalID in my object definition to NSString, but I cannot filter on integer values, what do I do?
Can I convert the data type WITHIN the predicate syntax? How?
First of all, the best way to store numbers in NSArray is to use NSNumber objects. You can simply convert a NSInteger to NSNumber using Objective-C literal syntax:
[HospitalIDs addObject: #(1)];
[HospitalIDs addObject: #(2)];
[HospitalIDs addObject: #(3)];
Then your predicate should work without any further changes.
There is a Type NSNumber that wraps ints, floats, bools, .. in a object.
NSMutableArray * hospitalIDs = [[NSMutableArray alloc] init];
[hospitalIDs addObject: #1]; // You can wrap an normal int into an NSNumber jusing #. In some siztuations you have to use #(yourOrdinaryValueTypeVariable)
[hospitalIDs addObject: #2];
[hospitalIDs addObject: #3];
By the way try to use proper naming using CamelCase:
Classes start with a upper case: SomeClassName
Objects, variables and method names with lower case: `SomeClassName* someObject;
Constants are all upper case: MY_CONSTANT

Add Mutable Dictionary to a Mutable Dictionary for conversion to JSON

I am trying to build a dictionary which has as dictionary within it (eventually I hope to convert to a JSON). The problem is I am having problems building it.
So far I have this, what it should do is build a small dictionary with keys and add it to a larger dictionary, reset and then load the small dict and then add it to the large one.
NSMutableDictionary *nestedList = [[NSMutableDictionary alloc]init];
NSMutableDictionary *nestedSections = [[NSMutableDictionary alloc] init];
[nestedList addEntriesFromDictionary:[NSDictionary dictionaryWithObjectsAndKeys:
[NSNumber numberWithInt:46], #"menuHeight",
#"editText", #"menuMethod",
[NSNumber numberWithInt:1], #"menuOption",
nil]];
[nestedSections addEntriesFromDictionary:[NSDictionary dictionaryWithObjectsAndKeys:
nestedList, "#Basic",
nil]];
[nestedList removeAllObjects];
[nestedList addEntriesFromDictionary:[NSDictionary dictionaryWithObjectsAndKeys:
[NSNumber numberWithInt:92], #"menuHeight",
#"sendText", #"menuMethod",
[NSNumber numberWithInt:1], #"menuOption",
nil]];
[nestedSections addEntriesFromDictionary:[NSDictionary dictionaryWithObjectsAndKeys:
nestedList, "#Pro",
nil]];
I then hoped to address like so;
NSString *string = [[nestedSections objectForKey:#"Pro"] objectForKey:#"menuMethod"];
NSLog(#"Method is : %#", string);
Log would hope to read sendText
The first dictionary builds fine but as soon as I try and add it to the second t bums out with EXC_BAD_ACCESS
I think it is a memory addressing problem because they are both mutable but I am not sure, maybe nestedList should not be mutable. Any help appreciated.
Ultimately I would like to convert this to a JSON like;
{
"Basic":
{
"menuHeight":"46",
"menuMethod":"editText",
"menuOption":"1",
},
"Pro":
{
"menuHeight":"96",
"menuMethod":"sendText",
"menuOption":"1",
}
}
A. NSMutableDictionary does not copy the values (only the keys). Therefore you add the same dictionary two times and change both (= the one) when removing objects and so on. Beside this in your sample JSON the numbers looks like strings not like numbers. I think, that this is a typo.
B. Adding modern Objective-C for better readability it should look like this:
NSDictionary *basicDictionary =
#{
#"menuHeight" : #46,
#"menuMethod" : "editText",
#"menuOption : #1
}
NSDictionary *proDictionary =
#{
#"menuHeight" : #96,
#"menuMethod" : "sendText",
#"menuOption : #1
}
NSDictionary *nestedSections = #{ #"Pro" : proDictionary, #"Basic" : basicDictionary };

analyze and memory alerts in xcode

I ran 'analyze" in xcode on a current iOS project to try to track down a freeze issue and there are a number of memory alerts that I don't understand (screenshot of one below).
What is going on there: I have a custom ObjC class extending NSObject; in the init method I alloc/init an NSMutableArray and then in a loop, populate it with NSMutableArrays. This nested array is declared as a property and released in dealloc(). It lives for the life of the app.
Am I doing this wrong? I don't understand the alert#3: # object not referenced in this execution path and has a retain count of +1.
Since my class allocs the outer array, it owns it and will clean it up. Do the inner arrays need to be released?
Thanks for any tips - still new at this.
EDIT/ADDITION
Trying to stamp out the additional memory warnings I am getting so I thought I would add to the question here in the event someone stumbles upon this w/ the same issue.
I am getting the following alert with the code below (the 2nd line "[keyArray addObject: etc"). What is going on: I have a custom class (Key - based on NSObject) that I instance and store in an array. Based on answers to my previous question, I guess my alloc increases the retain count and then when it is added to the array, the retain count isn't decremented - so the memory warning occurs.
What is the proper way to handle something like this? Use a placeholder like this:
Key * k = [[Key alloc] initKeyWithPath:path isBlackKey:NO]];
[keyArray addObject: k];
[k release];
Is that the proper way to do it? Or is there I way to write the custom class to return an autoreleased obj? (thanks and sorry to be so long winded!).
Potential leak of an object allocated on line 460
Method returns an Objective-C object with a +1 retain count (owning reference)
Object allocated on line 460 is not referenced later in this execution path and has a retain count of +1 (object leaked)
-(void) addOctaveToArraysWithTransform:(CGAffineTransform*)trans andPath: (CGMutablePathRef) path
{
path = [self createCF_keyUsingTransform: trans];
[keyArray addObject:[[Key alloc] initKeyWithPath:path isBlackKey:NO]];
}
Key.h
#import <Foundation/Foundation.h>
#import "Key.h"
#interface Key : NSObject {
#public
CGMutablePathRef keyPath;
BOOL isBlackKey;
NSValue * path;
int keyState;
BOOL needsRedraw;
}
#property (nonatomic, assign) int keyState;
#property (nonatomic, assign) BOOL needsRedraw;
#property (nonatomic) CGMutablePathRef keyPath;
-(id) initKeyWithPath:(CGMutablePathRef) aPath isBlackKey:(BOOL)flag;
-(CGMutablePathRef) getKeyPath;
#end
Key.m
#import "Key.h"
#implementation Key
#synthesize keyState, needsRedraw, keyPath;
-(id) initKeyWithPath:(CGMutablePathRef) aPath isBlackKey:(BOOL)flag
{
if ((self = [super init])){
isBlackKey = flag;
keyState = 0;
needsRedraw = NO;
keyPath = aPath;
CGPathRetain(keyPath);
}
return self;
}
-(CGMutablePathRef) getKeyPath
{
return keyPath;
}
#end
Yes, you have to release the inner arrays to balance the alloc/init. Remember the outer array will retain each inner array, and the outer array will presumably release those later. But here you are still responsible for the alloc/init you just did.
Hope that helps.
You have an allocation of an NSMutableArray on each iteration of the for-loop. Instead use: NSMutableArray array] which is a convenience method that return an autoreleased NSMUtableArray suitable for adding to fieldNotes which will retain the NSMutableArray.

Core Data Transformable attributes NOT working with NSPredicate

I often use Transformable for Core Data attributes, so I can change them later.
However, it seems like, if I want to use NSPredicate to find a NSManagedObject, using "uniqueKey == %#", or "uniqueKey MATCHES[cd] %#", it's not working as it should.
It always misses matching objects, until I change the attributes of the uniqueKey of the matching object to have specific class like NSString, or NSNumber.
Can someone explain the limitation of using NSPredicate with Transformable attributes?
Note: I'm not sure when/if this has changed since 5/2011 (from Scott Ahten's accepted answer), but you can absolutely search with NSPredicate on transformable attributes. Scott correctly explained why your assumptions were broken, but if Can someone explain the limitation of using NSPredicate with Transformable attributes? was your question, he implied that it is not possible, and that is incorrect.
Since the is the first google hit for "Core Data transformable value search nspredicate" (what I searched for trying to find inspiration), I wanted to add my working answer.
How to use NSPredicate with transformable properties
Short, heady answer: you need to be smart about your data transformers. You need to transfrom the value to NSData that contains what I'll call "primitive identifying information", i.e. the smallest, most identifying set of bytes that can be used to reconstruct your object. Long answer, ...
Foremost, consider:
Did you actual mean to use a transformable attribute? If any supported data type -- even binary data -- will suffice, use it.
Do you understand what transformable attributes actually are? How they pack and unpack data to and from the store? Review Non-Standard Persistent Attributes in Apple's documentation.
After reading the above, ask: does custom code that hides a supported type "backing attribute" work for you? Possibly use that technique.
Now, past those considerations, transformable attributes are rather slick. Frankly, writing an NSValueTransformer "FooToData" for Foo instances to NSData seemed cleaner than writing a lot of adhoc custom code. I haven't found a case where Core Data doesn't know it needs to transform the data using the registered NSValueTransformer.
To proceed simply address these concerns:
Did you tell Core Data what transformer to use? Open the Core Data model in table view, click the entity, click the attribute, load the Data Model Inspector pane. Under "Attribute Type: Transformable", set "Name" to your transformer.
Use a default transformer (again, see the previous Apple docs) or write your own transformer -- transformedValue: must return NSData.
NSKeyedUnarchiveFromDataTransformerName is the default transformer and may not suffice, or may draw in somewhat-transient instance data that can make two similar objects be different when they are equal.
The transformed value should contain only -- what I'll call -- "primitive identifying information". The store is going to be comparing bytes, so every byte counts.
You may also register your transformer globally. I have to do this since I actually reuse them elsewhere in the app -- e.g. NSString *name = #"FooTrans"; [NSValueTransformer setValueTransformer:[NSClassFromString(name) new] forName:name];
You probably don't want to use transforms heavily queried data operations - e.g. a large import where the primary key information uses transformers - yikes!
And then in the end, I simply use this to test for equality for high-level object attributes on models with NSPredicates -- e.g. "%K == %#" -- and it works fine. I haven't tried some of the various matching terms, but I wouldn't be surprised if they worked sometimes, and others not.
Here's an example of an NSURL to NSData transformer. Why not just store the string? Yeah, that's fine -- that's a good example of custom code masking the stored attribute. This example illustrates that an extra byte is added to the stringified URL to record if it was a file URL or not -- allowing us to know what constructors to use when the object is unpacked.
// URLToDataTransformer.h - interface
extern NSString *const kURLToDataTransformerName;
#interface URLToDataTransformer : NSValueTransformer
#end
...
// URLToDataTransformer.m - implementation
#import "URLToDataTransformer.h"
NSString *const kURLToDataTransformerName = #"URLToDataTransformer";
#implementation URLToDataTransformer
+ (Class)transformedValueClass { return [NSData class]; }
+ (BOOL)allowsReverseTransformation { return YES; }
- (id)transformedValue:(id)value
{
if (![value isKindOfClass:[NSURL class]])
{
// Log error ...
return nil;
}
NSMutableData *data;
char fileType = 0;
if ([value isFileURL])
{
fileType = 1;
data = [NSMutableData dataWithBytes:&fileType length:1];
[data appendData:[[(NSURL *)value path] dataUsingEncoding:NSUTF8StringEncoding]];
}
else
{
fileType = -1;
data = [NSMutableData dataWithBytes:&fileType length:1];
[data appendData:[[(NSURL *)value absoluteString] dataUsingEncoding:NSUTF8StringEncoding]];
}
return data;
}
- (id)reverseTransformedValue:(id)value
{
if (![value isKindOfClass:[NSData class]])
{
// Log error ...
return nil;
}
NSURL *url = nil;
NSData *data = (NSData *)value;
char fileType = 0;
NSRange range = NSMakeRange(1, [data length]-1);
[data getBytes:&fileType length:1];
if (1 == fileType)
{
NSData *actualData = [data subdataWithRange:range];
NSString *str = [[NSString alloc] initWithData:actualData encoding:NSUTF8StringEncoding];
url = [NSURL fileURLWithPath:str];
}
else if (-1 == fileType)
{
NSData *actualData = [data subdataWithRange:range];
NSString *str = [[NSString alloc] initWithData:actualData encoding:NSUTF8StringEncoding];
url = [NSURL URLWithString:str];
}
else
{
// Log error ...
return nil;
}
return url;
}
#end
Transformable attributes are usually persisted as archived binary data. As such, you are attempting to compare an instance of NSData with an instance of NSString or NSNumber.
Since these classes interpret the same data in different ways, they are not considered a match.
you can try this way
NSExpression *exprPath = [NSExpression expressionForKeyPath:#"transformable_field"];
NSExpression *exprKeyword = [NSExpression expressionForConstantValue:nsdataValue];
NSPredicate *predicate = [NSComparisonPredicate predicateWithLeftExpression:exprPath rightExpression:exprKeyword modifier:NSDirectPredicateModifier type:NSEqualToPredicateOperatorType options:0];

NSArray to Core Data items

I have an method that reads an xml file and stores the xml nodes at a certain XPath-path in an NSArray called *nodes. What I want to do is take each one of the items in the array and add it to a core data entity called Category with the attribute of "name".
I have tried a number of different ways of creating the entity but I'm not sure about the correct way to do this effectively. This is the code used to create the NSArray, any ideas on how to implement this? (ignore the NSError, I will fix this in the final version)
- (IBAction)readCategories:(id)sender
{
NSString *xmlString = [resultView string];
NSData *xmlData = [xmlString dataUsingEncoding: NSASCIIStringEncoding];
NSXMLDocument *xmlDoc = [[NSXMLDocument alloc] initWithData:xmlData options:nil error:nil];
//XPath
NSError *err=nil;
NSArray *nodes = [xmlDoc nodesForXPath:#"//member[name='description']/value/string" error:&err];
}
EDIT - My loop code
NSArray *nodes = [xmlDoc nodesForXPath:#"//member[name='description']/value/string" error:&err];
int arrayCount = [nodes count];
NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
NSXMLElement *categoryEl;
NSString *new = [catArrayController newObject];
int i;
for (i = 0; i < arrayCount; i++)
{
[categoryEl = [nodes objectAtIndex:i]];
[new setValue:[categoryEl stringValue] forKey:#"name"];
[catArrayController addObject:new];
}
[pool release];
Here's how I'd write it:
for (NSXMLElement *categoryElement in nodes) {
NSManagedObject *newObject = [catArrayController newObject];
[newObject setValue:[categoryElement stringValue] forKey:#"name"];
[catArrayController addObject:newObject];
[newObject release];
}
First, I'm using the Objective-C 2.0 for-each syntax. This is simpler than using index variables. I eliminated i and arrayCount.
Next, I took out your NSAutoreleasePool. None of the objects in the loop are autoreleased, so it had no effect. (The newObject method returns a retained object which is, by convention, what methods with the word new in their name do) This is also why I release newObject after adding it to the array controller. Since I'm not going to be using it any more in this method, I need to release it.
Also, you had defined new (which I renamed newObject) as an NSString. Core Data objects are always either an instance of NSManagedObject or a subclass of NSManagedObject.
Your line [categoryEl = [nodes objectAtIndex:i]] won't compile. That's because the bracket syntax is used to send a message to an object. This is an assignment statement, so the bracket syntax is not needed here. (This line is also not necessary any more because of I've changed the loop to use the for-each syntax) But, for future reference, categoryEl = [nodes objectAtIndex:i]; would have worked.
What part are you having trouble with? There shouldn't be much more to it than looping through the array, creating a new managed object for each entry, and setting the correct attributes. You can create the managed object with NSEntityDescription's -insertNewObjectForEntityForName:inManagedObjectContext: method.

Resources