I'm not sure if this is possible, but I've seen people do crazy things with regex and other tools.
I want to convert this plist to an Objective-C literals:
<dict>
<key>ar</key>
<array>
<string>+54## #### ####</string>
<string>## #### ####</string>
</array>
<key>at</key>
<array>
<string>+43 1 ########</string>
<string>+43 ############</string>
</dict>
converted to:
NSDictionary *dic = #{
#"ar" : #[#"+54## #### ####", #"## #### ####"],
#"at" : #[#"+43 1 ########",#"+43 ############"]
};
Is it possible to automate such conversion? This guy did something similiar: he parsed a PHP list into an NSDictionary using VIM.
Plist don't have a separate 'format' for use in code (this question doesn't quite make sense as-is). You either want to 1. generate Objective-C code which initializes the dictionary with these values, or 2. initialize the dictionary using the file, for which you can write
NSDictionary *dict = [NSDictionary dictionaryWithContentsOfFile:#"UIPhoneFormats.plist"];
Edit: so you want to generate Objective-C code that in turn will reproduce the same dictionary. For this, you need to re-print the contents of the dictionary in a formatted way. You can write a program like this:
#import <stdio.h>
#import <Foundation/Foundation.h>
NSString *recursiveDump(id object)
{
if ([object isKindOfClass:[NSString class]]) {
return [NSString stringWithFormat:#"#\"%#\"", object];
} else if ([object isKindOfClass:[NSNumber class]]) {
return [NSString stringWithFormat:#"#%#", object];
} else if ([object isKindOfClass:[NSArray class]]) {
NSMutableString *str = [NSMutableString stringWithString:#"#["];
NSInteger size = [object count];
NSInteger i;
for (i = 0; i < size; i++) {
if (i > 0) [str appendString:#", "];
[str appendString:recursiveDump([object objectAtIndex:i])];
}
[str appendString:#"]"];
return str;
} else if ([object isKindOfClass:[NSDictionary class]]) {
NSMutableString *str = [NSMutableString stringWithString:#"#{"];
NSString *key;
NSInteger size = [object count];
NSArray *keys = [object allKeys];
NSInteger i;
for (i = 0; i < size; i++) {
if (i > 0) [str appendString:#", "];
key = [keys objectAtIndex:i];
[str appendFormat:#"%#: %#", recursiveDump(key), recursiveDump([object objectForKey:key])];
}
[str appendString:#"}"];
return str;
} else {
// feel free to implement handling NSData and NSDate here,
// it's not that straighforward as it is for basic data types.
}
}
int main(int argc, char *argv[])
{
NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
NSDictionary *dict = [NSDictionary dictionaryWithContentsOfFile:#"UIPhoneFormats.plist"];
NSMutableString *code = [NSMutableString stringWithString:#"NSDictionary *dict = "];
[code appendString:recursiveDump(dict)];
[code appendString:#";"];
printf("%s\n", [code UTF8String]);
[pool release];
return 0;
}
This program will generate (hopefully syntax error-free) Objective-C initialization code out of the provided property list which can be copy-pasted to a project and be used.
Edit: I just run the program on a stripped version of the plist file OP has provided (the original file was way too large, so I cut it a bit) and it generated the following code:
NSDictionary *dict = #{#"at": #[#"+43 1 ########", #"+43 ############", #"01 ########", #"00 $"], #"ar": #[#"+54## #### ####", #"## #### ####", #"00 $", #"18 ### $ "]};
To verify it was really valid, I pasted this into the middle of an int main() to a file called 'test.m', so I got this program:
#import <Foundation/Foundation.h>
int main()
{
NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
NSDictionary *dict = #{#"at": #[#"+43 1 ########", #"+43 ############", #"0$
NSLog(#"%#", dict);
[pool release];
return 0;
}
To verify, I run clang -o test test.m -lobjc -framework Foundation and surprise, surprise:
It worked.
Edit 2: I made this a command line utility, just to facilitate further work - who knows, this may be useful in the future. Plist2ObjC
Hope this helps.
What you need is Serializing a Property List
NSData* plistData = [source dataUsingEncoding:NSUTF8StringEncoding];
NSString *error;
NSPropertyListFormat format;
NSDictionary* plist = [NSPropertyListSerialization propertyListFromData:plistData mutabilityOption:NSPropertyListImmutable format:&format errorDescription:&error];
NSLog( #"plist is %#", plist );
if(!plist){
NSLog(#"Error: %#",error);
[error release];
}
If you want to get it as a string, use this,
NSString *aString = [NSString stringWithFormat:#"%#", plist];
or else you can call NSString *aString = [plist description] to get the same as a string.
Source
Related
This code had been working fine until just recently. I hadn't' changed anything nor upgraded my system and I'm completely flummoxed.
I've been using it for 6 years and now it dies on me.
Is there an easier or better way of running an applescript from within a cocoa application? At this point I'm happy to pay to fix this problem!
utils.h
#import <Foundation/Foundation.h>
#interface Utils : NSObject
// Runs an applescript with a given map of variables (name/value)
+ (NSArray *)runApplescript:(NSString *)source withVariables:(NSDictionary *)variables;
// Runs an applescript from a file pathwith a given map of variables
// (name/value)
+ (NSArray *)runApplescriptFromFile:(NSString *)scriptName withVariables:(NSDictionary *)variables;
+ (NSArray *)arrayFromDescriptor:(NSAppleEventDescriptor *)descriptor;
// String is empty or only has white characters (space, tab...)
+ (BOOL)stringIsEmptyOrWhite:(NSString *)string;
#end
Utils.M
#import "Utils.h"
#implementation Utils
+ (NSArray *)arrayFromDescriptor:(NSAppleEventDescriptor *)descriptor {
// Enumerate the apple descriptors (lists) returned by the applescript and
// make them into arrays
NSMutableArray *returnArray = [NSMutableArray array];
NSInteger counter, count = [descriptor numberOfItems];
for (counter = 1; counter <= count; counter++) {
NSAppleEventDescriptor *desc = [descriptor descriptorAtIndex:counter];
if (nil != [desc descriptorAtIndex:1]) {
[returnArray addObject:[Utils arrayFromDescriptor:desc]];
} else {
NSString *stringValue = [[descriptor descriptorAtIndex:counter] stringValue];
if (nil != stringValue) {
[returnArray addObject:stringValue];
} else {
[returnArray addObject:#""];
}
}
}
return returnArray;
}
+ (NSString *)escapeCharacters:(NSString *)string {
return [string stringByReplacingOccurrencesOfString:#"\"" withString:#"\\\""];
}
+ (NSArray *)runApplescript:(NSString *)source withVariables:(NSDictionary *)variables {
NSString *input = #"";
NSArray *variableNames = [variables allKeys];
// Transform the dictionary of names/values to set sentences of applescript
for (NSString *variableName in variableNames) {
NSObject *variableValue = [variables objectForKey:variableName];
if ([variableValue isKindOfClass:[NSString class]]) {
input =
[input stringByAppendingString:[NSString stringWithFormat:#"set %# to (\"%#\" as text)\n", variableName,
[Utils escapeCharacters:variableValue], nil]];
} else if ([variableValue isKindOfClass:[NSNumber class]]) {
input = [input stringByAppendingString:[NSString stringWithFormat:#"set %# to (%# as integer)\n",
variableName, variableValue, nil]];
} else if ([variableValue isKindOfClass:[NSArray class]]) {
// Initialize a list
NSString *entry;
NSArray *values = (NSArray *)variableValue;
input = [input stringByAppendingString:[NSString stringWithFormat:#"set %# to {", variableName]];
BOOL first = TRUE;
for (entry in values) {
if (!first) {
input = [input stringByAppendingString:#", "];
}
input = [input
stringByAppendingString:[NSString stringWithFormat:#"\"%#\"", [Utils escapeCharacters:entry], nil]];
first = FALSE;
}
input = [input stringByAppendingString:#"}\n"];
}
}
NSString *finalScript = [input stringByAppendingString:[NSString stringWithFormat:#"\n\n%#", source]];
NSLog(#"Final script: %#", finalScript);
NSAppleScript *script = [[NSAppleScript alloc] initWithSource:finalScript];
NSDictionary *error;
NSAppleEventDescriptor *descriptor = [script executeAndReturnError:&error];
NSLog(#"applescript error: %#", [error description]);
// Transform the return value of applescript to nested nsarrays
return [Utils arrayFromDescriptor:descriptor];
}
+ (NSArray *)runApplescriptFromFile:(NSString *)scriptName withVariables:(NSDictionary *)variables {
NSString *scriptPath = [[NSBundle mainBundle] pathForResource:scriptName ofType:#"applescript"];
NSString *scriptSource =
[[NSString alloc] initWithContentsOfFile:scriptPath encoding:NSASCIIStringEncoding error:nil];
return [Utils runApplescript:scriptSource withVariables:variables];
}
+ (BOOL)stringIsEmptyOrWhite:(NSString *)string {
string = [string stringByTrimmingCharactersInSet:[NSCharacterSet whitespaceCharacterSet]];
return [string isEqualToString:#""];
}
#end
Easier, yes, although whether that’s your actual problem is another question.
http://appscript.sourceforge.net/asoc.html
I assume you’ve already got other details, including sandboxing and hardening settings and plist entries, taken care of. (Recent Xcode upgrades also had a habit of breaking it when auto-upgrading your project files, by turning on hardening for you so Apple events can’t get out.)
This question already has answers here:
"Thread 1: stopped at breakpoint" error when initializing an NSURL object
(3 answers)
Closed 7 years ago.
I am trying to run the following in Xcode:
#import <Foundation/Foundation.h>
int main(int argc, const char * argv[]) {
#autoreleasepool {
// NSLog(#"Hello, my name is Travis");
// NSString *firstName = #"Variable";
// NSLog(#"Hello, my name is %#", firstName);
// NSString *lastName = #"Last Name";
// NSLog((#"First name: %#, Last name: %#", firstName, lastName));
// NSNumber *age = #26;
// NSLog(#"%# age is %#", firstName, age);
// NSArray *items = #[#"item1", #"item2", #"item3"];
// NSLog(#"%#",items[1]);
// NSLog(#"%#", [items description]);
// NSString *result = [items description];
// NSLog(result);
NSString *city = #"San Francisco";
NSUInteger cityLength = [city length];
NSLog(#"%lu", cityLength);
// NSMutableArray *mystr = [NSMutableArray arrayWithObjects:#"hello",#"world", nil];
// NSLog(#"%#", mystr[0]);
// NSString *me = #"FROM ME!";
// [mystr addObject: me];
// NSLog(#"%#", mystr[2]);
// NSDictionary *appRatings = #{#"item1": #4, #"item2": #3};
// NSLog(#"%#", [appRatings allKeys]);
// NSLog(#"%#", [appRatings objectForKey:#"item1"]);
// NSLog(#"%#", [appRatings allValues]);
// NSLog(#"%#", [appRatings valueForKey:#4]);
}
}
I'm simply trying to run the code thats uncommented. Seems simple, should just count the number of characters within my string variable. When I run this, the build succeeds but nothing is put into the console. The only thing that appears is: (lldb)
The lldb means the code is paused somewhere at a breakpoint, and is waiting for a lldb command. Either disable breakpoints or find and remove them, and try again.
Thanks to a lot of help I've received here on SO, I've gotten an algorithm to check a list of around 15,000 8-letter words for any partial anagrams, against a list of around 50,000 total words (so I suppose a total of 108 million iterations). I call this method once for each comparison (so 750 million times). I'm getting the following error, always somewhere in the midst of the 119th iteration through the 1,350 there should be:
AnagramFINAL(2960,0xac8c7a28) malloc: *** mmap(size=2097152) failed (error code=12)
*** error: can't allocate region
*** set a breakpoint in malloc_error_break to debug
I've narrowed the memory issue down to being a huge number of allocated CFStrings (immutable). Any idea what I can do to remedy the issue? I'm using ARC and an #autoreleasepool, not sure what else I could do, it seems something isn't being released when it should be.
AnagramDetector.h
#import <Foundation/Foundation.h>
#interface AnagramDetector : NSObject {
NSDictionary *allEightLetterWords;
NSDictionary *allWords;
NSFileManager *fileManager;
NSArray *paths;
NSString *documentsDirectory;
NSString *filePath;
}
- (BOOL) does: (NSString *) longWord contain: (NSString *) shortWord;
- (NSDictionary *) setupAllWordList;
- (NSDictionary *) setupEightLetterWordList;
- (void) saveDictionary: (NSMutableDictionary *)currentArray;
#end
AnagramDetector.m
#implementation AnagramDetector
- (id) init {
self = [super init];
if (self) {
fileManager = [NSFileManager defaultManager];
paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
documentsDirectory = [paths objectAtIndex:0];
}
return self;
}
- (BOOL) does: (NSString *) longWord contain: (NSString *) shortWord {
#autoreleasepool {
NSMutableString *longerWord = [longWord mutableCopy];
for (int i = 0; i < [shortWord length]; i++) {
NSString *letter = [shortWord substringWithRange: NSMakeRange(i, 1)];
NSRange letterRange = [longerWord rangeOfString: letter];
if (letterRange.location != NSNotFound) {
[longerWord deleteCharactersInRange: letterRange];
} else {
return NO;
}
}
return YES;
}
}
- (NSDictionary *) setupAllWordList {
#autoreleasepool {
NSString *fileWithAllWords = [[NSBundle mainBundle] pathForResource:#"AllDefinedWords" ofType:#"plist"];
allWords = [[NSDictionary alloc] initWithContentsOfFile: fileWithAllWords];
NSLog(#"Total number of words: %d.", [allWords count]);
}
return allWords;
}
- (NSDictionary *) setupEightLetterWordList {
#autoreleasepool {
NSString *fileWithEightWords = [[NSBundle mainBundle] pathForResource:#"AllDefinedEights" ofType:#"plist"];
allEightLetterWords = [[NSDictionary alloc] initWithContentsOfFile: fileWithEightWords];
NSLog(#"Total number of words: %d.", [allEightLetterWords count]);
}
return allEightLetterWords;
}
- (void) saveDictionary: (NSMutableDictionary *)currentArray {
#autoreleasepool {
filePath = [documentsDirectory stringByAppendingPathComponent: #"A.plist"];
[fileManager createFileAtPath:filePath contents: nil attributes: nil];
[currentArray writeToFile: filePath atomically:YES];
[currentArray removeAllObjects];
}
}
#end
Code running on launch (inside AppDelegate for now, since no VC):
#autoreleasepool {
AnagramDetector *detector = [[AnagramDetector alloc] init];
NSDictionary *allWords = [[NSDictionary alloc] initWithDictionary:[detector setupAllWordList]];
NSDictionary *eightWords = [[NSDictionary alloc] initWithDictionary:[detector setupEightLetterWordList]];
int remaining = [eightWords count];
for (NSString *currentEightWord in eightWords) {
if (remaining % 10 == 0) NSLog(#"%d ::: REMAINING :::", remaining);
for (NSString *currentAllWord in allWords) {
if ([detector does: [eightWords objectForKey: currentEightWord] contain: [allWords objectForKey: currentAllWord]]) {
// NSLog(#"%# ::: CONTAINS ::: %#", [eightWords objectForKey: currentEightWord], [allWords objectForKey: currentAllWord]);
}
}
remaining--;
}
}
The problem seems to be that a lot of autoreleased objects fill up the memory waiting to be released. So a solution is to add your own autorelease pool scope to collect autoreleased objects and release them sooner.
I suggest that you do something like this:
for (NSString *currentEightLetterWord in [eightLetterWordsDictionary allKeys]) {
#autoreleasepool {
for (NSString *currentWord in [allWordsDictionary allKeys]) {
}
}
}
Now all autoreleased objects inside #autoreleasepool { .. } will be released for each iteration of the outer loop.
As you see ARC might save you from thinking about most reference counting and memory management issues but objects can still end up in autorelease pools with ARC when using methods that directly or indirectly create autoreleased objects.
An alternative solution that I don't really recommend is to try to avoid using method that will use autorelease. Then does:contain: could awkwardly be rewritten to something like this:
- (BOOL) does: (NSString* ) longWord contain: (NSString *) shortWord {
NSMutableString *haystack = [longWord mutableCopy];
NSMutableString *needle = [shortWord mutableCopy];
while([haystack length] > 0 && [needle length] > 0) {
NSMutableCharacterSet *set = [[NSMutableCharacterSet alloc] init];
[set addCharactersInRange:NSMakeRange([needle characterAtIndex:0], 1)];
if ([haystack rangeOfCharacterFromSet:set].location == NSNotFound) return NO;
haystack = [haystack mutableCopy];
[haystack deleteCharactersInRange:NSMakeRange(0, [haystack rangeOfCharacterFromSet: set].location)];
needle = [needle mutableCopy];
[needle deleteCharactersInRange:NSMakeRange(0, 1)];
}
return YES;
}
I want to be able to take an object and write out all its properties to a PLIST. I got so far with this:
// Get the properties of the parent class
NSMutableArray *contentViewPropertyNames = [self propertyNamesOfObject:[contentView superclass]];
// Add the properties of the content view class
[contentViewPropertyNames addObjectsFromArray:[self propertyNamesOfObject:contentView]];
// Get the values of the keys for both the parent class and the class itself
NSDictionary *keyValuesOfProperties = [contentView dictionaryWithValuesForKeys:contentViewPropertyNames];
// Write the dictionary to a PLIST
NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
NSString *documentsDirectory = [paths objectAtIndex:0];
NSString *pathAndFileName = [documentsDirectory stringByAppendingPathComponent:[dataFileName stringByAppendingString:#".plist"]];
[keyValuesOfProperties writeToFile:pathAndFileName atomically:YES];
All good, except I can't write this to a PLIST because it contains some properties that are not compliant with PLISTs, so writeToFile:atomically: fails and returns NO.
Is there a good way to serialize only those properties that are serailizable into a PLIST or modify the base class of my objects to make this work?
I realise I could archive to a binary file no problem with NSCoding however I need to be able to transfer the output between a MacOS application and an iOS app, so need to go via an intermediate, platform independent format.
Of course I could be missing the point entirely, if I have please do tell, and as always, any help is useful.
Best regards
Dave
P.S.
Here is my method to get the property names of an object:
- (NSMutableArray *)propertyNamesOfObject:(id)object {
NSMutableArray *propertyNames = nil;
unsigned int count, i;
objc_property_t *properties = class_copyPropertyList([object class], &count);
if (count > 0) {
propertyNames = [[[NSMutableArray alloc] init] autorelease];
for(i = 0; i < count; i++) {
objc_property_t property = properties[i];
const char *propName = property_getName(property);
if(propName) {
NSString *propertyName = [NSString stringWithCString:propName encoding:NSUTF8StringEncoding];
[propertyNames addObject:propertyName];
}
}
}
free(properties);
return propertyNames;
}
See if you can apply this function I recently wrote in a similar situation:
// Property list compatible types: NSString, NSData, NSArray, or NSDictionary */
- (BOOL)isPlistCompatibleDictionary:(NSDictionary *)dict {
NSSet *plistClasses = [NSSet setWithObjects:[NSString class], [NSData class],
[NSArray class], [NSDictionary class], [NSDate class],
[NSNumber class], nil];
BOOL compatible = YES;
NSArray *keys = [dict allKeys];
for (id key in keys) {
id obj = [dict objectForKey:key];
if (![plistClasses containsObject:[obj class]]) {
NSLog(#"not plist compatible: %#", [obj class]);
compatible = NO;
break;
}
}
return compatible;
}
I've got a question. Where is the reason for EXC_BAD_ACCESS in the following code ?
-(void)restClient:(DBRestClient *)client loadedMetadata:(DBMetadata *)metadata {
if(metadata.isDirectory) {
db_Path = metadata.path;
int i = 0;
NSString *fileName = [[NSString alloc] init];
for(DBMetadata *file in metadata.contents) {
fileName = [NSString stringWithFormat:#"%#", file.filename];
[db_MetaFileNames addObject:file.filename];
i++;
}
[self createMetaListArray];
[fileName release];
}
}
-(void)createMetaListArray {
fileNamesAtDirectory = db_MetaFileNames;
for (int i=0; i < [fileNamesAtDirectory count]; i++) {
NSString *filePathWithName = db_directory;
[filePathWithName stringByAppendingFormat:
[fileNamesAtDirectory objectAtIndex:i]];
[filePathsAtDirectory addObject:filePathWithName];
[filePathWithName release];
}
}
Can Anyone here help me ?
Here:
NSString *fileName = [[NSString alloc] init];
for(DBMetadata *file in metadata.contents) {
fileName = [NSString stringWithFormat:#"%#", file.filename];
The NSString on the first line gets overwritten with the new values on the third line. The original value leaks.
This means that:
}
[self createMetaListArray];
[fileName release];
The release on the last line releases not the fileName that you alloc/init above, but the assignment inside the loop. You don't alloc/copy/retain that, so you're not "in charge" of releasing it.
You have a similar misunderstanding in the second function.
[filePathWithName stringByAppendingFormat:[fileNamesAtDirectory objectAtIndex:i]];
This does not amend filePathWithName. It returns a new string.
I suggest you read up on Cocoa's memory management rules -- you're missing some fundamentals. Understanding those will make your life a lot easier.