Vincent Gable’s Blog

September 18, 2009

Strange AOL Instant Message Filtering

Filed under: Announcement,Bug Bite,Security | , , , ,
― Vincent Gable on September 18, 2009

You can’t send a message over AIM that has a JavaScript event handler name, followed by = in it. The message seems to be blocked on the server, not in the client, as this behavior was observed in different AIM clients (iChat, Adium, and meebo.)

Examples

The following messages can’t be sent over AIM:

onclick=

onclick =

Yo dawg, I heard you liked onclick= in your JavaScript…

Interestingly, using a newline, instead of space, between the handler name and = allows the message to be sent, even though it is still valid HTML/JavaScript. For example, you can send,

onclick
=x();
/*this is fine*/

I suspect there is an interesting security story behind all of this. If you know how and why this filtering came to pass, I please leave a comment.

Thanks to Dustin Silverman for helping me investigate this. In case you were wondering how I stumbled onto this behavior — I was sending snippets of HTML from twitterglyphs.com/ over AIM.

September 11, 2009

Never Start An Integer With 0

When programming, never start an integer with 0. Most programming languages treat a decimal number that starts with 0 as octal (base-8). So x = 013; does not set x to 13. Instead x is 11, because 013 is interpreted as 138 not 1310.

Languages with this quirk include: C, C++, Objective-C, Java, JavaScript, Perl, Python 3.0, and Ruby. If you add up the “market share” of these languages, it comes out to above 50%, which is why I say most languages.

“But I use {Smalltalk, Haskell, Lisp, etc.}”

I’m jealous that you get to use such a nice language. However, it’s bad programming hygiene to pick up habits that are dangerous in common languages.

Now, I assume you wouldn’t write 7 as 007, unless the leading zero(s) carried some extra meaning. There are cases where this clarity outweighs “cleanliness” (unless the code meant to be ported to a C-like language).

But you should at least be aware of this inter-lingual gotcha.

July 2, 2009

Design for Mental Imperfections

When it comes to building the physical world, we kind of understand our limitations. We build steps. … We understand our limitations. And we build around it. But for some reason when it comes to the mental world, when we design things like healthcare and retirement and stockmarkets, we somehow forget the idea that we are limited. I think that if we understood our cognitive limitations in the same way that we understand our physical limitations, even though they don’t stare us in the face in the same way, we could design a better world. And that, I think, is the hope of this thing.

Dan Ariely, concluding a very entertaining TED talk. The transcript is up, but I liked his delivery so much I watched the video.

Stairs and ladders aren’t an implication that you’re too weak to pull yourself out of a pool. Yet amazingly people sometimes get insulted by simplified interfaces, as if it somehow implies they are so stupid they can’t handle complexity.

I was fortunate enough to hear Jonathan Ive talk about launching the iMac. As he was leaving a store on launch-day, a furious technology reported accosted him in the parking lot, shouting What have you done? He was incensed that the iMac was so cute, approachable, and untechnical — everything that he thought a computer shouldn’t be.

Some of this behavior is explained by simple elitism. If computers are hard to use, than it keeps the idiots out, and proves what a macho man you are if you can use them.

But I suspect refusal to accept our cognitive limitations is also related to our cultural refusal to accept mental illness. Quoting Mark C. Chu-Carroll’s experience with depression,

How many people have heard about my stomach problems? A lot of people. I need to take the drugs three times a day, so people see me popping pills. … Out of the dozens of people who’ve heard about my stomach problem, and know about the drugs I take for it, how many have lectured me about how I shouldn’t take those nasty drugs? Zero. No one has ever even made a comment about how I shouldn’t be taking medications for something that’s just uncomfortable. Even knowing that some of the stuff I take for it is addictive, no one, not one single person has ever told me that I didn’t need my medication. No one would even consider it.

But depression? It’s a very different story.…

Somewhat over 1/2 of the people who hear that I take an antidepressant express disapproval in some way. Around 1/3 make snide comments about “happy pills” and lecture me about how only weak-willed nebbishes who can’t deal with reality need psychiatric medication.

I confess to being thoroughly mystified by this. Why is it OK for my stomach, or my heart, or my pancreas to be ill in a way that needs to be treated with medication, but it’s not OK for my brain? Why are illnesses that originate in this one organ so different from all others, so that so many people believe that nothing can possibly go wrong with it? That there are absolutely no problems with the brain that can possibly be treated by medication?

