EXC_BAD_ACCESS exception on accessing a property - ios

I want to create a class that I want to serialize as XML using the property names and property values of the class. For that I created the following function in my base class (from which I will derive all my other classes):
- (NSString*) serialize
{
unsigned int outCount, i;
NSMutableString* s = [NSMutableString string];
Class currentClass = [self class];
while(currentClass != nil) {
objc_property_t *properties = class_copyPropertyList([self class], &outCount);
if(outCount > 0) {
for(i = 0; i < outCount; i++) {
objc_property_t property = properties[i];
NSString *name = [NSString stringWithCString: property_getName(property) encoding: NSUTF8StringEncoding];
[s appendFormat: #"<%#>%#</%#>", name, (NSString*)property, name];
}
}
free(properties);
}
return (NSString*)s;
}
#end
I am assuming that all the properties are (nonatomic,strong) NSString* (for now - a more sophisticated code would come later). Now for some reason when I hit the line
[s appendFormat: #"<%#>%#</%#>", name, (NSString*)property], name];
I am getting a EXC_BAD_ACCESS exception.
What am I doing wrong?
Thanks in advance!

property is coming back as a c string, not an NSString. Just change your format to print out a c string instead of trying to convert it, something like this:
[s appendFormat: #"<%#>%s</%#>", name, property, name];

Found it. Just use
NSString *value = [self valueForKey:name];
to get the property value...

When you use %# with an object, the compiler basically tries to help you by calling that object's description method. Your objc_property_t variable doesn't support that method. I think it's actually a struct...You can't just cast that to NSString. You need to decide WHAT attribute you want to print from it, and then retrieve that value from it. Similar to what you did using property_getName for retrieving the "name" attribute.

Related

property list from object of type id

How to get property list from object of type id using class_copyPropertyList.
properties = class_copyPropertyList([WSAT_SignIN class], &count);
Instead of model class name, I have an id of type WSAT_SignIN. Beside the class_copyPropertyList method, is there a way to get property from an id?
Use this
objc_property_t *properties = class_copyPropertyList([WSAT_SignIN class], &count);
for (int i = 0; i < [YourArray count]; i++)
{
objc_property_t property = properties[i];
NSString *strName = [NSString stringWithUTF8String:property_getName(property)];
NSLog(#" prorerty name %#", strName);
}
i got the answer,
Class classname=id;
properties = class_copyPropertyList([classname class], &count);
without using classname i got the property list

How to iterate and set a property of a class in objective C iOS

I have a model class object which has the following properties
#property (nonatomic, strong) NSString *place;
#property (nonatomic, strong) NSString *date;
I am setting these properties from a controller class object.
I want to perform a null check of my model class object properties. So I want to write a for loop like following.
for (property in modelObject)
{
if (object == [NSNull null])//object is a property of modelobject
{
//the next two lines wont be true but need to check that
if([property isKindOfClass:[NSString Class]]) property = #"no place";
if([property isKindOfClass:[NSDate Class]]) property = #"No date;
}
}
My question is,
If the model class object property is set to null how can I check if the property is null and also the declaration type of that property?
Directly checking the two properties instead of looping through the properties won't be helpful because in the actual scenario there are lot of properties with different types for the model class.
Thanks in advance.
Add a method in Model Class
- (void)nullCheck {
unsigned int outCount, i;
objc_property_t *properties = class_copyPropertyList([self class], &outCount);
for (i = 0; i < outCount; i++) {
objc_property_t property = properties[i];
NSString *propertyName = [[NSString alloc] initWithCString:property_getName(property)];
id propertyValue = [self valueForKey:(NSString *)propertyName]; //check propertyValue here
//set property value here
[self setValue:#"some value" forKey:(NSString *)propertyName];
const char * type = property_getAttributes(property);
NSString *attr = [NSString stringWithCString:type encoding:NSUTF8StringEncoding];
NSString * typeString = [NSString stringWithUTF8String:type];
NSArray * attributes = [typeString componentsSeparatedByString:#","];
NSString * typeAttribute = [attributes objectAtIndex:0];
NSString * propertyType = [typeAttribute substringFromIndex:1];
}
free(properties);
}
First, you cant do this to check the properties on the modelObject view controller, if I understand correctly that that is what you want to do :
for (property in modelObject)
What this would do is iterate through an array called modelObject. So if you had first added your properties to that array, that would be fine. There is no simple way to iterate through every property that any class has - you just wouldnt want to do that anyway. You first need to either add them to an array for reference, or refer to them in some other collection type.
Second, you can quickly check if a property remains nil - ie it has never been used, like this :
if (object) {
}
This will return true if the object is not 'nil'. You dont have to use [NSNull null] for what you are trying to achieve. So you could instead do this :
if (!place) {
place = #"no place";
}
if (!date) {
date = #"no date";
}

Add variable value with property name in IOS

I have 5 property with name address1, address2, ..... , address5. Now i want to iterate through all the property and assign them some value from array as follows.
for (int i=0; i<5; i++) {
self.address *value of i+1* = endAddress[i]; // like address1 = endAddress[0] and address2 = endAddress[1]
}
Ia there any way to achieve this? If yes, then how?
I googled it but unable to find a solution of adding integer with property instead found solutions to add integer with NSString.
Thanks in advance.
Key Value coding could be used for this, however the value being stored must be an Objective-C object, and not a primitive type. So for a number this means using NSNumber:
#property NSNumber *address1;
#property NSNumber *address2;
#property NSNumber *address3;
#property NSNumber *address4;
#property NSNumber *address5;
...
for (int i=0; i<5; i++) {
NSString *keyName = [NSString stringWithFormat:#"address%d", i + 1];
[self setValue:#(endAddress[i])
forKey:keyName];
}

Parsing JSON data and handling an Array

I am using Mantle to parse some JSON data from Yelp.
For each business returned I get an NSArray of categories. This would be an example:
yelpCategories = (
(
"Wine Bars",
"wine_bars"
),
(
"Ice Cream & Frozen Yogurt",
icecream
)
);
yelpCategories is the name of the array that I save. Later on I am trying to parse the array into a string:
NSMutableString *yelpCats = [[NSMutableString alloc] init];
for (NSObject * obj in business.yelpCategories)
{
[yelpCats appendString:[NSString stringWithFormat:#"%#,",[obj description]]];
}
The issue is with the above. I am being returned a string just as "(" so I must be accessing the array incorrectly. How can I correctly access each object, ideally I would be looking for the end string o be #"Wine Bars, Ice Cream & Frozen Yogurt".
EDIT
The categories array: (
(
Pubs,
pubs
)
)
FINAL EDIT - Proposed Solution
for (NSArray *cats in business.yelpCategories)
{
NSString *category = [cats objectAtIndex:0];
if ([category length] > 0) {
category = [category substringToIndex:[category length] - 1];
}
if (cats == business.yelpCategories.lastObject) {
[yelpCats appendString:[NSString stringWithFormat:#"%#",category]];
} else {
[yelpCats appendString:[NSString stringWithFormat:#"%#, ",category]];
}
}
cell.yelpCategories.text = yelpCats;
Using the description of the object gives you what you see in the debugger, which includes extra carriage returns.
What you want to do is something like:
yelpCats = [yelpCategories componentsJoinedByString:#", "];
#jeffamaphone 's answer is the correct and best way of doing things however what your doing will almost work, I think your just confused on the contents of the array.
The yelpCategories array is an array of strings so you don't need to call stringWithFormat or call the description method. In fact [obj description] will return a string so you didn't even need stringWithFormat in your example and you would have gotten the same output. To make your original method work change to:
NSMutableString *yelpCats = [[NSMutableString alloc] init];
for (id obj in business.yelpCategories)
{
//obj is a string so we can just append it.
[yelpCats appendString:obj]];
}
Also noticed I changed NSObject *obj to just id obj, this is the idiomatic way and shorthand way of declaring NSObjects in objective-c. In this example however I would actually use (NSString *category in business.yelpCategories) instead for better readability. In this case you are declaring to everyone that you expect each object in the array to be a string and then if you wanted to use NSString methods on it inside the loop then you don't have to cast it.
for (NSArray *cats in business.yelpCategories)
{
NSString *category = [cats objectAtIndex:0];
if ([category length] > 0) {
category = [category substringToIndex:[category length] - 1];
}
if (cats == business.yelpCategories.lastObject) {
[yelpCats appendString:[NSString stringWithFormat:#"%#",category]];
} else {
[yelpCats appendString:[NSString stringWithFormat:#"%#, ",category]];
}
}
cell.yelpCategories.text = yelpCats;

How to release a string created inside a method?

I have declared a local string inside the method. I am releasing the string inside the same method. I found my code crashing if release that object. If I don't release the string, code runs successfully.
I have called that method in viewdidappear so that method is called while pushing and poping. Nothing gets printed in the console. Here is my code
-(void)appendString{
NSString *locStr = [[NSString alloc] initWithString:#""];
for (int i=0;i<[result count]; i++)
{
locStr=[locStr stringByAppendingFormat:#"%#",[result objectAtIndex:i]];
}
[str setString:locStr];
[locStr release];
}
I am calling the "appendString" method from "viewDidAppear"."str" is a NSMutable string declared in .h class.How should I release the "locStr".
What went wrong in my code? This isn't the normal way to release it?
Try this:
-(void)appendString{
//stringWithString returns an autorelease object
//so you don't need to worry about its memory management
NSString *locStr = [NSString stringWithString:#""];
for (int i=0;i<[result count]; i++)
{
//if your locstr is created by initWithString instead,
//the following line is going to cause memory leak
locStr=[locStr stringByAppendingFormat:#"%#",[result objectAtIndex:i]];
}
[str setString:locStr];
//[locStr release];
}
And make sure that the string property in your str instance is set to retain
You may want to use NSMutableString and not create multiple string objects that may or may not release at some time in the distant future.
-(void)appendString
{
NSMutableString *locStr = [[NSMutableString alloc] initWithString:#""];
for (int i=0;i<[result count]; i++)
{
[locStr appendFormat:#"%#",[result objectAtIndex:i]];
}
[str setString:locStr];
[locStr release];
}
see
In your code, calling [locStr release] is sent to the NSString instance returned from [locStr stringByAppendingFormat:#"%#",[result objectAtIndex:i]] That code actually returns a new String instance, so your variable locStr is now pointing to that new NSString instance and your reference to the original one is lost.
So it is NOT the instance of the NSString you created with [[NSString alloc] initWithString:#""]
The NSString returned from stringByAppendingFormat is autoreleased and your [locStr release] would over-release it. As the other answers indicate you could just use a NSMutableString to avoid lots of NSString instances to be created in your loop and actually releasing the original created instance.
No need to alloc/init locStr. This should do the trick:
-(void)appendString{
NSString *locStr = #"";
for (int i=0;i<[result count]; i++) {
locStr=[locStr stringByAppendingFormat:#"%#",[result objectAtIndex:i]];
}
[str setString:locStr];
}
Don't release, use autorelease. This is the way if you need to return something. When there is an autorelease pool (which is in most cases) it is released automatically. That's what methods like stringByAppendingFormat do also.
Or wait, when you di something like this, it is important, that you retain the Object in the setter Method. It is better then to use a property like
{
NSString* thestring;
}
#property (nonatomic, retain) NSString* thestring;
and a
#synthesize thestring;
in the .m file.
use this
locStr=[[locStr stringByAppendingFormat:#"%#",[result objectAtIndex:i]] retain];
then you can release this same as you are doing.
what happens here you get an autorelease object of string by stringByAppendingFormat method.
so you need to convert it into a retain copy.

Resources