Automatically trim NSString in custom setter? - objective-c

I have an object w/ an NSString for a property. I would like to use a custom setter for this property so that it always trims all whitespace when the string is set.
Normally this is done with NSString *blah2 = [blah stringByTrimmingCharactersInSet:[NSCharacterSet whitespaceAndNewlineCharacterSet]];, but I'm not sure about how to formulate this for a custom setter. Isn't it bad to use autoreleased objects like this in the setter? Should I use a mutable string instead and then convert it to unmutable?
Thanks for any help you guys can provide.

I'm going to presume ARC.
#import <Foundation/Foundation.h>
#interface Foo:NSObject
#property (nonatomic,copy) NSString *myString;
#end
#implementation Foo
#synthesize myString = _myString;
- (void)setMyString:(NSString *)aString;
{
_myString = [[aString stringByTrimmingCharactersInSet:[NSCharacterSet whitespaceAndNewlineCharacterSet]] copy];
}
#end
int main(int argc, char *argv[]) {
#autoreleasepool {
Foo *someFoo = [[Foo alloc] init];
someFoo.myString = #"Blah ";
NSLog(#"Length = %ld",[[someFoo myString] length]); // prints 4
}
}
In the setter, I do copy the parameter of the setter in the event someone tries to provide an instance of NSMutableString - presuming that Foo is not expected to modify the original string.
EDIT:
Or if manual reference counting, the setter might be:
- (void)setMyString:(NSString *)aString;
{
if( aString != _myString ) {
[_myString release];
_myString = [[aString stringByTrimmingCharactersInSet:[NSCharacterSet whitespaceAndNewlineCharacterSet]] copy];
}
}

Related

Objective-C Categories: Method not called