Why is it OK for me to take expensive, addictive drugs for a painful but non-life-threatening problem with my stomach; but totally unacceptable for me to take cheap harmless drugs for a painful but non-threatening problem with my brain?

If we can accept that our brains are fallible, like everything else, and that this isn’t somehow immoral, we can build a better world.

June 15, 2009

Ignoring Just One Deprecated Warning

Switching projects over to iPhone OS 3.0 means discovering that functions I’m using are deprecated. Occasionally there isn’t a totally straightforward replacement, and the best thing to do is to file a bug/TODO/note for myself, and ignore the warning until a later version, when major refactoring will be possible. But bitter experience has taught me to have Xcode treat warnings as errors1, so it’s necessary to trick the compiler into ignoring the warning for things to build.

-Wno-deprecated

The -Wno-deprecated flag tells GCC to suppress warnings about deprecated code. But adding it to an Xcode project means you won’t get useful warnings about other depreciated code.

You could file a bug telling yourself to turn that warning back on after the deprecated functionality has been updated. That should work just fine. But it feels like bad project hygiene to me.

Casting and Protocols

Type casting is a dangerous old-C technique that’s earned its infamy. But it’s undeniably fitting to use a deprecated language feature to get deprecated code to build. The basic idea is to declare a protocol that includes the method you want to suppress warnings for,

@protocol DeprecatedHack
- (void) myDeprecatedMethod;
@end

then just cast your objects so the compiler thinks they implement the protocol,

[foo myDeprecatedMethod]; //warnings
[(id<DeprecatedHack>>)foo myDeprecatedMethod]; //no warnings

Although having to declare a protocol is somewhat heavyweight, it leaves a nice artifact in the code reminding you to replace deprecated functionality.

Protocols Not Required

Sometimes just casting to id is enough. This happens if another object has a non-deprecated method with the same name.

1For experimental or prototyping projects I let warnings slide. But in the main project I always treat warnings as errors. Ignoring them in production code has never worked — warnings fester and grow on each other.

Because Objective-C is so dynamic, there are many errors that the compiler can warn you about, but can’t be totally sure are errors. For example, methods can be added to a class at runtime, so if you call -someMethodThatDoesNotExistAnywhere, the compiler will warn you that something is up, but won’t stop the build, because the necessary code could magically appear at runtime. Of course, 99% of the time, it’s me accidentally using count when I meant length, etc. What I’m really trying to say here is that treating warnings as errors is an even better idea in Objective-C.

May 1, 2009

NSXMLParser and HTML/XHTML

Filed under: Bug Bite,iPhone,Objective-C,Programming | , , , ,
― Vincent Gable on May 1, 2009

NSXMLParser converts HTML/XML-entities in the string it gives the delegate callback -(void)parser:(NSXMLParser *)parser foundCharacters:(NSString *)string. So if an XML file contains the string, "&lt; or &gt;", the converted string "< or >" would be reported to the delegate, not the string that you would see if you opened the file with TextEdit.

This is correct behavior for XML files, but it can cause problems if you are trying to use an NSXMLParser to monkey with XHTML/HTML.

I was using an NSXMLParser to modify an XHTML webpage from Simple Wikipedia, and it was turning: “#include &lt;stdio&gt;” into “#include <stdio>“, which then displayed as “#include “, because WebKit thought <stdio> was a tag.

Solution: Better Tools

For scraping/reading a webpage, XPath is the best choice. It is faster and less memory intensive then NSXMLParser, and very concise. My experience with it has been positive.

For modifying a webpage, JavaScript might be a better fit then Objective-C. You can use
- (NSString *)stringByEvaluatingJavaScriptFromString:(NSString *)script to execute JavaScript inside a UIWebView in any Cocoa program. Neat stuff!

My Unsatisfying Solution

Do not use this, see why below:

- (void)parser:(NSXMLParser *)parser foundCharacters:(NSString *)string;
{
	string = [string stringByReplacingOccurrencesOfString:@"<" withString:@"&lt;"];
	string = [string stringByReplacingOccurrencesOfString:@">" withString:@"&gt;"];

	/* ... rest of the method */
}

Frankly that code scares me. I worry I’m not escaping something I should be. Experience has taught me I don’t have the experience of the teams who wrote HTML libraries, so it’s dangerous to try and recreate their work.

(UPDATED 2009-05-26: And indeed, I screwed up. I was replacing & with &amp;, and that was causing trouble. While my “fix” of not converting & seems to work on one website, it will not in general.)

