[NSObject description] - objective-c

Hello can you give me an example of the usage of this method
+(NSString *)description
Do I use description with an instance of a NSObject (any kind of object) or NSString?
or do I use without an instance, directly using NSObject (any kind of object) or NSString?

The description of the instance gives you information about the specific instance you have created.
- (NSString *)description;
NSString *string = [NSString alloc] initwithString:#"aString"]];
[string description];
Gives you information about this instance (location in memory etc)
On the other side:
+ (NSString *)description;
[NSString description];
Gives you information about the class NSString.
The same rules apply to all NSObject subclasses and other classes that conform to NSObject protocol such NSArray, NSDictionary *NSProxy* etc

Let's say we have:
#interface randomObject : NSObject
{
NSString *yourString;
}
and somewhere:
yourString = [[NSString alloc] initWithString:#"random text"];
then we can override randomObject like this...
- (NSString *)description
{
return [NSString stringWithFormat:#"%#", yourString];
}
after we done this we can call a NSLog with our NSObject:
-(void)viewDidLoad {
randomObject *ourObj;
ourObj = [[randomObject alloc] init];
NSLog(#"%#", ourObj); // this will output "random text"
}

You seem to mainly be confused about the difference between class and instance methods.
NSObject declares the class method +[NSObject description], which, as the docs tell you "Returns a string that represents the contents of the receiving class.". If you send the message description to a class object, like so:
[NSArray description];
[NSNumber description];
[[someObject class] description];
this method will be called and you'll get the string the class uses to describe itself.
On the other hand, the NSObject protocol declares a required instance method -[id<NSObject> description], which will return "a string that describes the contents of the receiver". When you send this to an instance, you get a string representing it:
NSNumber * n = [NSNumber numberWithInt:10];
[n description];
NSArray * arr = [NSArray arrayWithObjects:#"Lemon", #"curry", #"?", nil];
[arr description];
NSDicitonary * d = [NSDictionary dictionaryWithObject:arr forKey:n];
[d description];
All subclasses of NSObject inherit both of these methods, and they can be overridden just like any other. Notice, for example, that NSDictionary and NSArray format themselves and send description to the objects they contain.
It should also be noted that, when using NSLog(), the %# format specifier causes description to be sent to its argument (whether it's a class object or an instance).

The call most normally used it actually
- (NSString *)description;
It is used on normally instances, not classes. It can be overridden in custom classes to provide detailed information about an object. If you attempt to access a class as as string, the description method will automatically be called.
NSLog(#"array: %#", array); //Identical
NSLog(#"array: %#", [array description]); //Identical
You can use it on classes just as you stated
[NSArray description];

+(NSString *)description
Is used mainly for debug and is used by instances. It allows to print a description of the object.

Related

SBJsonParser can't parse NSArray

I am trying to parse NSArray to JSON but I get the following error:
* Terminating app due to uncaught exception 'NSInvalidArgumentException', reason: '-[__NSArrayM
JSONRepresentation]: unrecognized selector sent to instance 0xa93e460'
* First throw call stack: (0x21f1012 0x1feae7e 0x227c4bd 0x21e0bbc 0x21e094e 0x3445a 0x33ecc 0x26a453f 0x26b6014 0x26a72e8 0x26a7450
0x95e22e12 0x95e0acca) libc++abi.dylib: terminate called throwing an
exception
I have included all classes from SBJson_3.1.1/Classes directory.
This is code:
NSMutableArray* arr = ...get array
NSString* jsonArr = [arr JSONRepresentation]; // here I get error
When I do this in array of simple strings it works:
NSData *jsonData = [NSJSONSerialization arr
options:NSJSONWritingPrettyPrinted error:nil];
NSString *jsonString = [[NSString alloc] initWithData:jsonData encoding:NSUTF8StringEncoding];
But my array contain list of objects (Person) maybe there is a problem.
I use Item instead of person just as example
Item.h
#interface Item : NSObject
{
BOOL IsOpen;
NSString* Description;
}
#property int ItemId;
#property int SequenceId;
#property BOOL IsOpen;
#property NSString* Description;
- (id) proxyForJson;
#end
Item.m
#implementation Item
#synthesize ItemId;
#synthesize SequenceId;
#synthesize Description;
#synthesize IsOpen;
- (id) proxyForJson {
return [NSDictionary dictionaryWithObjectsAndKeys:
[NSString stringWithFormat:#"%i", ItemId], #"ItemId",
SequenceId, #"SequenceId",
Description, #"Description",
IsScanned, #"IsOpen",
nil ];
}
#end
UPDATE
Student example
I tried to make a separate project. I copied to new project all from classes directory of sbjson framework. This is code:
#import "SBJson.h"
#interface Student : NSObject
{
NSString *name;
NSInteger sid;
NSString *email;
}
#property NSString *name;
#property NSInteger sid;
#property NSString *email;
- (id) proxyForJson;
#end
#implementation Student
#synthesize name;
#synthesize sid;
#synthesize email;
- (id) proxyForJson{
return [NSDictionary dictionaryWithObjectsAndKeys:
name, #"student_name",
[NSNumber numberWithInt:sid], #"student_id",
email, #"email",
nil ];
}
#end
NSMutableArray* studentArray = [[NSMutableArray alloc]init];
Student* s1 = [[Student alloc]init];
s1.name = #"student 1";
s1.sid = 45;
s1.email = #"test#test.com";
Student* s2 = [[Student alloc]init];
s2.name = #"student 2";
s2.sid = 46;
s2.email = #"plavi#test.com";
[studentArray addObject:s1];
[studentArray addObject:s2];
NSString *jsonString = [studentArray JSONRepresentation];
NSLog(#"%#", jsonString);
And again I get error:
Terminating app due to uncaught exception
'NSInvalidArgumentException', reason: '-[__NSArrayM
JSONRepresentation]: unrecognized selector sent to instance 0x741b100'
SBJson doesn't support serialising user-defined classes without assistance. If you implement a -proxyForJson method in your Person class (example here) it should work, however.
If you're using a recent Xcode the below should work. Header:
#interface Item : NSObject
#property int ItemId;
#property int SequenceId;
#property BOOL IsOpen;
#property(copy) NSString* Description;
- (id) proxyForJson;
#end
Implementation:
#implementation Item
- (id) proxyForJson {
return #{ #"ItemId": #(self.ItemId),
#"SequenceId": #(self.SequenceId),
#"Description": self.Description,
#"IsOpen": #(self.IsOpen)
};
}
#end
This should let SBJson serialise the Item objects to NSDictionaries. However, SBJson does not support parsing JSON into custom objects. So you will always get this back in the dictionary form. I don't know of any Objective-C JSON parser that provides bindings to custom types.
I would suggest reading the top two comments of this thread. If those don't help, it is still very likely that you are not installing the library correctly. Try removing the SBJSON files from your project and then readding them, making sure that they are added to your target. Also, make sure you are importing the SBJSON header into your class.
I would suggest that you try using JSONRepresentation on an array of NSString objects. If the framework is correctly installed, this should definitely work. This way you can narrow down whether it is an installation issue or whether it is an issue with your custom class.
Check out the following excerpt from Working with JSON in iOS 5 Tutorial
This is mainly for generating JSON.
//build an info object and convert to json
NSDictionary* info = [NSDictionary dictionaryWithObjectsAndKeys:
[loan objectForKey:#"name"],
#"who",
[(NSDictionary*)[loan objectForKey:#"location"]
objectForKey:#"country"],
#"where",
[NSNumber numberWithFloat: outstandingAmount],
#"what",nil];
//convert object to data
NSData* jsonData = [NSJSONSerialization dataWithJSONObject:info options:NSJSONWritingPrettyPrinted error:&error];
Now, the difference lies in using NSDictionary and converting that into JSON Data. Try forming the JSON in the way given above and check if the problem persists.
you are correctly linking the category? to me it kinda looks like you are missing a category

JSONKit with Key-Value Coding and BOOLs

I'm attempting to serialize various objects by using Key-Value Coding to convert them to an NSDictionary, then JSONKit to serialize the NSDictionary to an NSString/NSData. I'm running into problems converting BOOL properties.
The KVC guidelines state that valueForKey: will, for BOOL properties, create an NSNumber via [NSNumber numberWithBool:]. JSONKit states that NSNumbers created via numberWithBool: will be serialized to true/false. I've tested JSONKit's claim and it works. However, when I access a BOOL value with KVC, I get an object which does not look like it was created via numberWithBool:. In particular, it does not evaluate equal to kCFBooleanTrue, which JSONKit uses as a marker for a boolean. The end result is that my BOOL properties are serialized to 0/1 instead of true/false, which is causing problems for the receiving API.
How do I determine if an NSNumber from KVC came from a BOOL property? Am I misreading Apple's documentation? Or is there some other way to get this serialization procedure to work?
Below is the test which is failing:
#import "JSONKit.h"
- (void) testCompareKVCBoolToNumberWithBool {
NSNumber *numberBool = [NSNumber numberWithBool:YES];
//This passes
STAssertTrue(numberBool == (id)kCFBooleanTrue, #"Number %# should kCFBooleanTrue.", numberBool);
TestModel *model = [[TestModel alloc] init];
model.boolProperty = YES;
NSNumber *kvcBool = [model valueForKey:#"boolProperty"];
//This fails
STAssertTrue(kvcBool == (id)kCFBooleanTrue, #"Number %# should be a kCFBooleanTrue.", kvcBool);
NSDictionary *dict = [NSDictionary dictionaryWithObjectsAndKeys:
numberBool, #"numberBool",
kvcBool, #"kvcBool",
nil];
NSString *jsonString = [dict JSONString];
//This yields: jsonString: {"kvcBool":1,"numberBool":true}
NSLog(#"jsonString: %#", jsonString);
}
And here is the TestModel code:
#interface TestModel : NSObject
#property (assign) BOOL boolProperty;
#end
#implementation TestModel
#synthesize boolProperty = _boolProperty;
#end
Thanks!
You may want to checkout my implementation which does this automatically - https://github.com/QBurst/KVCObjectSerializer

initWithData: expected struct NSData * warning

I am getting a strange warning with my initWithData: method:
warning: incompatible Objective-C
types 'struct NSDictionary *',
expected 'struct NSData *' when
passing argument 1 of 'initWithData:'
from distinct Objective-C type
in TRDevice.h:
#interface TRDevice : NSObject
{
NSString *name;
}
#property (nonatomic, copy) NSString *name;
-(id)initWithData:(NSDictionary *)inData;
#end
in TRDevice.m:
- (id)initWithData:(NSDictionary *)inData
{
if ((self = [super init]))
{
self.name = [inData valueForKey:TRDeviceNameKey];
}
return self;
}
And where I try to instantiate the above object:
NSUserDefaults *prefs = [NSUserDefaults standardUserDefaults];
NSArray *dataArray = [prefs arrayForKey:TRDevicesKey];
for (NSDictionary *data in dataArray)
{
TRDevice *device = [[TRDevice alloc] initWithData:data];
[self.devices addObject:device];
[device release];
}
This warning goes away if I 1) rename the init method to something else such as initWithDictionary:. or 2) if I pass in nil to initWithData:.
AFAIK NSObject implements no initWithData: method so I am not overriding it either. I have no clue why I'm getting this warning.
+alloc does not return NSObject, it returns id. that's the proper declaration. the type is (from the compiler's perspective) erased when it's returned via + (id)alloc.
most uses of initWithData: take NSData as their first argument. this is part of the reason objc 'reads so well' and is descriptive.
anyways... personally, i'd just rename it to initWithDictionary: for clarity, convention, and to avoid the warning.
another option is to simply declare your initializer to include the type:
- (id)initTRDeviceWithData:(NSDictionary *)data;
this is detailed enough that it is not likely to conflict with other classes in the translation.
or you can declare a convenience constructor:
+ (TRDevice *)newTRDeviceWithData:(NSDictionary *)data;
or a variant for an autoreleased type:
+ (TRDevice *)trDeviceWithData:(NSDictionary *)data; // eww...

performSelectorInBackground with multiple params

How can I call a method with multiple params like below with performSelectorInBackground?
Sample method:
-(void) reloadPage:(NSInteger)pageIndex firstCase:(BOOL)firstCase;
The problem is that performSelectorInBackground:withObject: takes only one object argument. One way to get around this limitation is to pass a dictionary (or array) of arguments to a "wrapper" method that deconstructs the arguments and calls your actual method:
- (void)callingMethod {
NSDictionary * args = [NSDictionary dictionaryWithObjectsAndKeys:
[NSNumber numberWithInteger:pageIndex], #"pageIndex",
[NSNumber numberWithBool:firstCase], #"firstCase",
nil];
[self performSelectorInBackground:#selector(reloadPageWrapper:)
withObject:args];
}
- (void)reloadPageWrapper:(NSDictionary *)args {
[self reloadPage:[[args objectForKey:#"pageIndex"] integerValue]
firstCase:[[args objectForKey:#"firstCase"] boolValue]];
}
- (void)reloadPage:(NSInteger)pageIndex firstCase:(BOOL)firstCase {
// Your code here...
}
This way you're only passing a "single" argument to the backgrounding call, but that method can construct the multiple arguments you need for the real call (which will take place on the same backgrounded thread).
I've just found this question and wasn't happy with any of the answers. In my opinion neither make good use of the tools available, and passing around arbitrary information in arrays and dictionaries generally worries me.
So, I went and wrote a small NSObject category that will invoke an arbitrary selector with a variable number of arguments:
Category Header
#interface NSObject (NxAdditions)
-(void)performSelectorInBackground:(SEL)selector withObjects:(id)object, ... NS_REQUIRES_NIL_TERMINATION;
#end
Category Implementation
#implementation NSObject (NxAdditions)
-(void)performSelectorInBackground:(SEL)selector withObjects:(id)object, ...
{
NSMethodSignature *signature = [self methodSignatureForSelector:selector];
// Setup the invocation
NSInvocation *invocation = [NSInvocation invocationWithMethodSignature:signature];
invocation.target = self;
invocation.selector = selector;
// Associate the arguments
va_list objects;
va_start(objects, object);
unsigned int objectCounter = 2;
for (id obj = object; obj != nil; obj = va_arg(objects, id))
{
[invocation setArgument:&obj atIndex:objectCounter++];
}
va_end(objects);
// Make sure to invoke on a background queue
NSInvocationOperation *operation = [[NSInvocationOperation alloc] initWithInvocation:invocation];
NSOperationQueue *backgroundQueue = [[NSOperationQueue alloc] init];
[backgroundQueue addOperation:operation];
}
#end
Usage
-(void)backgroundMethodWithAString:(NSString *)someString array:(NSArray *)array andDictionary:(NSDictionary *)dict
{
NSLog(#"String: %#", someString);
NSLog(#"Array: %#", array);
NSLog(#"Dict: %#", dict);
}
-(void)someOtherMethod
{
NSString *str = #"Hello world";
NSArray *arr = #[#(1337), #(42)];
NSDictionary *dict = #{#"site" : #"Stack Overflow",
#"url" : [NSURL URLWithString:#"http://stackoverflow.com"]};
[self performSelectorInBackground:#selector(backgroundMethodWithAString:array:andDictionary:)
withObjects:str, arr, dict, nil];
}
Well, I have used this:
[self performSelectorInBackground:#selector(reloadPage:)
withObject:[NSArray arrayWithObjects:pageIndex,firstCase,nil] ];
for this:
- (void) reloadPage: (NSArray *) args {
NSString *pageIndex = [args objectAtIndex:0];
NSString *firstCase = [args objectAtIndex:1];
}
with performSelectorInBackground you can only pass one argument, so make a custom object for this method to hold your data, itll be more concise than an ambiguous dictionary or array. The benefit of this is you can pass the same object around when done containing several return properties.
#import <Foundation/Foundation.h>
#interface ObjectToPassToMethod : NSObject
#property (nonatomic, strong) NSString *inputValue1;
#property (nonatomic, strong) NSArray *inputArray;
#property (nonatomic) NSInteger returnValue1;
#property (nonatomic) NSInteger returnValue2;
#end
and pass that object to your method:
ObjectToPassToMethod *obj = [[ObjectToPassToMethod alloc] init];
obj.inputArray = #[];
obj.inputValue1 = #"value";
[self performSelectorInBackground:#selector(backgroundMethod:) withObject:obj];
-(void)backgroundMethod:(ObjectToPassToMethod*)obj
{
obj.returnValue1 = 3;
obj.returnValue2 = 90;
}
make sure to clean up the object when done to prevent memory leaks

Save / Write NSMutableArray of objects to disk?

Initially I thought this was going to work, but now I understand it won't because artistCollection is an NSMutableArray of "Artist" objects.
#interface Artist : NSObject {
NSString *firName;
NSString *surName;
}
My question is what is the best way of recording to disk my NSMutableArray of "Artist" objects so that I can load them the next time I run my application?
artistCollection = [[NSMutableArray alloc] init];
newArtist = [[Artist alloc] init];
[newArtist setFirName:objFirName];
[newArtist setSurName:objSurName];
[artistCollection addObject:newArtist];
NSLog(#"(*) - Save All");
[artistCollection writeToFile:#"/Users/Fgx/Desktop/stuff.txt" atomically:YES];
EDIT
Many thanks, just one final thing I am curious about. If "Artist" contained an extra instance variable of NSMutableArray (softwareOwned) of further objects (Applications) how would I expand the encoding to cover this? Would I add NSCoding to the "Applications" object, then encode that before encoding "Artist" or is there a way to specify this in "Artist"?
#interface Artist : NSObject {
NSString *firName;
NSString *surName;
NSMutableArray *softwareOwned;
}
#interface Application : NSObject {
NSString *appName;
NSString *appVersion;
}
many thanks
gary
writeToFile:atomically: in Cocoa's collection classes only works for property lists, i.e. only for collections that contain standard objects like NSString, NSNumber, other collections, etc.
To elaborate on jdelStrother's answer, you can archive collections using NSKeyedArchiver if all objects the collection contains can archive themselves. To implement this for your custom class, make it conform to the NSCoding protocol:
#interface Artist : NSObject <NSCoding> {
NSString *firName;
NSString *surName;
}
#end
#implementation Artist
static NSString *FirstNameArchiveKey = #"firstName";
static NSString *LastNameArchiveKey = #"lastName";
- (id)initWithCoder:(NSCoder *)decoder {
self = [super init];
if (self != nil) {
firName = [[decoder decodeObjectForKey:FirstNameArchiveKey] retain];
surName = [[decoder decodeObjectForKey:LastNameArchiveKey] retain];
}
return self;
}
- (void)encodeWithCoder:(NSCoder *)encoder {
[encoder encodeObject:firName forKey:FirstNameArchiveKey];
[encoder encodeObject:surName forKey:LastNameArchiveKey];
}
#end
With this, you can encode the collection:
NSData* artistData = [NSKeyedArchiver archivedDataWithRootObject:artistCollection];
[artistData writeToFile: #"/Users/Fgx/Desktop/stuff" atomically:YES];
Take a look at NSKeyedArchiver. Briefly :
NSData* artistData = [NSKeyedArchiver archivedDataWithRootObject:artistCollection];
[artistData writeToFile: #"/Users/Fgx/Desktop/stuff" atomically:YES];
You'll need to implement encodeWithCoder: on your Artist class - see Apple's docs
Unarchiving (see NSKeyedUnarchiver) is left as an exercise for the reader :)