It looks to me like sending setString:
to a NSMutableString
that hasn't had init
called on it yet does not call init
on it's own. For example:
NSMutableString *string; // Declare, but do not init yet
[string setString:@"foo"];
NSLog (@"%@",string); // Prints "(null)"
I'd like to overwrite this behavior, so that essentially
- (void) setString:(NSString *)aString
{
if (!self)
{
self = [self initWithString:aString];
}
else
{
[super setString:aString];
}
}
I could do so with a subclass, but I would have to go through my project and replace all my NSMutableString
s with my subclass, which is a pain. I was looking at the Apple Docs and it looks like what I want to do is create a Category for NSMutableString
. I haven't used categories before, so I got a couple questions:
First, it looks like categories only allow me to add new methods, it doesn't allow me to overwrite existing methods. I suppose it is easy enough to just create a setStringWithInit:
method that does what I want, so this first issue isn't really an issue after all (although I still have to do a find-replace through my project for setString
, but oh well).
Second, and more importantly, how do I check if the sender of my new method is nil
? If setString:
returned something other than void
, I think I could get it to work, but that's not the case here. How do I get the if (!self)
check from my code above to work with a category?
Or are categories not the way to go for this kind of thing, and I'd just be better off sub-classing after all?
EDIT:
So the strings I'm using this on will actually be @property
s of a custom NSObject
subclass. In my code, I'll actually be calling [myObject.someProperty setString:@"foo"];
, and the debugger is showing me that someProperty
is nil when I try to do this. Also, in other parts of my app I'm checking if (!myObject.someProperty)
to see if that property has been used yet, so I don't want to just automatically self.someProperty = [[NSMutableString alloc] init];
in the init
method of myObject
's class.
Although now that I think about it, I think I can get away with replacing if (!myObject.someProperty)
with if ([myObject.someProperty length] == 0)
, which would allow me to go through and alloc-init things right away. But if I'm initializing everything right away, that will create some memory space for it, correct? It's probably negligible though. Hm, perhaps this is what I should be doing instead.
macros FTW!
#define setString(X,Y) if(!X){X=[[NSMutableString alloc] initWithString:Y];}else{[X setString:Y];}
When I try to assign a value with this:
NSMutableString
, or if Y isn't an NSString
or NSMutableString
Y
is nil
, but I expect it will cause a crash, which is what I want.Drawbacks:
setString()
instead of the stock setString:
setValue:forKey:
, which I use extensively - one step at a time I guess) - a one size fits all solution would have been nice - maybe a topic for another question. Whatever I pass in has to be a NSString
before I pass it, I cannot convert it to a string in line - but at least I get a build error if I try to do so, so it isn't up to me to remember to do so (still adds clutter though)
NSMutableString *X;
int y = 0;
setString(X, [NSString stringWithFormat:@"%d",y]) // <--- Doesn't work
NSString *Y = [NSStirng stringWithFormat:@"%d",y];
setString(X,Y) // <--- Does work