I would like to experiment with using JavaScript instead of an NSXMLParser, but at the moment I have a working (and surprisingly compact) NSXMLParser implementation, and much less familiarity with JavaScript then Objective-C. And compiled Obj-C code should be more performant then JavaScript. So I’m sticking with what I have, at least until I’ve gotten Prometheus 1.0 out the door.

April 22, 2009

-[NSURL isEqual:] Gotcha

Filed under: Bug Bite,Cocoa,iPhone,MacOSX,Programming,Sample Code | , , , , , ,
― Vincent Gable on April 22, 2009

BREAKING UPDATE: Actually comparing the -absoluteURL or -absoluteString of two NSURLs that represent a file is not good enough. One may start file:///, and the other file://localhost/, and they will not be isEqual:! A work around is to compare the path of each NSURL. I’m still looking into the issue, but for now I am using the following method to compare NSURLs.

@implementation NSURL (IsEqualTesting)
- (BOOL) isEqualToURL:(NSURL*)otherURL;
{
	return [[self absoluteURL] isEqual:[otherURL absoluteURL]] || 
	[self isFileURL] && [otherURL isFileURL] &&
	([[self path] isEqual:[otherURL path]]);
}
@end

[a isEqual:b] may report NO for two NSURLs that both resolve to the same resource (website, file, whatever). So compare NSURLs like [[a absoluteString] isEqual:[b absoluteString]]. It’s important to be aware of this gotcha, because URLs are Apple’s preferred way to represent file paths, and APIs are starting to require them. Equality tests that worked for NSString file-paths may fail with NSURL file-paths.

The official documentation says

two NSURLs are considered equal if they both have the same base baseURL and relativeString.

Furthermore,

An NSURL object is composed of two parts—a potentially nil base URL and a string that is resolved relative to the base URL. An NSURL object whose string is fully resolved without a base is considered absolute; all others are considered relative.

In other words, two NSURL objects can resolve to the same absolute URL, but have a different base URL, and be considered !isEqual:.

An example should make this all clear,

NSURL *VGableDotCom = [NSURL URLWithString:@"http://vgable.com"];
NSURL *a = [[NSURL alloc] initWithString:@"blog" relativeToURL:VGableDotCom];
NSURL *b = [[NSURL alloc] initWithString:@"http://vgable.com/blog" relativeToURL:nil];
LOG_INT([a isEqual:b]);
LOG_INT([[a absoluteURL] isEqual:[b absoluteURL]]);
LOG_ID([a absoluteURL]);
LOG_ID([b absoluteURL]);

[a isEqual:b] = 0
[[a absoluteURL] isEqual:[b absoluteURL]] = 1
[a absoluteURL] = http://vgable.com/blog
[b absoluteURL] = http://vgable.com/blog

Remember that collections use isEqual: to determine equality, so you may have to convert an NSURL to an absoluteURL to get the behavior you expect, especially with NSSet and NSDictionary.

April 19, 2009

Beware rangeOf NSString Operations

Filed under: Bug Bite,iPhone,Objective-C,Programming,Sample Code | , , , ,
― Vincent Gable on April 19, 2009

I have repeatedly had trouble with the rageOfNSString methods, because they return a struct. Going forward I will do more to avoid them, here are some ways I plan to do it.

Sending a message that returns a struct to nil can “return” undefined values. With small structs like NSRange, you are more likely to get {0} on Intel, compared to PowerPC and iPhone/ARM. Unfortunately, this makes nil-messaging bugs hard to detect. In my experience you will miss them when running on the simulator, even if they are 100% reproducible on an actual iPhone.

This category method has helped me avoid using -rangeOfString: dangerously,

@implementation NSString  (HasSubstring)
- (BOOL) hasSubstring:(NSString*)substring;
{
	if(IsEmpty(substring))
		return NO;
	NSRange substringRange = [self rangeOfString:substring];
	return substringRange.location != NSNotFound && substringRange.length > 0;
}
@end

I choose to define [aString hasSubstring:@""] as NO. You might prefer to throw an exception, or differentiate between @"" and nil. But I don’t think a nil string is enough error to throw an exception. And even though technically any string contains the empty string, I generally treat @"" as semantically equivalent to nil.

As I’ve said before,