I'm trying to learn the basics of Objective-C through reading "Learning Objective-C 2.0" Theres an exercise on categories where your asked to add a method to NSString through use of categories. My simple program is below. it (should) take a string and then reverses the order of the words.
Main
#import <Foundation/Foundation.h>
#import "CatNSString.h"
int main(int argc, const char * argv[]) {
#autoreleasepool {
NSString *test = #"Dog bites Man";
NSString *test1 = nil;
test1 = [test1 reverseWords: test];
NSLog(#"%# : %#", test, test1);
}
return 0;
}
Interface
#import <Foundation/Foundation.h>
#interface NSString (CatNSString)
- (NSString*) reverseWords:(NSString*)string;
#end
Implementation
#import "CatNSString.h"
#implementation NSString (CatNSString)
- (NSString*) reverseWords: (NSString*) string
{
NSString *stringReturn = nil;
NSArray *arrayString = [string componentsSeparatedByCharactersInSet:[NSCharacterSet whitespaceCharacterSet]];
stringReturn = [string stringByAppendingString:#"hello"];
for (NSString *word in arrayString)
{
NSString *stringTmp1 = word;
NSString *stringTmp2 = stringReturn;
stringReturn = [stringTmp1 stringByAppendingString:stringTmp2];
NSLog(#"stringTmp1: %#", stringTmp1);
}
return stringReturn;
}
#end
It compiles but the program acts as though the method is never called. If I place a NSLog call in the method there is no output to the console. Can anyone see what I'm not doing that I should be doing?
You just did it wrong way.
Here is the proper way to define a Category:
#interface NSString (CatNSString)
-(NSString *)reverse;
#end
#implementation NSString (CatNSString)
-(NSString*)reverse {
// `Self` keyword will refer to your original string, no need to pass it as a parameter
NSArray *words = [self componentsSeparatedByCharactersInSet:[NSCharacterSet whitespaceCharacterSet]];
return [[[words reverseObjectEnumerator] allObjects] componentsJoinedByString:#" "];
}
#end
And in your main code, do the following:
NSString *test = #"Dog bites Man";
NSLog(#"%#", [test reverse]);

Duplicated custom object in NSSet

I have some problems about the NSMutableSet in Objective-C.
I learnt that the NSSet will compare the two objects' hash code to decide whether they are identical or not.
The problems is, I implemented a class that is subclass of NSObject myself. There is a property NSString *name in that class. What I want to do is when instances of this custom class has the same variable value of "name" , they should be identical, and such identical class should not be duplicated when adding to an NSMutableSet.
So I override the - (NSUInteger)hash function, and the debug shows it returns the same hash for my two instances obj1, obj2 (obj1.name == obj2.name). But when I added obj1, obj2 to an NSMutableSet, the NSMutableSet still contained both obj1, obj2 in it.
I tried two NSString which has the same value, then added them to NSMutableSet, the set will only be one NSString there.
What could be the solution? Thank you for any help!
The custom Class:
Object.h:
#import <Foundation/Foundation.h>
#interface Object : NSObject
#property (retain) NSString *name;
#end
Object.m
#implementation Object
#synthesize name;
-(BOOL)isEqualTo:(id)obj {
return [self.name isEqualToString:[(Object *)obj name]] ? true : false;
}
- (NSUInteger)hash {
return [[self name] hash];
}
#end
and main:
#import <Foundation/Foundation.h>
#import "Object.h"
int main(int argc, const char * argv[])
{
#autoreleasepool {
Object *obj1 = [[Object alloc]init];
Object *obj2 = [[Object alloc]init];
obj1.name = #"test";
obj2.name = #"test";
NSMutableSet *set = [[NSMutableSet alloc] initWithObjects:obj1, obj2, nil];
NSLog(#"%d", [obj1 isEqualTo:obj2]);
NSLog(#"%ld", [set count]);
}
return 0;
}
Instead of implementing isEqualTo: you have to implement isEqual:
- (BOOL)isEqual:(id)object {
return [object isKindOfClass:[MyObject class]] &&
[self.name isEqual:[(MyObject *)object name]];
}
This will (probably falsely) return NO if both self.name and object.name are nil. If you want to return YES if both properties are nil you should use
- (BOOL)isEqual:(id)object {
if ([object isKindOfClass:[MyObject class]]) {
return (!self.name && ![(MyObject *)object name]) ||
[self.name isEqual:[(MyObject *)object name]];
}
return NO;
}

Objective-C, retaining a variable after NSLog-output

I would like to know how weak property work in Objective-C.
In this example the value of the weak property "myString" in "myClass" is kept only when I print it with NSLog. Why is that?
#import <Foundation/Foundation.h>
#include <stdio.h>
#interface myClass : NSObject
#property (nonatomic, weak)NSString *myString;
- (void)readString;
#end
#implementation myClass
#synthesize myString;
- (void)readString
{
const int MAXBUFFER = 80;
char buffer[MAXBUFFER+1];
NSLog(#"Input string:");
fgets(buffer, MAXBUFFER, stdin);
NSString *tempString = [[NSString alloc] initWithUTF8String:buffer];
myString = tempString;
NSLog(#"myString: %#", myString); // Why does this line make all the difference?
}
#end
int main(int argc, const char * argv[])
{
#autoreleasepool
{
myClass *myInstance = [[myClass alloc] init];
[myInstance readString];
NSLog(#"myInstance.myString: %#", myInstance.myString);
}
return 0;
}
If the NSLog-line in the readString-method is commented out myInstance.myString becomes "(null)". Why??
From Apple:
weak Specifies that there is a weak (non-owning) relationship to the
destination object. If the destination object is deallocated, the
property value is automatically set to nil.
So Basically when arc insert code into [readString], he does:
NSString *tempString = [[NSString alloc] initWithUTF8String:buffer];
myString = tempString;
// + arc [tempString release]
So your tempString no longer exist outside the method, because nothing retain it.
But when you add NSlog inside [readString] with myString, NSLog will keep reference to the pointer (i don't know exactly how), but he actually does since he logs them.

How to return an NSString * in Objective C (keep getting invalid summary)

So I presume this is a memory issue, here's the code:
- (NSString *)giveMeAStringGoddammit
{
NSString *s;
// switch statement to choose which string to assign to s, so essentially:
s = #"a string";
return s;
}
And the calling code:
NSString *aString;
aString = [self giveMeAStringGoddammit];
However after this call, aString has an invalid summary when debugged and crashes when run.
I suspect I'm missing a retain or something, can someone help? Thanks.
What you've got works just fine:
#import <Foundation/Foundation.h>
#interface Test : NSObject
- (NSString *)giveMeAStringGoddammit;
#end
#implementation Test
- (NSString *)giveMeAStringGoddammit
{
NSString *s;
// switch statement to choose which string to assign to s, so essentially:
s = #"a string";
return s;
}
#end
int main (int argc, const char * argv[])
{
#autoreleasepool {
Test *t = [[Test alloc] init];
NSLog(#"t says: %#", [t giveMeAStringGoddammit]);
}
return 0;
}
The output of this program is:
t says: a string
To make this a little more realistic, let's change it to use a property:
#import <Foundation/Foundation.h>
#interface Test : NSObject
#property(copy, nonatomic) NSString *string;
- (NSString *)giveMeAStringGoddammit;
#end
#implementation Test
#synthesize string;
- (NSString *)giveMeAStringGoddammit
{
NSString *s;
// switch statement to choose which string to assign to s, so essentially:
s = self.string;
return s;
}
#end
int main (int argc, const char * argv[])
{
#autoreleasepool {
Test *t = [[Test alloc] init];
t.string = #"Hello world!";
NSLog(#"t says: %#", [t giveMeAStringGoddammit]);
}
return 0;
}
This does what you'd expect:
t says: Hello world!
You have created a pointer object and it's expected to increment it's retain count whenever you referring them, for increasing retain count the string should be initiated and allocated with memory else you could use [NSString stringwithString:[self giveMeAStringGoddammit]. You can use this definition only when you exactly need it reference locally. because whenever you try to refer it out side the auto release pool will crash the app (hence it's not retained manually). So if you need to use it out side of the function, better use [NSString alloc]init] and then load your string to the pointer object. Well the way to make your code to work is add the lines NSString *aString = [NSString stringWithString:[self giveMeAStringGoddammit]];
NSLog(#"My Str:%#",aString); Hooray now the goddammit string was given......

Objective-C error EXC_BAD_ACCESS help please

I am currently using the pragmatic screencast on Objective-C to help me program in objective-c. I have a background in Java and C++, but I am having a very difficult time getting used to everything in Objective(Mostly because I am not comfortable with the syntax).
Below is the error I am receiving with all the code.
I am also getting a warning in movie.m class as well: Wirtable atomic property 'title'
cannot be pair a synthesized setter/getter with a user defined setter/getter
thanks for your help.
I am receive this error
Current language: auto; currently objective-c
warning: Couldn't find class validation function, calling methods on uninitialized objects may deadlock your program.
Program received signal: “EXC_BAD_ACCESS”.
I ran it through the debugger and the address of movie in the code below is in red
main.m
int main (int argc, const char * argv[]) {
NSAutoreleasePool * pool = [[NSAutoreleasePool alloc] init];
Movie *movie = [[Movie alloc] initWithTitle:#"iron man"
andRating:5
andYear:2008];
[movie play];
NSLog(#"our movie is %#", movie);
[pool drain];
return 0;}
Movie.h
interface Movie : NSObject {
NSString *title;
int rating;
int year;
}
- (id)initWithTitle:(NSString *)newTitle
andRating:(int)newRating
andYear:(int) year;
#property(assign) NSString *title;
#property(assign) int rating;
#property(assign) int year;
-(void) play;
#end
Movie.m
#import "Movie.h"
#implementation Movie
#synthesize title;
#synthesize rating;
#synthesize year;
-(id)initWithTitle:(NSString *)newTitle
andRating:(int)newRating
andYear:(int)newYear;
{
self = [super init];
if(nil != self){
self.title = newTitle;
self.rating = newRating;
self.year = newYear;
}
return self;
}
-(NSString *) description{
NSString *oldDescription = [super description];
return [NSString stringWithFormat: #"%# title =%#, rating =%d year=%#",
oldDescription, self.title, self.rating, self.year];
}
- (void)setTitle:(NSString *)newTitle {
title = [newTitle capitalizedString];
}
-(void) play {
NSLog(#"Playing %#", self);
}
You use year=%# when it should be year=%d.
Some more random thoughts:
You should retain or better even copy the title instead of assigning it.
The init method should be named
-(id)initWithTitle:(NSString *)aTitle
rating:(int)aRating
year:(int)aYear;
Don't forget a dealloc method then.
Your title property is an object type and so should in generally be either retain or copy -- in the case of NSString properties, it is traditional to use copy to avoid issues when you're passed an NSMutableString instead.
#property (copy) NSString* title;
Since you explicitly define the setter, you then need to implement this policy yourself, something like this:
- (void)setTitle:(NSString *)newTitle
{
[title release];
title = [[newTitle capitalizedString] copy];
}
You'll also need to include a dealloc method to clean up:
- (void) dealloc
{
[title release];
[super dealloc];
}