HOM with Objective C - objective-c

I am new to Objective-C, but I tried to use HOM (higher order message) in order to iterate over an NSArray and append a string to each element.
Here is my code:
void print( NSArray *array ) {
NSEnumerator *enumerator = [array objectEnumerator];
id obj;
while ( nil!=(obj = [enumerator nextObject]) ) {
printf( "%s\n", [[obj description] cString] );
}
}
int main( int argc, const char *argv[] ) {
NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
NSArray *names = [[NSArray alloc] init];
NSArray *names_concat = [[NSArray alloc] init];
names = [NSArray arrayWithObjects:#"John",#"Mary",#"Bob",nil];
names_concat = [[names collect] stringByAppendingString: #" Doe"];
print(names_concat);
[pool release];
}
What is wrong with this code?
My compiler (gcc) says NSArray may not respond to "-collect"

Because the method -collect is not part of the standard Objective-C library!
You need to get some library and add it to your project to start with. See an article at CocoaDev. For collect, see this blog article.
Starting from 10.6, Apple added some methods to NSArray which accepts blocks (or in other words closures). See NSArray documentation and look for words block.
By the way, on a rather unrelated point, please don't use the method cString. This is deprecated! See NSString documentation. cString is very bad concerning the encoding of the characters. I know you're only using it for a debug purposes, but I don't want you to make it a habit to use deprecated methods in general, and especially about methods concerning encodings.
OS X is in general a OS very friendly to many encodings, but as an East Asian I saw many great programs behaving badly just because the programmers used cString etc... Everything works as long as you use non-deprecated methods. Sorry for putting an unrelated comment :p

Related

Allocate using #define

I created a preprocessor definition to allocate and construct classes for me as follows:
#define new(cls) cls* _new() {return [[cls alloc] init];}()
Now I tried using it like:
- (NSMutableArray*) stack
{
if (!_stack)
{
/*_stack = [[NSMutableArray alloc] init];*/
_stack = new(NSMutableArray);
}
return _stack;
}
but it says expected expression and unexpected NSMutableArray interface. What is wrong with my definition and why can't I do it? Is there another way I can do this?
Just use new:
_stack = [NSMutableArray new];
which does the same thing as
_stack = [[NSMutableArray alloc] init];
No need for a macro which will be confusing to others.
For a long time the use of new was discouraged by Apple but it seems with the arrival of ARC it has come back into favor even in Apple's documentation.

Should I use defensive copies in Objective C? [closed]

Closed. This question needs details or clarity. It is not currently accepting answers.
Want to improve this question? Add details and clarify the problem by editing this post.
Closed 9 years ago.
Improve this question
I was playing around with mutability, and I came up with the following code in which an immutable object can be cast to a mutable one.
- (NSString *) getaString {
NSMutableString * string = [[NSMutableString alloc] init];
[string appendString:#"This "];
[string appendString:#"was mutable."];
return string;
}
- (void)viewDidLoad {
[super viewDidLoad];
//Get a string returns an NSString, which is then cast to a mutable string with a compler warning.
NSMutableString * string = [self getaString];
[string appendString:#" And apparently still is"];
_showText.text = string;
}
or with no compiler warning
- (NSArray *) getaString {
NSMutableString * string = [[NSMutableString alloc] init];
[string appendString:#"This "];
[string appendString:#"was mutable."];
//Cast to NSString.
NSString * immutableString = string;
NSArray * array = [[NSArray alloc] initWithObjects:immutableString, nil];
return array;
}
- (void)viewDidLoad {
[super viewDidLoad];
NSMutableString * string = [[self getaString] objectAtIndex:0];
[string appendString:#" And apparently still is"];
_showText.text = string;
}
The UITextField shows the whole string of "This was mutable. And apparently still is", with no compiler warning. I've seen multiple SO posts that recommend simply casting or using mutable objects as immutable objects, but as I've just shown, that can be dangerous. Also, the cast still works without the array, but I do get a compiler warning.
My question is, should I be considering using java style defensive copies? I haven't seen any mention of defensive copies in objective C, and all I could find in Apple's documentation was a vague mention that it’s best to adopt some defensive programming practices. I'm concerned both about security and about protecting against careless coding.
The example you've given is misleading.
You are storing a NSMutableString in an array and you are taking it out. Why would you expect the string to be immutable after that?
However there are cases in which defensive copies are pretty much a standard in Objective-C.
Consider a class (let's call it CustomClass) defining a property for an NSString and a method to print it:
#property (nonatomic, strong) NSString *aString;
- (void)printTheString;
Now, since NSMutableString is a subclass of NSString, a client of this class could potentially do something like:
CustomClass *anObject = [CustomClass new];
NSMutableString *aMutableString = [NSMutableString stringWithString:#"Hey!"];
anObject.aString = aMutableString;
[anObject printTheString]; // Hey!
[aMutableString appendString:#" Got you!"];
[anObject printTheString]; // Hey! Got you!
which can be dangerous in some cases.
It has then become common practice to use the copy attribute, instead of strong, for immutable classes with a mutable subclass:
#property (nonatomic, copy) NSString *aString;
In this way a defensive copy is made when the string is assigned, preventing the client to mess with the object later on.
The previous example would then print Hey! both times.
It's also worth noting that for most of this classes sending copy to an immutable object returns the same object, instead of an actual copy. In this way you can have your cake and eat it too, since the copy will be performed only when needed.
I don't know for sure whether you should make defensive copies in Obj-c, but it is possible to make mutable copies of an immutable string, and it is possible to make immutable versions of a mutable string.
There aren't any immutable strings in your code, by the way. NSMutableStrings in an NSArray are still mutable, so you aren't doing any conversion. The array is the thing that cannot have objects appended to it.
Here's why: The array is just holding a pointer to the NSMutableString, not the actual object, so you're not changing the contents of the array by changing the contents of the string, because the memory address pointed to by the array stays the same.
Example:
NSString *original = #"Hello, world!";
// putting the original in a mutable string
NSMutableString *copy = [original mutableCopy];
NSArray *array = [NSArray arrayWithObjects:copy, nil];
// the object in the array can still be modified
[[array objectAtIndex:0] appendString:#" Goodbye, cruel world!"];
// make an immutable version of the mutable string
NSString *copyOfTheCopy =[NSString stringWithString:[array objectAtIndex:0]];
NSLog(#"%#", [array objectAtIndex:0]);
// just to make sure...
[[array objectAtIndex:0] appendString:#"Got you!"];
NSLog(#"%#", [array objectAtIndex:0]);
NSLog(#"%#", copyOfTheCopy);
The output should be:
2013-11-03 21:16:06.099 SomeProject[7045:303] Hello, world! Goodbye, cruel world!
2013-11-03 21:16:06.100 SomeProject[7045:303] Hello, world! Goodbye, cruel world!Got you!
2013-11-03 21:16:06.100 SomeProject[7045:303] Hello, world! Goodbye, cruel world!
Another answer given by Gabriele Petronella caused me to make a change that gets rid of one of those gotchas with pointers. This one doesn't use a pointer to the NSMutableString and instead makes a new string from the old one. Sorry I can't vote you up yet, you taught me something :)
If you're looking to obtain an immutable NSString from an NSMutableString, you can do something like this:
NSString *anImmutableString = [NSString stringWithString: aMutableString];
So if we take your original code in the question, and I'm going to assume that your viewDidLoad is where you want an immutable string:
- (NSArray *) getaString {
NSMutableString * string = [[NSMutableString alloc] init];
[string appendString:#"This "];
[string appendString:#"was mutable."];
NSArray * array = [[NSArray alloc] initWithObjects:string, nil];
return array;
}
- (void)viewDidLoad {
[super viewDidLoad];
NSString *string = [NSString stringWithString:[[self getaString] objectAtIndex:0]];
//[string appendString:#" And apparently still is"];
_showText.text = string;
}
Now, if you still want to append to string in viewDidLoad, you can, but you'd have to do it differently (the same way you append to any other NSString). It'd look something like this:
NSString *newString = [string stringByAppendingString:#" And apparently still is"];
Now look at the contents of the variables.
string = "This was mutable." //as you built it from an NSMutableString in the getaString method
newString = "This was mutable. And apparently still is" //as built from calling "stringByAppendingString" method on your variable string in viewDidLoad
It's important to note that the contents of newString are quite misleading here. newString is not an NSMutableString. It's a regular NSString. Calling stringByAppendingString returns an NSString that is the result of appending the argument you send to the end of the NSString you're calling the method on. (And the contents of the NSString you called the method on remain unchanged--it's immutable).
If you would like newString to be an NSMutableString, you'll have to do something like this:
NSMutableString *newString = [[string stringByAppendingString:" And apparently still is"] mutableCopy];

componentsJoinedByString causing crash with EXC_BAD_ACCESS

I'm pretty sure this is eactly the same problem as in componentsJoinedByString gives me EXC_BAD_ACCESS
basically, an array is populated using this code, with ARC turned on:
-(NSMutableArray *)getArrayOfCommaSeparatedSelectorStrings{
NSMutableArray *Array = [[NSMutableArray alloc] init];
for(NSMutableArray *e in [self getArrayOfSelectorArrays]) {
[Array addObject:[displayCSSInformation returnArrayAsCommaList:e]];
}
return Array;
}
and then displayCSSInformation tries to return a comma separated list with this method :
+(NSString *)returnArrayAsCommaList:(NSMutableArray *)ToBeConverted{
NSString *test = [ToBeConverted componentsJoinedByString:#", "];
return test;
}
Thanks for your help.
There's usually no need to use a separate method if all that method does is invoke another method. Remove your +returnArrayAsCommaList: method and just use componentsJoinedByString: on the array directly.
- (NSMutableArray *) getArrayOfCommaSeparatedSelectorStrings
{
NSMutableArray *array = [[NSMutableArray alloc] init];
for (NSMutableArray *e in [self getArrayOfSelectorArrays])
[array addObject:[e componentsJoinedByString:#", "]];
return array;
}
The above should work (it works in my small test example), if you are still getting errors:
Make sure that getArrayOfSelectorArrays is actually returning an array of array of strings. Log the output to the console or step through with a debugger.
Use the “Build & Analyze” option to have the static analyser check for any issues. This is less of an issue with ARC but it will still pick up things such as using uninitialised values.
Make sure you have properly bridged ownership from any Core Foundation objects.

Accessing a released NSString doesn't make the app crash

I know this is probably the exact opposite of the questions asked on Stackoverflow, but something really weird happened to me today.
I was trying to show someone how to work with Instruments and NSZombies, so I tried to create a crash.
I declared a NSString, released it and then tried to access it but the app didn't crash
NSString* test = [[NSString alloc] init];
[test release];
NSlog(#"%#", test);
I even tried to release it twice, but it still didn't make the app crash, it just printed null.
Can anyone please explain me what I did wrong or where is the flaw in my logic?
Thank you
EDIT : I tried something like this too and still no crash
NSString* test = [[NSString alloc] init];
test = #"something";
NSlog(#"%#", test);
[test release];
NSlog(#"%#", test);
I even added two consecutive releases, and a test = nil; after a release, just to make sure.
NSString may sometimes behave strangely.
In your example, you allocated a NSString without data.
That's the reason why it doesn't crash.
Allocating a NSString without data has no sense, as NSString objects are immutable.
In such a case, NSString will return a kind of singleton instance, meaning you can't release it (well, you can, but it will have no effect).
Every time you allocate a NSString object this way, the same instance will be returned.
Try this instead:
NSString * test = [ [ NSString alloc ] initWithCString: "hello, world" encoding: NSUTF8StringEncoding ];
[ test release ];
NSLog( #"%#", test );
Then it will crash, as expected...
To prove what I have explained earlier, try this:
NSString * test1 = [ [ NSString alloc ] init ];
NSString * test2 = [ [ NSString alloc ] init ];
NSLog( #"OK: %p %p", test1, test2 );
You can see the same address is printed, meaning only one object is allocated (the "singleton" one, for NSString objects without data).
EDIT
I've seen on your comment that you tried to "give the string a value".
This is not possible using NSString, as they are immutable.
I guess you tried this:
NSString * s = [ [ NSString alloc ] init ];
s = #"hello, world";
In such a case, you are not giving a value!
You are re-assigning a pointer!
It means your s variable (which is a pointer) will then point to another string object.
And it also mean you won't be able to access your previously allocated string, as you just lost the pointer to it.
If you want string objects that can be changed, take a look at the NSMutableString class.
But keep in mind that changing a pointer value is not the same as changing an object's value!
There are two things going on in your example.
First, nothing else happened between your release and your NSLog that would modify the memory where your string was stored, so it happens to still be intact. You can't rely on that.
Second, the system has a special case for [[NSString alloc] init]. It returns a predefined object of type __NSCFConstantString that is never deallocated. If you print its reference count, you'll see that it is 2147483647 (0x7fffffff). That is a magic number indicating that any attempt to retain or release the object is ignored.
If you take a look at the retainCount on an NSString allocated with either #"some_string" or [[NSString alloc] init] then you'll see that it's -1. This is because the compiler has known what the object is at compile time and has managed to store it as a literal. So when you release it, it's not actually going to do anything. It's not an object that has been allocated at runtime - it's just always there in your application binary.
You could make your crash occur by doing something like this on Mac:
#import <Foundation/Foundation.h>
int main(int argc, char** argv) {
NSString *test = [[NSString alloc] initWithCString:argv[0] encoding:NSASCIIStringEncoding];
NSLog(#"test = %p", test);
[test release];
NSLog(#"test = %p", test);
[test release];
NSLog(#"test = %p", test);
return 0;
}
The string is now no longer known at compile time, so it will actually be an object allocated and have a positive retain count, etc.
Note that NSNumber (on Mac OS X Lion) also exhibits this behaviour if the number is known at compile time. This is even more interesting as it's an example of tagged pointers in action. Take a look at this blog article for a discussion on that. And here is some code to show it:
#import <Foundation/Foundation.h>
int main(int argc, char** argv) {
NSNumber *test = [[NSNumber alloc] initWithInt:1];
NSLog(#"test = %p", test);
[test release];
NSLog(#"test = %p", test);
[test release];
NSLog(#"test = %p", test);
return 0;
}
Note that also doesn't crash, it just prints out test = 0x183 3 times.

NSDictionary: URL-Encoding

Can anyone please review the following method and tell me if it is necessary to release the enumerator for the dictionary keys after using it?
I'm new to objective c and not always sure, when to release variables returned from the SDK methods. I suppose the keyEnumerator method returns an autoreleased instance so I don't have to care about it when I'm finished with it. Right?
Hmm.. maybe anything else wrong in this method? ;)
#import "NSDictionaryURLEncoding.h"
#import <Foundation/NSURL.h>
#implementation NSDictionary(URLEncoding)
-(NSString *)urlEncoded
{
NSEnumerator *keyEnum = [self keyEnumerator];
NSString *currKey;
NSString *currObject;
NSMutableString *result = [NSMutableString stringWithCapacity: 64];
while ((currKey = [keyEnum nextObject]) != nil)
{
if ([result length] > 0)
{
[result appendString: #"&"];
}
currObject = [[self objectForKey: currKey] stringByAddingPercentEscapesUsingEncoding:NSUTF8StringEncoding];
currKey = [currKey stringByAddingPercentEscapesUsingEncoding:NSUTF8StringEncoding];
[result appendString: [NSString stringWithFormat:#"%#=%#", currKey, currObject]];
}
return result;
}
#end
You must never release objects for which you did not call the alloc method if you also didn't retain them. See the Memory Management Programming Guide for Cocoa.
Also, if you're programming for the iPhone OS or if you are targetting Mac OS X 10.5 or later, I suggest you use the for(... in ...) language construct to enumerate through your dictionary. It's much faster than using enumerators.
for(NSString* currKey in self)
{
NSString* currObject = [self objectForKey:currKey];
// the rest of your loop code
}
You should read the Fast Enumeration section of the Objective-C Programming Language Reference.
I also suggest, as a friendly advice, that you don't extend NSDictionary or create a category on it unless you really, really need it.