A few simple guidelines can help you avoid my misfortune:

  • Be especially careful using of any objective-C method that returns a double, struct, or long long
  • Don’t write methods that return a double, struct, orlong long. Return an object instead of a struct; an NSNumber* or float instead of a double or long long. If you must return a dangerous data type, then see if you can avoid it. There really isn’t a good reason to return a struct, except for efficiency. And when micro-optimizations like that matter, it makes more sense to write that procedure in straight-C, which avoids the overhead of Objective-C message-passing, and solves the undefined-return-value problem.
  • But if you absolutely must return a dangerous data type, then return it in a parameter. That way you can give it a default value of your choice, and won’t have undefined values if an object is unexpectedly nil.
    Bad:
    - (struct CStructure) evaluateThings;
    Good:
    - (void) resultOfEvaluatingThings:(struct CStructure*)result;.

It’s not a bad idea to wrap up all the rangeOf methods in functions or categories that play safer with nil.

Thanks to Greg Parker for corrections!

April 10, 2009

Percent Escapes Gotcha

Filed under: Bug Bite,Cocoa,Programming,Sample Code | ,
― Vincent Gable on April 10, 2009

If you use stringByAddingPercentEscapesUsingEncoding: more than once on a string, the resulting string will not decode correctly from just one call to stringByReplacingPercentEscapesUsingEncoding:. (stringByAddingPercentEscapesUsingEncoding: is not indempotent).

NSString *string = @"100%";
NSString *escapedOnce = [string stringByAddingPercentEscapesUsingEncoding:NSUTF8StringEncoding];
NSString *escapedTwice = [escapedOnce stringByAddingPercentEscapesUsingEncoding:NSUTF8StringEncoding];
NSLog(@"%@ escaped once: %@, escaped twice: %@", string, escapedOnce, escapedTwice);

100% escaped once: 100%25, escaped twice: 100%2525

I thought I was programming defensively by eagerly adding percent-escapes to any string that would become part of a URL. But this caused some annoying bugs resulting form a string being percent-escaped more then once. My solution was to create an indempotent replacement for stringByAddingPercentEscapesUsingEncoding: (I also simplified things a little by removing the encoding parameter, because I never used any encoding other then NSUTF8StringEncoding),

@implementation NSString (IndempotentPercentEscapes)
- (NSString*) stringByReplacingPercentEscapesOnce;
{
	NSString *unescaped = [self stringByReplacingPercentEscapesUsingEncoding:NSUTF8StringEncoding];
	//self may be a string that looks like an invalidly escaped string,
	//eg @"100%", in that case it clearly wasn't escaped,
	//so we return it as our unescaped string.
	return unescaped ? unescaped : self;
}

- (NSString*) stringByAddingPercentEscapesOnce;
{
	return [[self stringByReplacingPercentEscapesOnce] stringByAddingPercentEscapesUsingEncoding:NSUTF8StringEncoding];
}
@end

Usage example,

NSString *string = @"100%";
NSString *escapedOnce = [string stringByAddingPercentEscapesOnce];
NSString *escapedTwice = [escapedOnce stringByAddingPercentEscapesOnce];
NSLog(@"%@ escaped once: %@, escaped twice: %@", string, escapedOnce, escapedTwice);

100% escaped once: 100%25, escaped twice: 100%25

The paranoid have probably noticed that [aBadlyEncodedString stringByReplacingPercentEscapesOnce] will return aBadlyEncodedString not nil, This could make it harder to detect an error.

But it’s not something that I’m worried about for my application. Since I only ever use a UTF8 encoding, and it can represent any unicode character, it’s not possible to have an invalid string. But it’s certainly something to be aware of in situations where you might have strings with different encodings.

March 29, 2009

How To Write Cocoa Object Setters

There are several ways to write setters for Objective-C/Cocoa objects that work. But here are the practices I follow; to the best of my knowledge they produce the safest code.

Principle 0: Don’t Write a Setter

When possible, it’s best to write immutable objects. Generally they are safer, and easier to optimize, especially when it comes to concurrency.

By definition immutable objects have no setters, so always ask yourself if you really need a setter, before you write one, and whenever revisiting code.

I’ve removed many of my setters by making the thing they set an argument to the class’s -initWith: constructor. For example,

CustomWidget *widget = [[CustomWidget alloc] init];
[widget setController:self];

becomes,

CustomWidget *widget = [[CustomWidget alloc] initWithController:self];

This is less code, and now, widget is never in a partially-ready state with no controller.

It’s not always practical to do without setters. If an object looks like it needs a settable property, it probably does. But in my experience, questioning the assumption that a property needs to be changeable pays off consistently, if infrequently.

