concatenate Strings from a NSArray? - objective-c

#import <Foundation/Foundation.h>
#include <stdlib.h>
#include "GenerarPassword.h"
#implementation GenerarPassword
-(NSDictionary*) generarDiccionario
{
NSDictionary* m_Dict =[NSDictionary dictionaryWithObjectsAndKeys:
#"Dx", #"1",
#"Om", #"2",
#"Al", #"3",
#"Na", #"4",
#"Je", #"5",
#"Ko", #"6",
#"Ke", #"7",
#"Fi", #"8",
#"Re", #"9",
#"Me", #"10",
#"Mu", #"11",
#"Ra", #"12",
#"Lu", #"13",
#"Lo", #"14",
#"Ka", #"15"
,nil];
return m_Dict;
}
-(void) printPassword:(int) password
{
NSLog(#"%d",password);
}
/*Se genera la clave numérica*/
-(int) generarClave
{
srand(time(0));
int r = rand() %(9999-1000+1) +1000;
return r;
}
//Esta función Genera el valor Aleatorio
-(NSString*) GenerarValor:(NSString*) key
{
NSString *valor = [[self generarDiccionario] valueForKey: key];
return valor;
}
-(NSArray*) generarlistaletras:(int) numero
{
NSArray* lista = [[NSArray alloc] initWithObjects: [NSNumber numberWithInt:0], [NSNumber numberWithInt:0], [NSNumber numberWithInt:0],nil];
return lista;
}
-(void) imprimirArreglo:(NSArray*) arreglo
{
int i = 0;
NSString *str1 =#" ";
for (i=0;i<2;i++)
{
[str1 stringByAppendingString:[arreglo objectAtIndex:0]];
}
NSLog(#"%#",str1);
}
// lista = [0,0,0]
//lista[random.randrange(0,3)] = [GenerarValor(numero)]
//for i in range(len(lista)):
// if lista[i] == 0:
// lista[i] = [GenerarValor(random.randrange(11,20))]
// return lista
#end
int main(int argc, char const *argv[])
{
NSAutoreleasePool * pool = [[NSAutoreleasePool alloc] init];
GenerarPassword *Generar1 = [[GenerarPassword alloc]init];
int clave = [Generar1 generarClave];
[Generar1 printPassword:clave];
NSDictionary* dict = [Generar1 generarDiccionario];
NSLog(#"%#",[[Generar1 generarDiccionario] valueForKey:#"1"]);
[Generar1 imprimirArreglo:[Generar1 generarlistaletras:123]];
[pool drain];
return 0;
}
My idea is to print the array (the function imprimirArreglo), but when run the program I get this exception: Uncaught exception NSInvalidArgumentException, reason: Can not determine type information for -[NSIntNumber (null)]; what i wanted was to print the array with format 0 0 0.

OK, I edited your code into something more sensible.
#import <Foundation/Foundation.h>
#include "GenerarPassword.h"
#interface GenerarPassword ()
#property (nonatomic, strong) NSArray *generarArray;
#end
#implementation GenerarPassword
- (NSArray *)generarArray
{
if (!_generarArray) {
_generarArray = #[#"Dx", #"Om", #"Al", #"Na", #"Je", #"Ko", #"Ke", #"Fi", #"Re", #"Me", #"Mu", #"Ra", #"Lu", #"Lo", #"Ka"];
}
return _generarArray;
}
- (void)printPassword:(NSInteger)password
{
NSLog(#"%#", #(password));
}
/*Se genera la clave numérica*/
- (NSInteger)generarClave
{
return arc4random_uniform(9000) + 1000;
}
//Esta función Genera el valor Aleatorio
- (NSString *)generarValor:(NSInteger)key
{
return self.generarArray[key];
}
- (NSArray *)generarlistaletras:(NSInteger)numero
{
return #[#0, #0, #0];
}
- (void)imprimirArreglo:(NSArray *)arreglo
{
NSString *string1 = [NSString stringWithFormat:#"%#", [arreglo componentsJoinedByString:#" "]];
NSLog(#"%#", string1);
}
#end
int main(int argc, char const *argv[])
{
NSAutoreleasePool * pool = [[NSAutoreleasePool alloc] init];
GenerarPassword *generarPassword = [[GenerarPassword alloc] init];
NSInteger clave = [generarPassword generarClave];
[generarPassword printPassword:clave];
NSLog(#"%#", generarPassword.generarArray[0]);
[generarPassword imprimirArreglo:[generarPassword generarlistaletras:123]];
[pool drain];
return 0;
}
There are a few things I noticed.
First, if you're going to store value next to keys that are just counting numbers... then use an array not a dictionary.
Second, you're never actually calling the method generarValor so a lot of the code doesn't seem to be doing anything.
Third, you should really be using a property to store the array/dictionary in. You are repeatedly creating new dictionaries in your code.
The line NSDictionary* dict = [Generar1 generarDiccionario]; is actually just wasting processor cycles because you're not actually using dict at all.
Fourth, variable names and methods should start with a lowercase letter, class name start with an uppercase letter.
Fifth, there is a lot of useless code here. I think I've fixed the problem, in your imprimirArreglo method though.
Sixth, format your code nicely when you put it onto StackOverflow. Pretend you're doing and exam for college. No one here is paid so the chance they'll struggle through code that's all over the place is very small.
Also, take a look through the code. I've made some improvements and changes to it to make it Objective-C code and not C++ code.

Related

Building a USB Tree in Objective C

I am trying to build a tree to store USB device information. I thought that I would use NSMutableArray and NSMutableDictionary to contain this information. My problem is that I've never studied software engineering - I'm learning as I go - and I haven't the faintest idea about tree theory.
I'm basing my tree on the USB Location ID, which is eight nibbles long. As I understand it, each nibble represents a layer of the tree (if you see what I mean). I've written a little bit of test code to see if I can build my tree properly - and, sadly, it seems that I can't!
#import <Foundation/Foundation.h>
#define MAXCHILDREN 0xf
NSDictionary* AddItemToTree(NSDictionary* nodeEntry, unsigned int value, int depth)
{
// Convert the value into a set of nibbles
char *bytes = (char *)&value;
char byte = bytes[depth];
NSMutableDictionary* thisEntry = [[[NSMutableDictionary alloc] initWithDictionary:nodeEntry] autorelease];
if (byte == 0)
{
[thisEntry setObject:[NSString stringWithFormat:#"%08x",value] forKey:#"Value"];
[thisEntry setObject:[NSString stringWithFormat:#"%08x",byte] forKey:#"Byte"];
[thisEntry setObject:[NSNumber numberWithInt:depth] forKey:#"Depth"];
return thisEntry;
}
if(![[thisEntry allKeys]containsObject:#"ChildEntries"])
{
NSMutableArray* childArray = [[NSMutableArray alloc]init];
NSMutableDictionary* newNode = [[NSMutableDictionary alloc] init];
[childArray addObject:AddItemToTree(newNode,value,++depth)];
[thisEntry setObject:[NSNumber numberWithInt:depth] forKey:#"Depth"];
[thisEntry setObject:[NSString stringWithFormat:#"%08x",value] forKey:#"Value"];
[thisEntry setObject:[NSString stringWithFormat:#"%08x",byte] forKey:#"Byte"];
[thisEntry setObject:childArray forKey:#"ChildEntries"];
[newNode release];
[childArray release];
}
else
{
[[thisEntry objectForKey:#"ChildEntries"]addObject:AddItemToTree(thisEntry,value, ++depth)];
}
return thisEntry;
}
int main(int argc, char *argv[]) {
NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
NSMutableDictionary* treenode=[[NSMutableDictionary alloc]init];
char bytearray[4] = {0x0F, 0x0F, 0x02, 0x00};
unsigned int *value = (unsigned int*)bytearray;
char bytearray2[4] = {0x0F, 0x02, 0x00, 0x00};
unsigned int *value2 = (unsigned int*)bytearray2;
char bytearray3[4] = {0x0F, 0x02, 0x00, 0x00};
unsigned int *value3 = (unsigned int*)bytearray3;
[treenode setObject:[NSNumber numberWithInt:0] forKey:#"Depth"];
[treenode setObject:[NSString stringWithFormat:#"%08x",*value] forKey:#"Value"];
[treenode setObject:AddItemToTree(treenode,*value, 0) forKey:#"ChildEntries"];
// [[treenode objectForKey:#"ChildEntries"]addObject:AddItemToTree(treenode,*value2, 0)];
[treenode writeToFile:#"/Users/headbanger/Desktop/test.plist" atomically:YES];
[pool release];
}
Adding one USB location ID works perfectly. Adding a second (by uncommenting-out the line in main) causes SIGABRT. I'm sure that it's perfectly simple, and I've committed a typical newbie error. However, it's not obvious to me and any help that you can provide would be more than welcome.
My tree will need to look something like this:
F-
|--F-
| |--2
|
|--2
This tree should be true even if an attempt is made to add the third byte array.If you can answer the question without being USB specific then that would be most helpful, because I'd really like to understand about trees and what I've done wrong. That said, if there's a quick and easy way to get a tree built for me in Objective-C then I'd love to hear it.So please, experts, can someone tell me what I'm doing wrong? Thank you for your time.
One problem is that you set dictionary as the type of the ChildEntries:
[treenode setObject:AddItemToTree(treenode,*value, 0) forKey:#"ChildEntries"];
but elsewhere you attempt to use it as a NSMutableArray (mind the addObject: method):
[[thisEntry objectForKey:#"ChildEntries"]addObject:AddItemToTree(thisEntry,value, ++depth)];
To fix it, in your main you could do
[treenode setObject:[[NSMutableArray alloc] initWithObjects:AddItemToTree(treenode,*value, 0), nil]forKey:#"Children"];
but even when your recursion progresses towards the 0x00 byte if (byte==0), I think, from mentally inspecting it, that it's going to add duplicated children and produce a reeeally deep tree.
There is something wrong with your environment if you didn't get a message warning you of the wrong method addObject with the SIGABORT.
Btw, it's hard to read. Lines like these
[treenode setObject:[[NSMutableArray alloc] initWithObjects:AddItemToTree(treenode,*value, 0), nil]forKey:#"Children"];
are easier to scan and less prone to mistakes if you write:
NSString * const kChildren = #"Children";
// ...
NSMutableArray *children = [[NSMutableArray alloc] initWithObjects:AddItemToTree(treenode,*value, 0), nil];
[treenode setObject:children forKey:kChildren];
The style is not very objective-c-ish, you could use NSUInteger and NSData instead unsigned int and char arrays.
You should first write a generic tree, then use it for your purposes. This is my tree example. It's ugly but it's mine. As you see, it's common sense. You could set conditions like, two childs per node, and left child < root < right child, and then you would get a binary search tree which has better properties to find stuff. But that will take you a lot more code I guess.
#import <Foundation/Foundation.h>
typedef NS_ENUM(unsigned char, MyTreeVisitingOrder) {
MyTreeOrderDepthFirst,
MyTreeOrderValueFirst
};
#define Tree NSObject<MyTree>
#protocol MyTree
#property (nonatomic,strong) NSObject<NSCopying>* key;
#property (nonatomic,strong) NSObject *value;
#property (nonatomic,strong) NSMutableDictionary *children;
-(void) insertChild:(Tree*)node;
-(void) each:(void(^)(NSObject*))block order:(MyTreeVisitingOrder)order;
#end
#interface TreeImpl : NSObject <MyTree>
-(id) init __attribute__((unavailable("disabled")));
#end
#implementation TreeImpl
#synthesize key = _key;
#synthesize value = _value;
#synthesize children = _children;
-(id) initWithKey:(NSObject<NSCopying>*)key value:(NSObject*)value {
self = [super init];
if (self){
_key = key;
_value = value;
_children = [NSMutableDictionary new];
}
return self;
}
-(void) insertChild:(Tree*)node {
[_children setObject:node forKey:node.key];
}
-(void) each:(void(^)(NSObject*))block order:(MyTreeVisitingOrder)order {
switch (order) {
case MyTreeOrderDepthFirst:{
if (_children) {
for (id key in _children){
[[_children objectForKey:key] each:block order:order];
}
}
block(_value);
break;
}
case MyTreeOrderValueFirst:{
block(_value);
if (_children) {
for (id key in _children){
[[_children objectForKey:key] each:block order:order];
}
}
break;
}
}
}
#end
int main(int argc, char *argv[]) {
#autoreleasepool {
TreeImpl *a = [[TreeImpl alloc] initWithKey:#"A" value:#"A"];
TreeImpl *b = [[TreeImpl alloc] initWithKey:#"B" value:#"B"];
TreeImpl *c = [[TreeImpl alloc] initWithKey:#"C" value:#"C"];
[a insertChild:b];
[a insertChild:c];
[a each:^(NSObject* value) {
NSLog(#"> %#",value);
} order:MyTreeOrderValueFirst];
return EXIT_SUCCESS;
}
}

Is it possible to use a wildcard in KVC?

I'm trying to use wildcard in KVC like this.
Is it possible?
Or Is there other ways to use a wildcard to indicate a member variable?
#interface MyClass : NSObject
#property(nonatomic, retain) NSNumber *test1;
#property(nonatomic, retain) NSNumber *test2;
#end
#implementation MyClass{
NSNumber * test1;
NSNumber * test2;
}
#synthesize test1;
#synthesize test2;
#end
using wildcard
MyClass *testClass = [[[MyClass alloc] init] autorelease];
testClass.test1 = #50;
NSLog(#"test value : %#", [testClass valueForKey:#"*1"]);
For detail codes.
A real reason i wanted is to indicate a member variable of instance by value of integer or nsnumber type.
If possible, it is easier to set values and read values of any instance.
For example of property part copy.
MyClass *testClass = [[[MyClass alloc] init] autorelease];
testClass.year_1 = #2012;
testClass.quarter_2 = #3;
testClass.month_3 = #8;
testClass.day_4 = #20;
testClass.week_5 = #4;
// copy propertys to other instance.
// Normal way
MyClass *testClassCopy = [[[MyClass alloc] init] autorelease];
testClassCopy.year_1 = testClass.year_1;
testClassCopy.quarter_2 = testClass.quarter_2;
testClassCopy.month_3 = testClass.month_3;
testClassCopy.day_4 = testClass.day_4;
// copy propertys by using wildcard
for (int j = 0; j < 4; j++) {
NSString *indicate = [NSString stringWithFormat:#"*%#", [NSNumber numberWithInteger:j + 1]];
NSNumber *sourceProperty = [testClass valueForKey:indicate];
[testClassCopy setValue:sourceProperty forKey:indicate];
}
I'll raise your wildcards by adding Regex, and by using categories:
To read about how regex works with this, please read the NSRegularExpression Class Reference.
Features:
Uses regex, for matching of a wide variety of keys
Uses a category that works on any instance
Caches key lists per class
Full KVC support (not just properties, but accessor methods & iVars too!)
Integrates flawlessly with current KVC methods (only uses the regex if the key wasn't found, improving performance)
Subclassing doesn't mess it up, like #JamesWebster's solution
Doesn't needlessly pollute the list of keys with NSObject's methods
Returns a NSDictionary of matched keys & values
Cons:
Uses regex, which is slower and more complex to understand
Slow initial lookup for a class (must iterate through all methods & iVars)
Automatically overwrites the -valueForUndefinedKey: method, so it's possible that this could break some existing code (move it to it's own method to fix).
Currently doesn't support setting of values (by design, that's a whole other bag of cats).
Can have duplicate keyPaths in the result (not the biggest of issues, but stems from the fact that KVC matching is complex, and I have to implement all of the rules)
Uses NSRegularExpression, which is only available in iOS 4 and later (not the largest of issues).
Version History:
1.0: Initial Release
So, here is the code:
NSObject+KVCRegex.h:
//
// NSObject+KVCRegex.h
// TestProj
//
// Created by Richard Ross on 8/20/12.
// Copyright (c) 2012 Ultimate Computer Services, Inc. All rights reserved.
//
#import <Foundation/Foundation.h>
#interface NSObject (KVCRegex)
// custom implemenation
-(id) valueForUndefinedKey:(NSString *)key;
#end
NSObject+KVCRegex.m:
//
// NSObject+KVCRegex.m
// TestProj
//
// Created by Richard Ross on 8/20/12.
// Copyright (c) 2012 Ultimate Computer Services, Inc. All rights reserved.
//
#import "NSObject+KVCRegex.h"
#import <objc/runtime.h>
#implementation NSObject (KVCRegex)
static NSSet *keyPathsForClass(Class cls)
{
NSMutableSet *keys = [NSMutableSet set];
do
{
if (cls == [NSObject class])
{
// nothing good can come from trying to use KVC on NSObject methods
break;
}
unsigned count = 0;
Method *methods = class_copyMethodList(cls, &count);
for (int i = 0; i < count; i++) {
// make sure that the method returns a value
const char *methodName = sel_getName(method_getName(methods[i]));
char returnType[64];
method_getReturnType(methods[i], returnType, 64);
if (strcmp(returnType, "v") == 0)
continue;
// make sure that the method takes no args (except for self & _cmd)
if (method_getNumberOfArguments(methods[i]) == 2)
{
// add a duplicate entry for ones matching 'is'
if (strstr(methodName, "is") == methodName)
{
char *newStr = strdup(methodName + 2);
newStr[0] = tolower(newStr[0]);
[keys addObject:[NSString stringWithUTF8String:newStr]];
free(newStr);
}
[keys addObject:[NSString stringWithUTF8String:methodName]];
}
}
free(methods);
// now copy iVars
count = 0;
Ivar *ivars = class_copyIvarList(cls, &count);
for (int i = 0; i < count; i++)
{
const char *ivarName = ivar_getName(ivars[i]);
if (strstr(ivarName, "_") == ivarName)
[keys addObject:[NSString stringWithUTF8String:ivarName + 1]]; // iVar name starting with _<key>
[keys addObject:[NSString stringWithUTF8String:ivarName]];
}
free(ivars);
} while ((cls = [cls superclass]));
return [NSSet setWithSet:keys];
}
// returns a dictionary based on 'key' as a regex
-(id) valueForUndefinedKey:(NSString *)key
{
// lookup for later use
static NSMutableDictionary *keyClassPairs;
if (!keyClassPairs)
keyClassPairs = [NSMutableDictionary dictionary];
if (!keyClassPairs[[self class]])
{
keyClassPairs[(id<NSCopying>)[self class]] = keyPathsForClass([self class]);
}
NSSet *keyPaths = keyClassPairs[[self class]];
// assume 'key' is a regex
NSRegularExpression *regex = [NSRegularExpression regularExpressionWithPattern:key options:0 error:nil];
NSMutableArray *matches = [NSMutableArray array];
for (NSString *keyPath in keyPaths)
{
NSRange matchRange = [regex rangeOfFirstMatchInString:keyPath options:0 range:(NSRange) { 0, keyPath.length }];
if (matchRange.length == keyPath.length)
{
// we have a match
[matches addObject:keyPath];
}
}
if (matches.count)
return [self dictionaryWithValuesForKeys:matches];
else
[NSException raise:NSUndefinedKeyException format:#"Could not find a key that matches the regex in %#", key];
return nil;
}
#end
Example:
#interface MyObject : NSObject
{
#public
int normalIvar;
id _underscoreIvar;
}
#property id someProp;
#property BOOL isProperty;
#property int nativeProp;
-(void) notAKey;
-(id) aKey;
#end
#implementation MyObject
#synthesize someProp, isProperty, nativeProp;
-(void) notAKey
{
NSLog(#"Not a key!");
}
-(id) aKey
{
return #"Value";
}
#end
int main()
{
#autoreleasepool {
MyObject *obj = [MyObject new];
obj.someProp = #"a property";
obj.nativeProp = 15;
obj.isProperty = YES;
obj->normalIvar = 172;
obj->_underscoreIvar = #"Ivar";
NSString *regex = #"[a|s].*"; // match a key starting with 'a' or 's', then matching anything else after
NSLog(#"%#", [obj valueForKey:regex]); // prints "{ aKey = 'Value', someProp = 'a property' }"
regex = #"_.*"; // match a key starting with '_', and then match anything else after
NSLog(#"%#", [obj valueForKey:regex]); // prints "{ _underscoreIvar = 'Ivar' }"
regex = #".*"; // match any key declared for this object
NSLog(#"%#", [obj valueForKey:regex]); // prints "{ "_underscoreIvar" = Ivar; aKey = Value; isProperty = 1; nativeProp = 15; normalIvar = 172; property = 1; someProp = "a property"; underscoreIvar = Ivar; }"
regex = #"(?i)[A-J].*"; // match (case insensitive) a key starting with A - J
NSLog(#"%#", [obj valueForKey:regex]); // prints "{ aKey = value; isProperty = 1; }"
}
}
Though I couldn't find a way to support wildcards using the syntax you were attempting. I found this roundabout method using the Objective-C runtime!
First we get all of the properties of the class you'd like to use
#import <objc/runtime.h>
unsigned int outCount;
objc_property_t *properties = class_copyPropertyList([MyClass class], &outCount);
NSMutableArray *array = [NSMutableArray arrayWithCapacity:outCount];
for (int i = 0; i < outCount; i++)
{
objc_property_t property = properties[i];
const char *propName = property_getName(property);
if(propName)
{
NSString *propertyName = [NSString stringWithUTF8String:propName];
[array addObject:propertyName];
}
}
free(properties);
Then filter out the ones you actually want
NSPredicate *predicate = [NSPredicate predicateWithFormat:#"SELF ENDSWITH '1'"];
[array filterUsingPredicate:predicate];
Then actually use them
for (NSString *key in array)
NSLog(#"%#", [testClass valueForKey:key]);

List of class properties in Objective-C

Is there a way to get an array of class properties of certain kind? For example if i have interface like this
#interface MyClass : NSObject
#property (strong,nonatomic) UILabel *firstLabel;
#property (strong,nonatomic) UILabel *secondLabel;
#end
can i get the reference to those labels in implementation without knowing their name?
#implementation MyClass
-(NSArray*)getListOfAllLabels
{
?????
}
#end
I know i can do it easily with [NSArray arrayWithObjects:firstLabel,secondLabel,nil], but i would like to do it with some kind of class enumeration like for (UILabel* oneLabel in ???[self objects]???)
So more precisely, you want dynamic, runtime observaion of the properties, if I got it correctly. Do something like this (implement this method on self, the class you want to introspect):
#import <objc/runtime.h>
- (NSArray *)allPropertyNames
{
unsigned count;
objc_property_t *properties = class_copyPropertyList([self class], &count);
NSMutableArray *rv = [NSMutableArray array];
unsigned i;
for (i = 0; i < count; i++)
{
objc_property_t property = properties[i];
NSString *name = [NSString stringWithUTF8String:property_getName(property)];
[rv addObject:name];
}
free(properties);
return rv;
}
- (void *)pointerOfIvarForPropertyNamed:(NSString *)name
{
objc_property_t property = class_getProperty([self class], [name UTF8String]);
const char *attr = property_getAttributes(property);
const char *ivarName = strchr(attr, 'V') + 1;
Ivar ivar = object_getInstanceVariable(self, ivarName, NULL);
return (char *)self + ivar_getOffset(ivar);
}
Use it like this:
SomeType myProperty;
NSArray *properties = [self allPropertyNames];
NSString *firstPropertyName = [properties objectAtIndex:0];
void *propertyIvarAddress = [self getPointerOfIvarForPropertyNamed:firstPropertyName];
myProperty = *(SomeType *)propertyIvarAddress;
// Simpler alternative using KVC:
myProperty = [self valueForKey:firstPropertyName];
Hope this helps.
use attributeKeys method of NSObject.
for (NSString *key in [self attributeKeys]) {
id attribute = [self valueForKey:key];
if([attribute isKindOfClass:[UILabel class]])
{
//put attribute to your array
}
}
Check out this link. It is an objective c wrapper over objective C runtime.
You can use code like below
uint count;
objc_property_t* properties = class_copyPropertyList(self.class, &count);
NSMutableArray* propertyArray = [NSMutableArray arrayWithCapacity:count];
for (int i = 0; i < count ; i++)
{
const char* propertyName = property_getName(properties[i]);
[propertyArray addObject:[NSString stringWithCString:propertyName encoding:NSUTF8StringEncoding]];
}
free(properties);
You must include the runtime headers
#import<objc/runtime.h>
uint propertiesCount;
objc_property_t *classPropertiesArray = class_copyPropertyList([self class], &propertiesCount);
free(classPropertiesArray);
The answer by #user529758 won't work with ARC and it won't list the properties of any ancestor classes.
To fix this, you need to traverse up the class hierarchy, and use the ARC-compatible [NSObject valueForKey:] to get the property values.
Person.h:
#import <Foundation/Foundation.h>
extern NSMutableArray *propertyNamesOfClass(Class klass);
#interface Person : NSObject
#property (nonatomic) NSString *name;
#end
Person.m:
#import "Person.h"
#import <objc/runtime.h>
NSMutableArray *propertyNamesOfClass(Class klass) {
unsigned int count;
objc_property_t *properties = class_copyPropertyList(klass, &count);
NSMutableArray *rv = [NSMutableArray array];
for (unsigned int i = 0; i < count; i++)
{
objc_property_t property = properties[i];
NSString *name = [NSString stringWithUTF8String:property_getName(property)];
[rv addObject:name];
}
free(properties);
return rv;
}
#implementation Person
- (NSMutableArray *)allPropertyNames {
NSMutableArray *classes = [NSMutableArray array];
Class currentClass = [self class];
while (currentClass != nil && currentClass != [NSObject class]) {
[classes addObject:currentClass];
currentClass = class_getSuperclass(currentClass);
}
NSMutableArray *names = [NSMutableArray array];
[classes enumerateObjectsWithOptions:NSEnumerationReverse usingBlock:^(Class currentClass, NSUInteger idx, BOOL *stop) {
[names addObjectsFromArray:propertyNamesOfClass(currentClass)];
}];
return names;
}
- (NSString*)description {
NSMutableArray *keys = [self allPropertyNames];
NSMutableDictionary *properties = [NSMutableDictionary dictionaryWithCapacity:keys.count];
[keys enumerateObjectsUsingBlock:^(NSString *key, NSUInteger idx, BOOL *stop) {
properties[key] = [self valueForKey:key];
}];
NSString *className = NSStringFromClass([self class]);
return [NSString stringWithFormat:#"%# : %#", className, properties];
}
Student.h:
#import "Person.h"
#interface Student : Person
#property (nonatomic) NSString *studentID;
#end
Student.m:
#import "Student.h"
#implementation Student
#end
main.m:
#import <Foundation/Foundation.h>
#import "Student.h"
int main(int argc, const char * argv[]) {
#autoreleasepool {
// insert code here...
Student *student = [[Student alloc] init];
student.name = #"John Doe";
student.studentID = #"123456789";
NSLog(#"student - %#", student);
}
return 0;
}
The solution of serhats is great unfortunately it doesn't work for iOS (as you mentioned) (and this question is tagged for iOS). A workaround would be to get a NSDictionary representation of the object and then access it normally as key-value pairs. I would recommend a category for NSObject:
Header-File:
#interface NSObject (NSDictionaryRepresentation)
/**
Returns an NSDictionary containing the properties of an object that are not nil.
*/
- (NSDictionary *)dictionaryRepresentation;
#end
Implementation-File:
#import "NSObject+NSDictionaryRepresentation.h"
#import <objc/runtime.h>
#implementation NSObject (NSDictionaryRepresentation)
- (NSDictionary *)dictionaryRepresentation {
unsigned int count = 0;
// Get a list of all properties in the class.
objc_property_t *properties = class_copyPropertyList([self class], &count);
NSMutableDictionary *dictionary = [[NSMutableDictionary alloc] initWithCapacity:count];
for (int i = 0; i < count; i++) {
NSString *key = [NSString stringWithUTF8String:property_getName(properties[i])];
NSString *value = [self valueForKey:key];
// Only add to the NSDictionary if it's not nil.
if (value)
[dictionary setObject:value forKey:key];
}
free(properties);
return dictionary;
}
#end
Borrowed from this article: http://hesh.am/2013/01/transform-properties-of-an-nsobject-into-an-nsdictionary/
This way you could do something similar as serhats mentioned:
for (NSString *key in objectDic.allKeys) {
if([objectDic[key] isKindOfClass:[UILabel class]])
{
//put attribute to your array
}
}

Obj-C easy method to convert from NSObject with properties to NSDictionary?

I ran across something that I eventually figured out, but think that there's probably a much more efficient way to accomplish it.
I had an object (an NSObject which adopted the MKAnnotation protocol) that had a number of properties (title, subtitle,latitude,longitude, info, etc.). I needed to be able to pass this object to another object, which wanted to extract info from it using objectForKey methods, as an NSDictionary (because that's what it was getting from another view controller).
What I ended up doing was create a new NSMutableDictionary and use setObject: forKey on it to transfer each piece of vital info, and then I just passed on the newly created dictionary.
Was there an easier way to do this?
Here's the relevant code:
// sender contains a custom map annotation that has extra properties...
- (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender
{
if ([[segue identifier] isEqualToString:#"showDetailFromMap"])
{
DetailViewController *dest =[segue destinationViewController];
//make a dictionary from annotaion to pass info
NSMutableDictionary *myValues =[[NSMutableDictionary alloc] init];
//fill with the relevant info
[myValues setObject:[sender title] forKey:#"title"] ;
[myValues setObject:[sender subtitle] forKey:#"subtitle"];
[myValues setObject:[sender info] forKey:#"info"];
[myValues setObject:[sender pic] forKey:#"pic"];
[myValues setObject:[sender latitude] forKey:#"latitude"];
[myValues setObject:[sender longitude] forKey:#"longitude"];
//pass values
dest.curLoc = myValues;
}
}
Thanks in advance for your collective wisdom.
Here's what I came up with, thanks to the folks, below...
- (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender
{
if ([[segue identifier] isEqualToString:#"showDetailFromMap"])
{
DetailViewController *dest =[segue destinationViewController];
NSArray *myKeys = [NSArray arrayWithObjects:
#"title",#"subtitle",#"info",#"pic",#"latitude",#"longitude", nil];
//make a dictionary from annotaion to pass info
NSDictionary *myValues =[sender dictionaryWithValuesForKeys:myKeys];
//pass values
dest.curLoc = myValues;
}
}
And a even simpler fix, as seen below...
Using valueForKey instead of object for key to retrieve the information.
Sure thing! Use the objc-runtime and KVC!
#import <objc/runtime.h>
#interface NSDictionary(dictionaryWithObject)
+(NSDictionary *) dictionaryWithPropertiesOfObject:(id) obj;
#end
#implementation NSDictionary(dictionaryWithObject)
+(NSDictionary *) dictionaryWithPropertiesOfObject:(id)obj
{
NSMutableDictionary *dict = [NSMutableDictionary dictionary];
unsigned count;
objc_property_t *properties = class_copyPropertyList([obj class], &count);
for (int i = 0; i < count; i++) {
NSString *key = [NSString stringWithUTF8String:property_getName(properties[i])];
[dict setObject:[obj valueForKey:key] forKey:key];
}
free(properties);
return [NSDictionary dictionaryWithDictionary:dict];
}
#end
And you would use like this:
MyObj *obj = [MyObj new];
NSDictionary *dict = [NSDictionary dictionaryWithPropertiesOfObject:obj];
NSLog(#"%#", dict);
This is an old post and Richard J. Ross III's answer is really helpful, but in case of custom objects (an custom class has another custom object in it). However, sometimes properties are other objects and so forth, making the serialization a bit complicated.
Details * details = [[Details alloc] init];
details.tomato = #"Tomato 1";
details.potato = #"Potato 1";
details.mangoCount = [NSNumber numberWithInt:12];
Person * person = [[Person alloc]init];
person.name = #"HS";
person.age = #"126 Years";
person.gender = #"?";
person.details = details;
For converting these type of objects (multiple custom objects) into dictionary, I had to modify Richard J. Ross III's Answer a little bit.
+(NSDictionary *) dictionaryWithPropertiesOfObject:(id)obj
{
NSMutableDictionary *dict = [NSMutableDictionary dictionary];
unsigned count;
objc_property_t *properties = class_copyPropertyList([obj class], &count);
for (int i = 0; i < count; i++) {
NSString *key = [NSString stringWithUTF8String:property_getName(properties[i])];
Class classObject = NSClassFromString([key capitalizedString]);
if (classObject) {
id subObj = [self dictionaryWithPropertiesOfObject:[obj valueForKey:key]];
[dict setObject:subObj forKey:key];
}
else
{
id value = [obj valueForKey:key];
if(value) [dict setObject:value forKey:key];
}
}
free(properties);
return [NSDictionary dictionaryWithDictionary:dict];
}
I hope it will help someone. Full credit goes to Richard J. Ross III.
If the properties had the same names as the keys used to access the dictionary then you could have just used KVC and had valueForKey: instead of objectForKey.
For example given this dictionary
NSDictionary *annotation = [[NSDictionary alloc] initWithObjectsAndKeys:
#"A title", #"title", nil];
and this Object
#interface MyAnnotation : NSObject
#property (nonatomic, copy) NSString *title;
#end
it wouldn't matter if I had an instance of the dictionary or MyAnnotation I could call
[annotation valueForKey:#"title"];
Obviously that works the other way as well e.g.
[annotation setValue:#"A title" forKey:#"title"];
To complete the method of Richard J. Ross, this one works with NSArray of custom object.
+(NSDictionary *) dictionaryWithPropertiesOfObject:(id)obj
{
NSMutableDictionary *dict = [NSMutableDictionary dictionary];
unsigned count;
objc_property_t *properties = class_copyPropertyList([obj class], &count);
for (int i = 0; i < count; i++) {
NSString *key = [NSString stringWithUTF8String:property_getName(properties[i])];
Class classObject = NSClassFromString([key capitalizedString]);
id object = [obj valueForKey:key];
if (classObject) {
id subObj = [self dictionaryWithPropertiesOfObject:object];
[dict setObject:subObj forKey:key];
}
else if([object isKindOfClass:[NSArray class]])
{
NSMutableArray *subObj = [NSMutableArray array];
for (id o in object) {
[subObj addObject:[self dictionaryWithPropertiesOfObject:o] ];
}
[dict setObject:subObj forKey:key];
}
else
{
if(object) [dict setObject:object forKey:key];
}
}
free(properties);
return [NSDictionary dictionaryWithDictionary:dict];
}
There are so many solutions and nothing worked for me as I had a complex nested object structure. This solution takes things from Richard and Damien but improvises as Damien's solution is tied to naming keys as class names.
Here is the header
#interface NSDictionary (PropertiesOfObject)
+(NSDictionary *) dictionaryWithPropertiesOfObject:(id)obj;
#end
Here is the .m file
#implementation NSDictionary (PropertiesOfObject)
static NSDateFormatter *reverseFormatter;
+ (NSDateFormatter *)getReverseDateFormatter {
if (!reverseFormatter) {
NSLocale *locale = [[NSLocale alloc] initWithLocaleIdentifier:#"en_US_POSIX"];
reverseFormatter = [[NSDateFormatter alloc] init];
[reverseFormatter setDateFormat:#"yyyy-MM-dd'T'HH:mm:ss.SSS'Z'"];
[reverseFormatter setLocale:locale];
}
return reverseFormatter;
}
+ (NSDictionary *)dictionaryWithPropertiesOfObject:(id)obj {
NSMutableDictionary *dict = [NSMutableDictionary dictionary];
unsigned count;
objc_property_t *properties = class_copyPropertyList([obj class], &count);
for (int i = 0; i < count; i++) {
NSString *key = [NSString stringWithUTF8String:property_getName(properties[i])];
id object = [obj valueForKey:key];
if (object) {
if ([object isKindOfClass:[NSArray class]]) {
NSMutableArray *subObj = [NSMutableArray array];
for (id o in object) {
[subObj addObject:[self dictionaryWithPropertiesOfObject:o]];
}
dict[key] = subObj;
}
else if ([object isKindOfClass:[NSString class]]) {
dict[key] = object;
} else if ([object isKindOfClass:[NSDate class]]) {
dict[key] = [[NSDictionary getReverseDateFormatter] stringFromDate:(NSDate *) object];
} else if ([object isKindOfClass:[NSNumber class]]) {
dict[key] = object;
} else if ([[object class] isSubclassOfClass:[NSObject class]]) {
dict[key] = [self dictionaryWithPropertiesOfObject:object];
}
}
}
return dict;
}
#end
You also can use the NSObject+APObjectMapping category which is available on GitHub: https://github.com/aperechnev/APObjectMapping
It's a quit easy. Just describe the mapping rules in your class:
#import <Foundation/Foundation.h>
#import "NSObject+APObjectMapping.h"
#interface MyCustomClass : NSObject
#property (nonatomic, strong) NSNumber * someNumber;
#property (nonatomic, strong) NSString * someString;
#end
#implementation MyCustomClass
+ (NSMutableDictionary *)objectMapping {
NSMutableDictionary * mapping = [super objectMapping];
if (mapping) {
NSDictionary * objectMapping = #{ #"someNumber": #"some_number",
#"someString": #"some_string" };
}
return mapping
}
#end
And then you can easily map your object to dictionary:
MyCustomClass * myObj = [[MyCustomClass alloc] init];
myObj.someNumber = #1;
myObj.someString = #"some string";
NSDictionary * myDict = [myObj mapToDictionary];
Also you can parse your object from dictionary:
NSDictionary * myDict = #{ #"some_number": #123,
#"some_string": #"some string" };
MyCustomClass * myObj = [[MyCustomClass alloc] initWithDictionary:myDict];

parse error on objective c

#import <Foundation/NSArray.h>
#import <Foundation/NSString.h>
#import <Foundation/NSAutoreleasePool.h>
#import <Foundation/NSEnumerator.h>
#import <stdio.h>
void print( NSArray *array ) {
NSEnumerator *enumerator = [array objectEnumerator];
id obj;
while ( obj = [enumerator nextObject] ) {
printf( "%s\n", [[obj description] cString] );
}
}
int main( int argc, const char *argv[] ) {
NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
NSArray *arr = [[NSArray alloc] initWithObjects:
#"Me", #"Myself", #"I", nil];
NSMutableArray *mutable = [[NSMutableArray alloc] init];
// enumerate over items
printf( "----static array\n" );
print( arr );
// add stuff
[mutable addObject: #"One"];
[mutable addObject: #"Two"];
[mutable addObjectsFromArray: arr];
[mutable addObject: #"Three"];
// print em
printf( "----mutable array\n" );
print( mutable );
// sort then print
printf( "----sorted mutable array\n" );
[mutable sortUsingSelector: #selector( caseInsensitiveCompare: )];
print( mutable );
// free memory
[arr release];
[mutable release];
[pool release];
return 0;
}
this program compiled with oscv0.1.4 in windows.
it gives an error as shown below
Error: Parse error on line 6:
...import <stdio.h>
void print( NSArray
---------------------^
Expecting 'INTERFACE', 'IMPLEMENTATION', 'PROTOCOL', 'IMPORT', 'CLASS', 'DEFINE', 'EOF'
now i got one more error for the program shown below(it is another program)
#import "Forwarder.h"
#import "Recipient.h"
int main(void)
{
Forwarder *forwarder = [Forwarder new];
Recipient *recipient = [Recipient new];
[forwarder setRecipient:recipient]; //Set the recipient.
[forwarder hello];
[recipient release];
[forwarder release];
return 0;
}
error is
Error: Parse error on line 3:
...Recipient : Object
- (id)hello;
#end#i
----------------------^
Expecting '<', '{'
the format of your program must like the code shown below
here main must contain Main class
compare this with the link
http://code.google.com/p/oscompiler/downloads/list
#interface Main : NSObject { }
#end
#implementation Main
+(void)main {
NSLog(#"Hello world!");
}
#end