Enumerating a NSArray of different objects - objective-c

NSMutableArray *array = [NSMutableArray arrayWithObjects:#"Hello World!", [NSURL URLWithString:#"http://www.apple.com"], nil];
for (id *object in array) {
NSLog(#"Class name: %#", [object className]);
}
Given the above array of varying objects what is the proper way to fast enumerate thru them? Using the above code I do see my log statement properly, but Xcode does complain with the following message
Invalid receiver type 'id*' on my NSLog statement.

That should be:
for (id object in array) {
// ...
That is because id already is a pointer, see the section on id in Apples The Objective-C Programming Language for details on it.

Related

Objective-C Changing values of instance variables in method

I am write a Objective-C Code on XCode 4.4.
I have a NSMutableArray as a instance variable of my class k_info
I have defined and synthesized (nonatomic,retain) property by the name of onesplaces too.
I am unable to add a NSMUtableString object in the NSMutableArray onesplaces.
When I try to add it.The size of onesplaces remains 0 and object at zero index obviously remains null.
I tried doing this with and without using "self" key-word but it didnt worked in either case.
My syntax of adding object and printing it is right because when I create a new NSMutableArray test
and try to do the same thing it works for it but not for the instance variable onesplaces.
I cannot proceed any further in my project without solving this issue.please tell me why is it
happening and how should I solve this problem.
-(void)createinfo:(NSMutableArray )al varsis:(int)vars
{
NSMutableString stes=[[NSMutableString alloc]init];
stes=(NSMutableString*)#"string0";
[ onesplaces addObject:stes];
NSLog(#"%u",[onesplaces count]);
NSLog(#"value is: %# ",[ onesplaces objectAtIndex:0]);
[ self.onesplaces addObject:stes];
NSLog(#"%u",[onesplaces count]);
NSLog(#"value is: %# ",[self.onesplaces objectAtIndex:0]);
NSMutableArray* test=[[NSMutableArray alloc]init];
[ test addObject:stes];
NSLog(#"%u",[test count]);
NSLog(#"value is: %# ",[test objectAtIndex:0]);
}
You probably forgot to create the array. Somewhere in your code, maybe in your init method, you need to create the array before using it.
self.onesplaces = [[NSMutableArray alloc] init];
You get nil instead of error messages because Objective-C allows you to send messages to nil, which always return nil.

Fast Enumeration on NSArray of Different Types

I have this question here (as well other quesrtions on SO), and the Apple docs about Objective-C collections and fast enumeration. What is not made clear is if an NSArray populated with different types, and a loop is created like:
for ( NSString *string in myArray )
NSLog( #"%#\n", string );
What exactly happens here? Will the loop skip over anything that is not an NSString? For example, if (for the sake of argument) a UIView is in the array, what would happen when the loop encounters that item?
Why would you want to do that? I think that would cause buggy and unintended behavior. If your array is populated with different elements, use this instead:
for (id object in myArray) {
// Check what kind of class it is
if ([object isKindOfClass:[UIView class]]) {
// Do something
}
else {
// Handle accordingly
}
}
What you are doing in your example is effectively the same as,
for (id object in myArray) {
NSString *string = (NSString *)object;
NSLog(#"%#\n", string);
}
Just because you cast object as (NSString *) doesn't mean string will actually be pointing to an NSString object. Calling NSLog() in this way will call the - (NSString *)description method according to the NSObject protocol, which the class being referenced inside the array may or may not conform to. If it conforms, it will print that. Otherwise, it will crash.
You have to understand that a pointer in obj-c has no type information. Even if you write NSString*, it's only a compilation check. During runtime, everything is just an id.
Obj-c runtime never checks whether objects are of the given class. You can put NSNumbers into NSString pointers without problems. An error appears only when you try to call a method (send a message) which is not defined on the object.
How does fast enumeration work? It's exactly the same as:
for (NSUInteger i = 0; i < myArray.count; i++) {
NSString* string = [myArray objectAtIndex:i];
[...]
}
It's just faster because it operates on lower level.
I just tried a quick example... Here is my code.
NSMutableArray *array = [[NSMutableArray alloc] initWithCapacity:1];
NSNumber *number = [NSNumber numberWithInteger:6];
[array addObject:number];
[array addObject:#"Second"];
Now if I simply log the object, no problem. The NSNumber instance is being cast as an NSString, but both methods respond to -description, so its not a problem.
for (NSString *string in array)
{
NSLog(#"%#", string);
}
However, if I attempt to log -length on NSString...
for (NSString *string in array)
{
NSLog(#"%i", string.length);
}
... it throws an NSInvalidArgumentException because NSNumber doesn't respond to the -length selector. Long story short, Objective-C gives you a lot of rope. Don't hang yourself with it.
Interesting question. The most generic syntax for fast enumeration is
for ( NSObject *obj in myArray )
NSLog( #"%#\n", obj );
I believe that by doing
for ( NSString *string in myArray )
NSLog( #"%#\n", string );
instead, you are simply casting each object as an NSString. That is, I believe the above is equivalent to
for ( NSObject *obj in myArray ) {
NSString *string = obj;
NSLog( #"%#\n", string );
}
I could not find precise mention of this in Apple's documentation for Fast Enumeration, but you can check it on an example and see what happens.
Since all NSObject's respond to isKindOfClass, you could still keep the casting to a minimum:
for(NSString *string in myArray) {
if (![string isKindOfClass:[NSString class]])
continue;
// proceed, knowing you have a valid NSString *
// ...
}

How do I pass arrays of values to SudzC-generated webservice classes?

I have a sudzc service class generated from a WSDL that accepts an ArrayOfInt and ArrayOfString objects as parameters. The service method signature is this:
- (SoapRequest*) Search: (id <SoapDelegate>) handler filters: (NSMutableArray*) displayedAttributes: (NSMutableArray*) displayedAttributes;
My question is, how do I pass values into the parameters that expect NSMutableArrays?
In the above method signature, the "displayedAttributes" parameter is expecting an ArrayOfInt object (which should be populated with several integers in an int tag, e.g., <int>1</int><int>2</int><int>3</int> etc).
However none of these things which I've tried have worked:
Directly passing an NSArray/NSMutableArray of (int) objects
Directly passing an NSArray/NSMutableArray of NSNumber objects
Passing an array of strings containing #"1", #"2", #"3" etc
Passing an array of strings that already contain #"<int>1</int>", #"<int>2</int>", etc
Constructing a CXMLDocument out of a string based on the integers
I'm sure this is somehow explained in the accompanying documentation in the download -- it's just not clear to me at the moment.
#Jon Limjap: Lucky you are!!! it asks you for a type which you have dealt before, I have custom class type that SudzC generated for me (!)... It initializes only when passed CXMLNode, (which need CXMLDocument / CXMLElement).. I have no idea how to deal with such type...
an instance is: filter is a class, I have a class of the filter, but there is no way to initialize it, (except alloc-init and then setting its properties, but its properties are another such custom type.. !!!!)...If you know any "trick" to tell/configure sudzc to allow us to pass objects or fetch objects of cocoa type, do tell me....
I had similar situation of passing array of objects to SOAP request. I managed to get it working by doing following changes.
SOAP array case in not added in
+(NSString *) serialize: (id) object()
{
//look if it is array not implemented
}
so I managed to change in the following method
+ (NSString*) serialize: (id) object withName: (NSString*) nodeName {
if([object respondsToSelector:#selector(serialize:)]) {
if([object isKindOfClass:[SoapArray class]])
return [object serialize:object];
return [object serialize: nodeName];
}
NSString *temp =[NSString stringWithFormat:#"<%#>%#</%#>", nodeName, [Soap serialize: object], nodeName];
NSLog(#"serialise = %#", temp);
return temp;
}
at the time SOAP request,
NSMutableArray arr = [[MYTable_STUB_ARR alloc] init]
MYTABLE_OBJ *obj = [[MYTABLE_OBJ alloc] init];
[arr addObject:obj];
[obj release];
Pass element arr object your SOAP Request ??
This is a bit old, but I hope it will help someone. I implemented the serialize: method on SoapArray this way:
- (NSMutableString *)serialize:(NSString *)name
{
NSMutableString *str = [NSMutableString string];
//[str appendFormat:#"<%#>", name];
for (id content in self)
{
//[str appendString:[Soap serialize:content]];
[str appendString:[Soap serialize:content withName:name]];
}
//[str appendFormat:#"</%#>", name];
return str;
}
As you can see, there are some commented lines. If you uncomment them and comment the currently used one inside the for, you will get a tag named name which will contain objects tagged with the content class name.

Getting an object from an NSSet

If you can't get an object with objectAtIndex: from an NSSet then how do you retrieve objects?
There are several use cases for a set. You could enumerate through (e.g. with enumerateObjectsUsingBlock or NSFastEnumeration), call containsObject to test for membership, use anyObject to get a member (not random), or convert it to an array (in no particular order) with allObjects.
A set is appropriate when you don't want duplicates, don't care about order, and want fast membership testing.
NSSet doesn't have a method objectAtIndex:
Try calling allObjects which returns an NSArray of all the objects.
it is possible to use filteredSetUsingPredicate if you have some kind of unique identifier to select the object you need.
First create the predicate (assuming your unique id in the object is called "identifier" and it is an NSString):
NSPredicate *myPredicate = [NSPredicate predicateWithFormat:#"identifier == %#", identifier];
And then choose the object using the predicate:
NSObject *myChosenObject = [mySet filteredSetUsingPredicate:myPredicate].anyObject;
NSArray *myArray = [myNSSet allObjects];
MyObject *object = [myArray objectAtIndex:(NSUInteger *)]
replace NSUInteger with the index of your desired object.
For Swift3 & iOS10 :
//your current set
let mySet : NSSet
//targetted index
let index : Int
//get object in set at index
let object = mySet.allObjects[index]
NSSet uses the method isEqual: (which the objects you put into that set must override, in addition, the hash method) to determine if an object is inside of it.
So, for example if you have a data model that defines its uniqueness by an id value (say the property is:
#property NSUInteger objectID;
then you'd implement isEqual: as
- (BOOL)isEqual:(id)object
{
return (self.objectID == [object objectID]);
}
and you could implement hash:
- (NSUInteger)hash
{
return self.objectID; // to be honest, I just do what Apple tells me to here
// because I've forgotten how Sets are implemented under the hood
}
Then, you can get an object with that ID (as well as check for whether it's in the NSSet) with:
MyObject *testObject = [[MyObject alloc] init];
testObject.objectID = 5; // for example.
// I presume your object has more properties which you don't need to set here
// because it's objectID that defines uniqueness (see isEqual: above)
MyObject *existingObject = [mySet member: testObject];
// now you've either got it or existingObject is nil
But yeah, the only way to get something out of a NSSet is by considering that which defines its uniqueness in the first place.
I haven't tested what's faster, but I avoid using enumeration because that might be linear whereas using the member: method would be much faster. That's one of the reasons to prefer the use of NSSet instead of NSArray.
for (id currentElement in mySet)
{
// ** some actions with currentElement
}
Most of the time you don't care about getting one particular object from a set. You care about testing to see if a set contains an object. That's what sets are good for. When you want to see if an object is in a collection sets are much faster than arrays.
If you don't care about which object you get, use -anyObject which just gives you one object from the set, like putting your hand in a bag and grabbing something.
Dog *aDog = [dogs anyObject]; // dogs is an NSSet of Dog objects
If you care about what object you get, use -member which gives you back the object, or nil if it's not in the set. You need to already have the object before you call it.
Dog *spot = [Dog dogWithName:#"Spot"];
// ...
Dog *aDog = [dogs member:spot]; // Returns the same object as above
Here's some code you can run in Xcode to understand more
NSString *one = #"One";
NSString *two = #"Two";
NSString *three = #"Three";
NSSet *set = [NSSet setWithObjects:one, two, three, nil];
// Can't use Objective-C literals to create a set.
// Incompatible pointer types initializing 'NSSet *' with an expression of type 'NSArray *'
// NSSet *set = #[one, two, three];
NSLog(#"Set: %#", set);
// Prints looking just like an array but is actually not in any order
//Set: {(
// One,
// Two,
// Three
// )}
// Get a random object
NSString *random = [set anyObject];
NSLog(#"Random: %#", random); // Random: One
// Iterate through objects. Again, although it prints in order, the order is a lie
for (NSString *aString in set) {
NSLog(#"A String: %#", aString);
}
// Get an array from the set
NSArray *array = [set allObjects];
NSLog(#"Array: %#", array);
// Check for an object
if ([set containsObject:two]) {
NSLog(#"Set contains two");
}
// Check whether a set contains an object and return that object if it does (nil if not)
NSString *aTwo = [set member:two];
if (aTwo) {
NSLog(#"Set contains: %#", aTwo);
}

Print_r equivalent for XCode? I just want to see my array's content

I am a web developer trying to make it in an Xcode world, and I need to see the contents of an array I have in my console, what are my options?
There's no need to iterate through an array just to print it.
All the collection types have a -description method that returns a NSString of their contents. Just use the object format specifier %#
NSLog(#"%#", array);
As an additional note you can dynamically print NSArrays and other objects in the debugger using po object. This uses the same description method that NSLog does. So it's not always necessary to litter your code with NSLogs, especially if you're already in the debugger.
You could try something like this:
NSArray *array = [NSArray arrayWithObjects: #"a", #"b", #"Hello World", #"d", nil];
for (id obj in array) {
NSLog(#"%#", obj);
}
... which would log each item in the array to the console in their own separate NSLog messages.
Or if you want to see your NSDictionary's content (which is comparable to a PHP associated array()), you could use:
for (id key in [dictionary allKeys]) {
NSLog(#"Key: %#, Value: %#", key, [dictionary objectForKey:key]);
}