mikeash.com pyblog/friday-qa-2010-05-14-what-every-apple-programmer-should-know.html commentshttp://www.mikeash.com/?page=pyblog/friday-qa-2010-05-14-what-every-apple-programmer-should-know.html#commentsmikeash.com Recent CommentsFri, 29 Mar 2024 07:42:09 GMTPyRSS2Gen-1.0.0http://blogs.law.harvard.edu/tech/rssmikeash - 2013-03-03 20:12:13http://www.mikeash.com/?page=pyblog/friday-qa-2010-05-14-what-every-apple-programmer-should-know.html#commentsI mean that when invoking a class method <i>from an instance method</i>, you should do that. <br /><code> <br />[MyClass myClassMethod]; // bad, breaks subclasses <br />[[self class] myClassMethod]; // good <br /></code>9a087d8771e8ad0672706771e9184814Sun, 03 Mar 2013 20:12:13 GMTAlex - 2013-03-02 04:41:34http://www.mikeash.com/?page=pyblog/friday-qa-2010-05-14-what-every-apple-programmer-should-know.html#commentsWhat exactly do you mean in your paragraph, "Always use [self class] when invoking your own class methods?" <br /> <br />Let's say I have a class MyClass with a public class method myClassMethod. Now, it sounds like you are saying to call [self MyClass] every time I call [self myClassMethod], but I don't think I understand this. Calling [self myClassMethod] should be sufficient to utilize any override of myClassMethod written in a subclass, right?e2b3df1147ece585b060175a24298172Sat, 02 Mar 2013 04:41:34 GMTmikeash - 2010-05-27 14:33:36http://www.mikeash.com/?page=pyblog/friday-qa-2010-05-14-what-every-apple-programmer-should-know.html#commentsIt is sometimes documented, like with <code>-waitUntilExit</code>. But you're right that this is absolutely an antipattern, and even worse with things like <code>-isRunning</code> where it doesn't even say that you do it. <br /> <br />Always run your sub-runloops in a custom mode! Definitely good advice to follow.7ea70528b0fa0bb0c64fba9dabf55613Thu, 27 May 2010 14:33:36 GMTDave Keck - 2010-05-27 09:05:06http://www.mikeash.com/?page=pyblog/friday-qa-2010-05-14-what-every-apple-programmer-should-know.html#commentsI've been meaning to bring this up on Cocoa-dev, but this seems like a good place: <br /> <br />A problem I often run into is code that runs the run loop recursively in the default mode. For example NSTask's isRunning property does this (on 10.5 at least, I haven't checked on 10.6 since I rolled my own NSTask), and of course this behavior has side-effects that you would expect: notifications being sent, timers firing, etc, all from within the -isRunning stack frame! Such side-effects can be fatal in some circumstances, if the code calling -isRunning is setting up state that the said notifications/timers/etc. rely on. I quickly learned to only run the run loop recursively in a custom mode; unfortunately Apple's frameworks don't obey this rule, and not once have I seen it documented when the frameworks do this.22ceb5cda0804ee61ff0a01fe508bcb1Thu, 27 May 2010 09:05:06 GMTMichael Crawford - 2010-05-20 15:46:58http://www.mikeash.com/?page=pyblog/friday-qa-2010-05-14-what-every-apple-programmer-should-know.html#commentsmikeash said:<div class="blogcommentquote"><div class="blogcommentquoteinner">I get that filing bugs is useful to Apple, but as far as I've seen it's frequently not worth the time for me. If Apple benefits from my time spent filing bugs, and they want me to file more bugs, maybe they should start paying for my time.</div></div> <br /> <br />I say: Maybe Apple should take it upon themselves to make filing bugs easier, more convenient, and less time consuming than it currently is today. There has got to be a way to do this and they would then get the feedback that they seem to be asking for. <br /> <br />It might also be nice to see how many duplicates there are in order to get an idea about how our reports are affecting the priority of an issue; sort of like Digg (if I correctly understand the meaning of the term 'digg').3801c17568f08e9161697c6de074ffbbThu, 20 May 2010 15:46:58 GMTLouis Gerbarg - 2010-05-15 22:11:57http://www.mikeash.com/?page=pyblog/friday-qa-2010-05-14-what-every-apple-programmer-should-know.html#commentsHeh, I absolutely agree that 99% of the time you should go through an accessor, and then whenever you don't you should actually have a justified reason for it that you can actually state (and probably put in a comment right there). Apple's lax use of accessors is very unfortunate, but somewhat understandable given how old the code is. <br /> <br />What I generally do in a case like this (but did not because it would have made the example more complicated) is make all the accessors thin wrappers that funnel through a single common function that updates all of the dependent properties together. That places all the direct manipulation in one easily auditable location, and also provides a method that subclasses can sensibly override that conveys the semantic relation (imagine all the above setters just called something like -[Person setName:nameType:] that handled all the special behavior and directly manipulated the appropriate ivars). b6ece71b49dc9deef6b31f24912db8b7Sat, 15 May 2010 22:11:57 GMTmikeash - 2010-05-15 14:09:13http://www.mikeash.com/?page=pyblog/friday-qa-2010-05-14-what-every-apple-programmer-should-know.html#commentsWell, I think you can have a notion of primitive setters and derived setters. In this particular example, I'd expect the first/last name setters to be called from the full name setter, but not vice versa. Of course, which one is primitive and which one is derived is something of an implementation detail, so this is not entirely straightforward. <br /> <br />For a complex case like this, I could see bending the rule a bit and accessing things directly. Where it really grates is when no such complexity exists, it's just a simple value, but the code accesses the ivar directly and bypasses the accessor for no reason. For example, there are many NSWindow properties which you can't override because NSWindow will bypass the getter.45206a50c409f60b49aaae32765fbd53Sat, 15 May 2010 14:09:13 GMTLouis Gerbarg - 2010-05-15 05:10:15http://www.mikeash.com/?page=pyblog/friday-qa-2010-05-14-what-every-apple-programmer-should-know.html#commentsIn the above example it isn't just about KVO. Like I said in the description, if you use the setters in other setters they will cross recur with each other and stack overflow. IOW: <br /> <br />setFirstName: calls setFullName: calls setFirstName:.... <br /> <br />Avoiding that requires the setters to have additional state hidden somewhere which is just as likely to blow up when subclassing. If you are changing the interaction semantics of dependent properties you will need to follow certain rules in subclass in order to guarantee the invariants the superclass depends on. <br /> <br />In my opinion it is better to just figure out some way to structure appropriate asserts for those invariants in your code (so the mistakes blow up in a spectacular and obvious manner), and document the constraints for subclassing.fb85ae13c197be58a4b3393edf39cb03Sat, 15 May 2010 05:10:15 GMTmikeash - 2010-05-15 04:11:11http://www.mikeash.com/?page=pyblog/friday-qa-2010-05-14-what-every-apple-programmer-should-know.html#comments<b>Louis Gerbarg:</b> Yeah, that's an example of what I'm talking about. Directly setting the first and last names without using the setter is a big no-no in my view. If you override one of those setters, it'll be called inconsistently, which could easily lead to a crash or data corruption despite perfectly reasonable code in the subclass. For something like this, I say you should either manually manage your KVO notifications, or (my preferred solution) just not use KVO. <br /> <br /><b>Trevor:</b> Since that property is readonly, @synthesize only generates a getter, but the example bypasses the non-existent setter.2d7565d98de735665dbada052c17adb4Sat, 15 May 2010 04:11:11 GMTTrevor - 2010-05-15 04:00:34http://www.mikeash.com/?page=pyblog/friday-qa-2010-05-14-what-every-apple-programmer-should-know.html#comments<div class="blogcommentquote"><div class="blogcommentquoteinner">There isn't any accessor to bypass.</div></div> <br /> <br />But the @synthesize generates an accessor, does it not? <br /> <br /> <br />0d409a0d66989cbba94521efba356d04Sat, 15 May 2010 04:00:34 GMTLouis Gerbarg - 2010-05-15 03:26:06http://www.mikeash.com/?page=pyblog/friday-qa-2010-05-14-what-every-apple-programmer-should-know.html#commentsThose NSNumbers should be NSStrings... a web browser is not a code editor ;-)25057c22d8c768e412e86543efff2588Sat, 15 May 2010 03:26:06 GMTLouis Gerbarg - 2010-05-15 03:24:32http://www.mikeash.com/?page=pyblog/friday-qa-2010-05-14-what-every-apple-programmer-should-know.html#commentsI did that because I wanted a simple example. If you want one with a full dependency situation here is one. Note that in all 3 accessors for the dependent properties they access each others ivars directly, the accessors are public, replacing the direct accesses with property accesses doesn't just cause KVOs to fire while things are in an intermediate state, it will cause a stack overfull due to co-recursion. Yes, there is still an issue with subclass if you override one of them without overriding all of them, but that is true of any properties that have semantic dependencies. <br /> <br />@interface Person : NSObject { <br />&nbsp;&nbsp;NSString *firstName; <br />&nbsp;&nbsp;NSNumber *lastName; <br />&nbsp;&nbsp;NSNumber *fullName; <br />} <br /> <br />@property (nonatomic, retain) NSString *firstName; <br />@property (nonatomic, retain) NSNumber *lastName; <br />@property (nonatomic, retain) NSNumber *fullName; <br /> <br />@end <br /> <br />@implementation <br /> <br />@synthesize firstName; <br />@synthesize lastName; <br />@synthesize fullName; <br /> <br />//Omitting sanity checking and error code because it would make things a lot longer and just obfuscate the point <br /> <br />- (void)setLastName:(NSDate *)lastName_ { <br />&nbsp;&nbsp;[lastName release]; <br />&nbsp;&nbsp;lastName = [lastName_ retain]; <br />&nbsp;&nbsp;[fullName release]; <br />&nbsp;&nbsp;fullName = [[NSString alloc] initWithFormat:@"%@ %@", firstName, lastName]; <br />} <br /> <br />- (void)setFirstName:(NSDate *)firstName_ { <br />&nbsp;&nbsp;[firstName release]; <br />&nbsp;&nbsp;firstName = [firstName_ retain]; <br />&nbsp;&nbsp;[fullName release]; <br />&nbsp;&nbsp;fullName = [[NSString alloc] initWithFormat:@"%@ %@", firstName, lastName]; <br />} <br /> <br />- (void)setFullName:(NSDate *)fullName_ { <br />&nbsp;&nbsp;[fullName_ release]; <br />&nbsp;&nbsp;fullName = [fullName_ retain]; <br />&nbsp;&nbsp;NSArray *names = [fullName componentsSeparatedByString:@" "]; <br />&nbsp;&nbsp;[firstName release]; <br />&nbsp;&nbsp;firstName = [[names objectAtIndex:0] retain]; <br />&nbsp;&nbsp;[lastName release]; <br />&nbsp;&nbsp;lastName = [[names objectAtIndex:1] retain]; <br />} <br /> <br /> <br /> <br />+ (NSSet *)keyPathsForValuesAffectingValueForKey:(NSString *)key <br />{ <br />&nbsp;&nbsp;&nbsp;&nbsp;NSSet *keyPaths = [super keyPathsForValuesAffectingValueForKey:key]; <br />&nbsp;&nbsp; <br />&nbsp;&nbsp;&nbsp;&nbsp;if ([key isEqualToString:@"firstName"]) { <br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;NSSet *affectingKeys = [NSSet setWithObjects:@"fullName", @"lastName",nil]; <br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;keyPaths = [keyPaths setByAddingObjectsFromSet:affectingKeys]; <br />&nbsp;&nbsp;&nbsp;&nbsp;} else if ([key isEqualToString:@"lastName"]) { <br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;NSSet *affectingKeys = [NSSet setWithObjects:@"fullName", @"firstName",nil]; <br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;keyPaths = [keyPaths setByAddingObjectsFromSet:affectingKeys]; <br />&nbsp;&nbsp;&nbsp;&nbsp;} else if ([key isEqualToString:@"firstName"]) { <br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;NSSet *affectingKeys = [NSSet setWithObjects:@"lastName", @"firstName",nil]; <br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;keyPaths = [keyPaths setByAddingObjectsFromSet:affectingKeys]; <br />&nbsp;&nbsp;&nbsp;&nbsp;} <br /> <br />&nbsp;&nbsp;&nbsp;&nbsp;return keyPaths; <br />} <br /> <br />@end b87d410402e22b56b639ae745949c8c7Sat, 15 May 2010 03:24:32 GMTmikeash - 2010-05-15 01:29:16http://www.mikeash.com/?page=pyblog/friday-qa-2010-05-14-what-every-apple-programmer-should-know.html#comments<b>Red Palmer:</b> Again, my own personal experience is that there is <i>not</i> a very good chance that filed bugs will eventually be fixed. No matter how much people tell me otherwise, that's not going to change that fact. Even if I just got a little feedback it might seem worthwhile, but the vast majority of my bugs disappear into a black hole and are never seen again, so I have no idea if anyone has even noticed. <br /> <br /><b>Louis Gerbarg:</b> That code doesn't bypass the accessor. There isn't any accessor to bypass. I have no problem with direct ivar access in the absence of an accessor. What I have a problem with is declaring a public accessor and then not using it in the implementation, because it's deceptive for subclassers. If the accessor isn't there in the first place, then it's fine.5891885f201b373e5a701e8314c9aafcSat, 15 May 2010 01:29:16 GMTLouis Gerbarg - 2010-05-15 01:00:42http://www.mikeash.com/?page=pyblog/friday-qa-2010-05-14-what-every-apple-programmer-should-know.html#commentsHow this is problematic to subclass: <br /> <br />@interface Person : NSObject { <br />&nbsp;&nbsp;NSDate *dob; <br />&nbsp;&nbsp;NSNumber *age; <br />} <br /> <br />@property (nonatomic, retain) NSDate *dob; <br />@property (nonatomic, retain, readonly) NSNumber *age; <br /> <br />@end <br /> <br />@implementation <br /> <br />@synthesize dob; <br />@synthesize age; <br /> <br />- (void)setDob:(NSDate *)date_ { <br />&nbsp;&nbsp;[date release]; <br />&nbsp;&nbsp;date = [date_ retain]; <br />&nbsp;&nbsp;age = [self calculateAge]; <br />} <br /> <br />+ (NSSet *)keyPathsForValuesAffectingValueForKey:(NSString *)key <br />{ <br />&nbsp;&nbsp;&nbsp;&nbsp;NSSet *keyPaths = [super keyPathsForValuesAffectingValueForKey:key]; <br />&nbsp; <br />&nbsp;&nbsp;&nbsp;&nbsp;if ([key isEqualToString:@"age"]) <br />&nbsp;&nbsp;&nbsp;&nbsp;{ <br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;NSSet *affectingKeys = [NSSet setWithObjects:@"dob", @"firstName",nil]; <br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;keyPaths = [keyPaths setByAddingObjectsFromSet:affectingKeys]; <br />&nbsp;&nbsp;&nbsp;&nbsp;} <br />&nbsp;&nbsp;&nbsp;&nbsp;return keyPaths; <br />} <br /> <br />@end <br /> <br />Admittedly, if you want to implement an age setter in the subclass things get hairy, but that would likely be the case even if you used a private setter for age in the above implementation since the Person is likely to have semantic dependencies on age and dob being related, so the only clean way to subclass would be to override all the declared entry points anyway.a610ee2bf0bffe10dc4d2b24fad22b08Sat, 15 May 2010 01:00:42 GMTRed Palmer - 2010-05-15 00:57:09http://www.mikeash.com/?page=pyblog/friday-qa-2010-05-14-what-every-apple-programmer-should-know.html#commentsRe: isolation. I talked to my buddy who works at Apple, and he told me that lots of engineers were indies before joining Apple. They just don't have the ability to do anything about the bug reporting situation. All developer communication is controlled by the developer technical support division, and engineers working on the products don't have any sway with them. <br /> <br />If you file bugs, there's a good chance the bugs will get fixed eventually. If not, there's a possibility Apple doesn't even know about the bugs.7a8d3fd130a33f8b4d68377c2bdcf345Sat, 15 May 2010 00:57:09 GMTmikeash - 2010-05-14 22:45:47http://www.mikeash.com/?page=pyblog/friday-qa-2010-05-14-what-every-apple-programmer-should-know.html#commentsAvoiding an accessor just because you don't want immediate KVO sounds really dangerous. If the subclass overrides the accessor, then you'll be calling it in an inconsistent manner that the subclass won't expect. Danger lies down that road. <br /> <br />I think a better route would be to add a KVO flag checked by your accessor, and decline automatic KVO, so you can have control if you need it. <br /> <br />Or just don't use KVO. I've come across few situations where it's worth struggling with.859f8889c8016d047124a8cb4a2649c5Fri, 14 May 2010 22:45:47 GMTLouis Gerbarg - 2010-05-14 21:35:32http://www.mikeash.com/?page=pyblog/friday-qa-2010-05-14-what-every-apple-programmer-should-know.html#commentsDamn iPad sutocorrected accessor to accessory ;-)ff78ffc164ef545f4e78d3f3c32eeec1Fri, 14 May 2010 21:35:32 GMTLouis Gerbarg - 2010-05-14 21:33:20http://www.mikeash.com/?page=pyblog/friday-qa-2010-05-14-what-every-apple-programmer-should-know.html#commentsExcellent article, though I have one slight disagreement. You should almost always use accessors, but there are times when it is appropriate not to. In particular, bypassing an accessory is appropriate in the exact same situations where you would use a primitive accessor in CoreData: When you want to make a modification without immediately triggers KVO. The most common case for that is when two properties need to modified atomically relative to observers. 6d03e75ebcf9ef2f10cd2ef813615e31Fri, 14 May 2010 21:33:20 GMTmikeash - 2010-05-14 20:06:08http://www.mikeash.com/?page=pyblog/friday-qa-2010-05-14-what-every-apple-programmer-should-know.html#commentsI meant to expand the bug reporting thing a bit, but forgot. <br /> <br />I don't think Apple engineers are lying to me or anything like that. They're just a bit isolated. Most Apple engineers I've talked with don't really know what it's like to be a small third-party developer. It's not their fault, that's just not an environment they're familiar with. Apple engineers largely don't realize that our access to Radar is greatly limited compared to their own, and don't know what it's like to have every bug come back as Duplicate and then never be able to hear anything about it again. And this is a large part of why I don't file many bugs, even though Apple engineers say it's the way to get things done.d836502a17d50c8f1266c85c1806051fFri, 14 May 2010 20:06:08 GMTmikeash - 2010-05-14 19:05:28http://www.mikeash.com/?page=pyblog/friday-qa-2010-05-14-what-every-apple-programmer-should-know.html#commentsI've had tons of Apple engineers tell me that filing bugs is helpful too. It's an ongoing mantra with pretty much everyone at @apple.com: "file bugs, file bugs, file bugs!" <br /> <br />The thing is, it conflicts with the results I've seen, and when there's a conflict between what someone says and what I actually see, I go with what I see. <br /> <br />Even major bugs I've filed like NSOperationQueue have gotten a brush-off and no traction until I started raising a ruckus through other means. For inconsequential bugs, odds are extremely high that nothing will happen. Even if it does, I already put in the effort to work around it, and there's often no benefit, to me, to having a fix once that's done. <br /> <br />I get that filing bugs is useful <i>to Apple</i>, but as far as I've seen it's frequently not worth the time for me. If Apple benefits from my time spent filing bugs, and they want me to file more bugs, maybe they should start paying for my time.f9a468c6fb41dc3dbd25330bc0636dc5Fri, 14 May 2010 19:05:28 GMTMark Munz - 2010-05-14 18:43:15http://www.mikeash.com/?page=pyblog/friday-qa-2010-05-14-what-every-apple-programmer-should-know.html#commentsI have to disagree with you on whether filing a bug is worthwhile. I've talked with many Apple engineers that say having a collection of bugs against a particular area does help them. <br /> <br />Now, if you turn out to be the only one that files a bug on an API, it may not get the traction you like. But if 20, or 30 (or a few hundred) developers are all pushing back on a particular API, I think it helps bring the issue to Apple's attention. <br /> <br />They may still not fix it in a timeframe that is useful to you or me (they may have different priorities), but I think it is worthwhile. They also may solve the problem in a different way (I know I sometimes do this with my own customer bug reports). <br /> <br />I also file new bugs for any major issue that was not addressed with each major release. I think with the help of twitter/blogs, you can get other developers to help raise Apple's attention to their specific needs. <br /> <br />da3315683ea04b4de0e78dcadda0483dFri, 14 May 2010 18:43:15 GMTJim Dovey - 2010-05-14 18:19:45http://www.mikeash.com/?page=pyblog/friday-qa-2010-05-14-what-every-apple-programmer-should-know.html#commentsIf I get this job at Apple, I will make it my <i>life's aim</i> to fix all these little niggly things. Even if I'm not on the team. I will grab the code, fix it, and send patches in daily, widening the recipient list of my email every day, until it's accepted. <br /> <br />First such order of business: make NSXMLParser read 4KB-chunks from a stream, so it's actually usable on serious data on the iPhone. All of about 5 lines to change, 20 to add, all internal. *sigh*2dfa70ee7728816634e08a02fc4102b1Fri, 14 May 2010 18:19:45 GMTnatemartinsf - 2010-05-14 18:19:42http://www.mikeash.com/?page=pyblog/friday-qa-2010-05-14-what-every-apple-programmer-should-know.html#commentsre: accessing instance variables. <br /> <br />I thought I had read somewhere that if you're using automatically generated accesors, you should not use them inside init:(even if you use them everywhere else) <br /> <br />Do you directly access instance variables in your init method, or not even there?922a07cf6213f2689f167566d5c60fc3Fri, 14 May 2010 18:19:42 GMTAndy Lee - 2010-05-14 18:09:59http://www.mikeash.com/?page=pyblog/friday-qa-2010-05-14-what-every-apple-programmer-should-know.html#commentsNow that you're slowing down, maybe I have a prayer of catching up on my long, long backlog of unread NSBlog articles.f29cb8d5414527c22cf1385bb8a490ffFri, 14 May 2010 18:09:59 GMTmikeash - 2010-05-14 17:15:01http://www.mikeash.com/?page=pyblog/friday-qa-2010-05-14-what-every-apple-programmer-should-know.html#commentsFiling a bug is only slightly better than sacrificing a chicken when it comes to fixing these problems. Apple barely manages to fix serious crash and data loss bugs, minor API troubles are far down on the priority list. For example, the NSStream thing has been known for years by Apple engineers, and yet it's still there. <br /> <br />When I come across a problem in the APIs, I look at the tradeoff between the time and effort it takes to file a decent bug, the benefit to me if it results in action, and the probability of a fix. Most of the time, the time and effort to file a bug isn't worth it. For most little stuff, if you're <i>lucky</i>, it gets fixed years later and with no notice. Usually it's just ignored.3397b954867df952700c8d205e5d7d04Fri, 14 May 2010 17:15:01 GMTRick Schaut - 2010-05-14 17:14:13http://www.mikeash.com/?page=pyblog/friday-qa-2010-05-14-what-every-apple-programmer-should-know.html#commentsRather than using casts on toll-free bridged types, we use language-independent typedefs: <br /> <br /><code>#ifdef __OBJC__ <br />typedef NSData TFBDataRef; <br />#else <br />typedef CFDataRef TFBDataRef; <br />#endif</code> <br /> <br />The C/C++ typedef for id is a bit tricky, but the actual C-language underlying typedef for an id is "struct objc_object". <br /> <br />Overall, this works rather nicely when bridging between Obj-C code and C/C++ code, and avoids the need for casts.c45eb34b261844ab5fff2124fbc4265aFri, 14 May 2010 17:14:13 GMTJeff Johnson - 2010-05-14 16:44:13http://www.mikeash.com/?page=pyblog/friday-qa-2010-05-14-what-every-apple-programmer-should-know.html#commentsA variation on the stub method is the unimplemented enumeration constant, e.g., NSURLRequestReloadIgnoringLocalAndRemoteCacheData. The docs say "Specifies that not only should the local cache data be ignored, but that proxies and other intermediates should be instructed to disregard their caches so far as the protocol allows". However, NSURLRequest.h says "// Unimplemented". Yay. 58f55c1c3069c2f009b8d42e4431b420Fri, 14 May 2010 16:44:13 GMTAnonymous - 2010-05-14 16:39:16http://www.mikeash.com/?page=pyblog/friday-qa-2010-05-14-what-every-apple-programmer-should-know.html#commentsFor anyone with suggestions or complaints about Cocoa API, make sure to file a bug. That's the only way that your issue can get to the right engineer who can actually fix the problem.eefedda860cb0d4f0f8539bc1d2ed486Fri, 14 May 2010 16:39:16 GMT