Principle 1: Use @synthesize

This should go without saying, but as long as I’m enumerating best-practices: if you are using Objective-C 2.0 (iPhone or Mac OS X 10.5 & up) you should use @synthesize-ed properties to implement your setters.

The obvious benefits are less code, and setters that are guaranteed to work by the compiler. A less obvious benefit is a clean, abstracted way to expose the state values of an object. Also, using properties can simplify you dealloc method.

But watch out for the a gotcha if you are using copy-assignment for an NSMutable object!

(Note: Some Cocoa programmers strongly dislike the dot-syntax that was introduced with properties and lets you say x.foo = 3; instead of [x setFoo:3];. But, you can use properties without using the dot-syntax. For the record, I think the dot syntax is an improvement. But don’t let a hatred of it it keep you from using properties.)

Principle 2: Prefer copy over retain

I covered this in detail here. In summary, use copy over retain whenever possible: copy is safer, and with most basic Foundation objects, copy is just as fast and efficient as retain.

The Preferred Pattern

When properties are unavailable, this is my “go-to” pattern:

- (void) setX:(TypeOfX*)newX;
{
  [memberVariableThatHoldsX autorelease];
  memberVariableThatHoldsX = [newX copy];
}

Sometimes I use use retain, or very rarely mutableCopy, instead of copy. But if autorelease won’t work, then I use a different pattern. I have a few reasons for writing setters this way.

Reason: Less Code

This pattern is only two lines of code, and has no conditionals. There is very little that can I can screw up when writing it. It always does the same thing, which simplifies debugging.

Reason: autorelease Defers Destruction

Using autorelease instead of release is just a little bit safer, because it does not immediately destroy the old value.

If the old value is immediately released in the setter then this code will sometimes crash,

NSString* oldName = [x name];
[x setName:@"Alice"];
NSLog(@"%@ has changed their name to Alice", oldName);

If -setName: immediately releasees the value that -name returned, oldName will be invalid when it’s used in NSLog.

But if If -setName: autorelease-ed the old value instead, this wouldn’t be a problem; oldName would still be valid until the current autorelease pool was drained.

Reason: Precedent

This is the pattern that google recommends.

When assigning a new object to a variable, one must first release the old object to avoid a memory leak. There are several “correct” ways to handle this. We’ve chosen the “autorelease then retain” approach because it’s less prone to error. Be aware in tight loops it can fill up the autorelease pool, and may be slightly less efficient, but we feel the tradeoffs are acceptable.

- (void)setFoo:(GMFoo *)aFoo {
  [foo_ autorelease];  // Won't dealloc if |foo_| == |aFoo|
  foo_ = [aFoo retain]; 
}

Backup Pattern (No autorelease)

When autorelease won’t work, my Plan-B is:

- (void) setX:(TypeOfX*)newX;
{
  id old = memberVariableThatHoldsX;
  memberVariableThatHoldsX = [newX copy];
  [old release];
}

Reason: Simple

Again, there are no conditionals in this pattern. There’s no if(oldX != newX) test for me to screw up. (Yes, I have done this. It wasn’t a hard bug to discover and fix, but it was a bug nonetheless.) When I’m debugging a problem, I know exactly what setX: did to it’s inputs, without having to know what they are.

On id old

I like naming my temporary old-value id old, because that name & type always works, and it’s short. It’s less to type, and less to think about than TypeOfX* oldX.

But I don’t think it’s necessarily the best choice for doing more to old than sending it release.

To be honest I’m still evaluating that naming practice. But so far I’ve been happy with it.

Principle 3: Only Optimize After You Measure

This is an old maxim of Computer Science, but it bears repeating.

The most common pattern for a setter feels like premature optimization:

- (void) setX:(TypeOfX*)newX;
{
  if(newX != memberVariableThatHoldsX){
    [memberVariableThatHoldsX release];
    memberVariableThatHoldsX = [newX copy];
  }
}

Testing if(newX != memberVariableThatHoldsX) can avoid an expensive copy.

But it also slows every call to setX:. if statements are more code, that takes time to execute. When the processor guesses wrong while loading instructions after the branch, if‘s become quite expensive.

To know what way is faster, you have to measure real-world conditions. Even if a copy is very slow, the conditional approach isn’t necessarily faster, unless there is code that sets a property to it’s current value. Which is kind of silly really. How often do you write code like,

[a setX:x1];
[a setX:x1]; //just to be sure!

or

[a setX:[a x]];

Does that look like code you want to optimize? (Trick question! You don’t know until you test.)

Hypocrisy!

I constantly break Principle 3 by declaring properties in iPhone code as nonatomic, since it’s the pattern Apple uses in their libraries. I assume Apple has good reason for it; and since I will need to write synchronization-code to safely use their libraries, I figure it’s not much more work to reuse the same code to protect access to my objects.

I can’t shake the feeling I’m wrong to do this. But it seems more wrong to not follow Apple’s example; they wrote the iPhone OS in the first place.

If you know a better best practice, say so!

There isn’t a way to write a setter that works optimally all the time, but there is a setter-pattern that works optimally more often then other patterns. With your help I can find it.

UPDATE 03-30-2009:

Wil Shiply disagrees. Essentially his argument is that setters are called a lot, so if they aren’t aggressive about freeing memory, you can have thousands of dead objects rotting in an autorelease pool. Plus, setters often do things like registering with the undo manager, and that’s expensive, so it’s a good idea to have conditional code that only does that when necessary.

My rebuttal is that you should optimize big programs by draining autorelease pools early anyway; and that mitigates the dead-object problem.

With complex setters I can see why it makes sense to check if you need to do something before doing it. I still prefer safer, unconditional, code as a simple first implementation. That’s why it’s my go-to pattern. But if most setters you write end up being more complex, it might be the wrong pattern.

Really you should read what Wil says, and decide for yourself. He’s got much more experience with Objective-C development then I do.

March 17, 2009

Mutable @property and Copy Gotcha

Filed under: Bug Bite,Cocoa,Objective-C,Programming | , , ,
― Vincent Gable on March 17, 2009

If you have a @property for an object who’s name starts with NSMutable, and you use the copy declaration attribute, then the code that is synthesized for you is not correct. Because it uses copy, not mutableCopy, to do the copy during assignment, the value will not be mutable.

Here’s a demonstration,

@interface Gotcha : NSObject {
	NSMutableString *mutableString;
}
@property (copy) NSMutableString *mutableString;
@end
@implementation Gotcha
@synthesize mutableString;
@end

... Gotcha *x = [[[Gotcha alloc] init] autorelease]; x.mutableString = [NSMutableString stringWithString:@"I am mutable."]; [x.mutableString appendString:@" Look at me change."];

It crashes with the message,

*** Terminating app due to uncaught exception ‘NSInvalidArgumentException’, reason: ‘Attempt to mutate immutable object with appendString:’

copy vs mutableCopy

The copy method returns an immutable copy of an object, even if the object is mutable. Eg the copy of an NSMutableString is an immutable NSString.

The mutableCopy method returns a mutable copy of an object, even if the object is not mutable. Eg., the mutableCopy of an NSString is an NSMutableString object.

@synthesize always uses copy in the setters it generates. That means an immutable copy will always be stored, even if an NSMutable object is given.

Workarounds

Apple says, ” …In this situation, you have to provide your own implementation of the setter method..” (To me, this isn’t as satisfying of an answer as a mutablecopy attribute. But it’s what Apple has chosen, so all we can do is file enhancement requests and trust their reasoning).

Another workaround is to make any property that is a mutable value readonly. If something is mutable, you can change it’s contents directly, so there is rarely a need to set it. For example, with the Gotcha object, it’s just as easy to say

[x.mutableString setString:@"New String"];

as

x.mutableString = [NSMutableString stringWithString:@"New String"];

Finally, you might consider using an immutable readwrite property. In my experience, immutable objects are generally safer, and often faster (I owe you an article on this, but there are plenty of articles on the benefits of immutability). I know this sounds like a smart-ass suggestion, and it won’t be a good answer much of the time. But it’s surprising how often things turn out not to need mutability once a design matures. Since reducing the mutability of an object often improves it, and it does work around the problem, it’s worth at least considering this solution.

Why not Just Use retain?

Changing the copy to retain well let you assign a mutable object, but I do not like the idea. My article, Prefer copy Over retain explains in detail. My biggest worry is aliasing,

NSMutableString *aLocalString = [NSMutableString string];
x.mutableString = aLocalString;
[x doSomething];
//aLocalString might have been changed by doSomething!
//
// 300 lines later...
//
[aLocalString appendString:@" now with more bugs!"];
//x has been changed too!

Using retain with NSMutable objects is asking for bugs.

« Newer PostsOlder Posts »

Powered by WordPress