For those of you having trouble running the application, I've also added i386 to the supported architectures. Because of my use of blocks, Snow Leopard is required to run the app.
Ipod Touch Review
# # 8 GB capacity for about 2,000 songs, 10,000 photos, or 10 hours of video # Up to 40 hours of audio playback or 7 hours of video playback on a single charge # Support for AAC, Protected AAC (iTunes Store) and other audio formats; H.264, MPEG-4,
Ipod Touch Review
# One-year limited warranty # iPod touch has 8 GB capacity for about 2,000 songs, 10,000 photos, or 10 hours of video. # iPod touch has a 3.5-inch (diagonal) widescreen Multi-Touch display with 960-by-640-pixel resolution (326 pixels per inch). › See more technical details.
Ipod Touch Review
# iPod touch has one-year limited warranty. # IPod touch plays up to 40 hours of audio playback or 7 hours of video playback on a single charge. # Motion JPEG video codecs in M4V, MP4, MOV, and AVI formats
Tampilkan postingan dengan label Objective-C. Tampilkan semua postingan
Tampilkan postingan dengan label Objective-C. Tampilkan semua postingan
Sabtu, 30 Oktober 2010
Tile Cutter Updated
Had to re-tile a large image, so spent a bit of time converting tile cutter to use NSOperationQueue. After a little playing, I opted for one operation per row, which seems to give the best all-around performance for the widest variety of images. You can download the latest version from here, or pull the source from GitHub.
For those of you having trouble running the application, I've also added i386 to the supported architectures. Because of my use of blocks, Snow Leopard is required to run the app.
For those of you having trouble running the application, I've also added i386 to the supported architectures. Because of my use of blocks, Snow Leopard is required to run the app.
Kamis, 14 Oktober 2010
Outlets, Cocoa vs. Cocoa Touch
I almost always follow Apple's lead on Cocoa and Cocoa Touch conventions. I figure that by the time outside developers like me see something for the first time, Apple engineers have been living with that thing for many months, so they've likely got a much better idea than I do about the best way to use that new thing.
But, after spending time with their stuff, sometimes — not often, but sometimes — I disagree with what appears to be Apple's recommended "best practice" for doing something. I think I've come to the decision that the IBOutlet behavior in iOS is one of these areas.
If you look at Apple's documentation snippets and sample code, you find that they almost always retain IBOutlet properties, like:
There's a good reason for this. In iOS, the documentation explicitly states that you need to retain all outlets because the bundle loader for iOS autoreleases all objects created as a result of loading a nib.
This is different from Cocoa on the Mac, where it wasn't necessary to retain outlets and people rarely did. In fact, we didn't usually bother with accessor or mutator methods for outlets (it was just unnecessary extra typing in most cases), we just put the IBOutlet keyword in front of the instance variable and the nib loader was happy to attach our outlets like that, retaining the objects that needed retaining.
The behavior under Cocoa/Mac is not actually to retain everything in the nib, but rather, to retain any object that doesn't have a parent object to retain it. So, in other words, if an object in a nib will be retained by something else, like a superview, the nib loader doesn't bother to retain it again. But, if it doesn't, the bundle loader retains it so that it doesn't get deallocated.
This is a logical approach and, in fact, was necessary back in the pre-Objective-C 2.0 days because outlets back then were just iVars and there was no easy way for the controller class to retain objects that needed to be retained.
I have to wonder why they would change the fundamental behavior of a foundation object like NSBundle between Mac OS and iOS? NSBundle is not part of Cocoa or Cocoa Touch, it's part of Foundation, and the whole point of Foundation is to have common objects between the different operating systems.
I wrote a small project to test if the Bundle Loader really did behave differently, as documented, by using an instance of UIView in the nib with no superview. Sure enough, when I didn't retain the outlet, I either got an EXC_BAD_ACCESS or a different object altogether when I printed the outlet to NSLog(). The difference is real. The bundle loader on the Mac will retain outlets for you if they need to be retained, which allows you to continue using instance variables, or properties with the assign keyword. This means you don't have to release your outlets in dealloc and you don't have to mess around with anything like viewDidUnload on iOS.
The bundle loader on the iOS, on the other hand, does not retain anything for you, so if an object does not have a parent object to retain it, you have to retain it in your controller class or you will end up with an invalid outlet.
I really don't see the value in changing this behavior. I'm guessing the decision was made for the sake of memory efficiency in the early days of the iPhone. The idea being that you might load a nib with object instances that you aren't actually using, and with the old behavior, those would take up memory as long as the nib was loaded. That doesn't necessarily sound like a good idea on an embedded device with no virtual memory and 128 megs of RAM, which is what the original iPhone and iPhone 3G had.
Despite that, I think the cure here is worse than the disease. If you don't remember to release your outlets in viewDidUnload (which, if I remember right, we couldn't even do in the 2.0 version of the SDK), your outlets will continue to use up memory after the nib is unloaded, obviating any advantage of the lazy loading. Essentially, it's more fragile, because it depends on the programmer doing the right thing and there are few if any situations where a programmer would need to not do the right thing.
By virtue of the bundle loader not retaining outlets, it also requires more rote, boilerplate code to be written in every controller class in every iOS application, yet it runs just as much of a risk of unnecessary memory use, arguably a greater risk. In other words, the cure is no better than the disease.
iPhones are getting more robust and less memory constrained with every new device that comes out. I would argue that it's already time (or, at very least, soon will be time), to bring the behavior of the two bundle loaders together. If they are brought together, they should be brought together using the old Cocoa behavior not the new iOS behavior. When you think of the number of people coding for the iOS now, those extra required dealloc and viewDidUnload lines in every single controller class in every single iOS application are really adding up to a lot of engineering hours lost on boilerplate.
A few weeks ago, I started experimenting with using assign instead of retain for IBOutlets except in cases where the outlet's object didn't have a superview or another object retaining it. If an outlet's not a view or control, then I also use retain. In essence, I'm mimicking the old behavior of the nib in designing my controller classes.
This has led to a lot less typing and less code to maintain because 95% or more of the outlets I create are connected to objects retained by their superview during the entire existence of the controller class.
Now, I'm not necessarily saying you should do what I'm doing. It can be tricky, at times, remembering which objects need to be retained and they can be hard to debug if you get it wrong. Apple has made a recommendation with good reason and I don't think you should disregard that recommendation lightly. That being said, if you're comfortable enough with Objective-C memory management and the bundle loader to be able to distinguish when a nib object will be automatically retained by something else, you could save yourself a fair bit of typing over time.
I normally try to embrace changes Apple makes, but in this case, I just can't convince myself that this was a good change. The old nib behavior of retaining only things that need retaining has been in use for over 20 years, dating back to when desktop computers were less powerful than our iPhones are, and there doesn't appear to be any practical advantage to the change. On the other hand, we'd all benefit from going back to the old Mac OS bundle behavior because we'd have less make-work to do when setting up a controller class. There's also little danger in changing this behavior because code that follows Apple's current recommendations would continue to work correctly.
But, after spending time with their stuff, sometimes — not often, but sometimes — I disagree with what appears to be Apple's recommended "best practice" for doing something. I think I've come to the decision that the IBOutlet behavior in iOS is one of these areas.
If you look at Apple's documentation snippets and sample code, you find that they almost always retain IBOutlet properties, like:
#import <UIKit/UIKit.h>
@interface FooView : UIView
{
}
@synthesize (nonatomic, retain) IBOutlet UIButton button;
@synthesize (nonatomic, retain) IBOutlet UITextField textField;
@synthesize (nonatomic, retain) IBOutlet UIImageView imageView;
@end
There's a good reason for this. In iOS, the documentation explicitly states that you need to retain all outlets because the bundle loader for iOS autoreleases all objects created as a result of loading a nib.
Objects in the nib file are created with a retain count of 1 and then autoreleased. As it rebuilds the object hierarchy, however, UIKit reestablishes connections between the objects using the setValue:forKey: method, which uses the available setter method or retains the object by default if no setter method is available. If you define outlets for nib-file objects, you should always define a setter method (or declared property) for accessing that outlet. Setter methods for outlets should retain their values, and setter methods for outlets containing top-level objects must retain their values to prevent them from being deallocated. If you do not store the top-level objects in outlets, you must retain either the array returned by the loadNibNamed:owner:options: method or the objects inside the array to prevent those objects from being released prematurely.
This is different from Cocoa on the Mac, where it wasn't necessary to retain outlets and people rarely did. In fact, we didn't usually bother with accessor or mutator methods for outlets (it was just unnecessary extra typing in most cases), we just put the IBOutlet keyword in front of the instance variable and the nib loader was happy to attach our outlets like that, retaining the objects that needed retaining.
The behavior under Cocoa/Mac is not actually to retain everything in the nib, but rather, to retain any object that doesn't have a parent object to retain it. So, in other words, if an object in a nib will be retained by something else, like a superview, the nib loader doesn't bother to retain it again. But, if it doesn't, the bundle loader retains it so that it doesn't get deallocated.
This is a logical approach and, in fact, was necessary back in the pre-Objective-C 2.0 days because outlets back then were just iVars and there was no easy way for the controller class to retain objects that needed to be retained.
I have to wonder why they would change the fundamental behavior of a foundation object like NSBundle between Mac OS and iOS? NSBundle is not part of Cocoa or Cocoa Touch, it's part of Foundation, and the whole point of Foundation is to have common objects between the different operating systems.
I wrote a small project to test if the Bundle Loader really did behave differently, as documented, by using an instance of UIView in the nib with no superview. Sure enough, when I didn't retain the outlet, I either got an EXC_BAD_ACCESS or a different object altogether when I printed the outlet to NSLog(). The difference is real. The bundle loader on the Mac will retain outlets for you if they need to be retained, which allows you to continue using instance variables, or properties with the assign keyword. This means you don't have to release your outlets in dealloc and you don't have to mess around with anything like viewDidUnload on iOS.
The bundle loader on the iOS, on the other hand, does not retain anything for you, so if an object does not have a parent object to retain it, you have to retain it in your controller class or you will end up with an invalid outlet.
I really don't see the value in changing this behavior. I'm guessing the decision was made for the sake of memory efficiency in the early days of the iPhone. The idea being that you might load a nib with object instances that you aren't actually using, and with the old behavior, those would take up memory as long as the nib was loaded. That doesn't necessarily sound like a good idea on an embedded device with no virtual memory and 128 megs of RAM, which is what the original iPhone and iPhone 3G had.
Despite that, I think the cure here is worse than the disease. If you don't remember to release your outlets in viewDidUnload (which, if I remember right, we couldn't even do in the 2.0 version of the SDK), your outlets will continue to use up memory after the nib is unloaded, obviating any advantage of the lazy loading. Essentially, it's more fragile, because it depends on the programmer doing the right thing and there are few if any situations where a programmer would need to not do the right thing.
By virtue of the bundle loader not retaining outlets, it also requires more rote, boilerplate code to be written in every controller class in every iOS application, yet it runs just as much of a risk of unnecessary memory use, arguably a greater risk. In other words, the cure is no better than the disease.
iPhones are getting more robust and less memory constrained with every new device that comes out. I would argue that it's already time (or, at very least, soon will be time), to bring the behavior of the two bundle loaders together. If they are brought together, they should be brought together using the old Cocoa behavior not the new iOS behavior. When you think of the number of people coding for the iOS now, those extra required dealloc and viewDidUnload lines in every single controller class in every single iOS application are really adding up to a lot of engineering hours lost on boilerplate.
A few weeks ago, I started experimenting with using assign instead of retain for IBOutlets except in cases where the outlet's object didn't have a superview or another object retaining it. If an outlet's not a view or control, then I also use retain. In essence, I'm mimicking the old behavior of the nib in designing my controller classes.
This has led to a lot less typing and less code to maintain because 95% or more of the outlets I create are connected to objects retained by their superview during the entire existence of the controller class.
Now, I'm not necessarily saying you should do what I'm doing. It can be tricky, at times, remembering which objects need to be retained and they can be hard to debug if you get it wrong. Apple has made a recommendation with good reason and I don't think you should disregard that recommendation lightly. That being said, if you're comfortable enough with Objective-C memory management and the bundle loader to be able to distinguish when a nib object will be automatically retained by something else, you could save yourself a fair bit of typing over time.
I normally try to embrace changes Apple makes, but in this case, I just can't convince myself that this was a good change. The old nib behavior of retaining only things that need retaining has been in use for over 20 years, dating back to when desktop computers were less powerful than our iPhones are, and there doesn't appear to be any practical advantage to the change. On the other hand, we'd all benefit from going back to the old Mac OS bundle behavior because we'd have less make-work to do when setting up a controller class. There's also little danger in changing this behavior because code that follows Apple's current recommendations would continue to work correctly.
Jumat, 24 September 2010
More on dealloc
I really didn't expect to kick off such a shitstorm yesterday with my dealloc post. A few people accused me of proselytizing the practice of nil'ing ivars, at least for release code. Possibly I did, but my real intention was to share the reasons why you might choose one approach over the other, not to say "you should do it this way." I mostly wrote the blog post to make sure I had my head fully wrapped around the debate because it had come up during the revising of Beginning iPhone Development.
Daniel Jalkut responded with a very lucid write-up that represents one of the common points of view on this matter. That view might best be summed up as "crash, baby, crash". If you want to find bugs, the best thing you can do is crash when they happen, that way you know there's a problem and know to look for it. This is not a new idea. I seem to remember a system extension back in the old Mac System 6 or System 7 days that would actually cause your system to crash if your app did a certain bad thing, even if that bad thing didn't cause any noticeable problems in your app. I don't honestly remember the specifics, but it was something to do with the trapping mechanism, I think (anybody know what I'm talking about??). Of course, Apple didn't ship that extension to non-developers.
What makes this a difficult argument is that Daniel is absolutely right… sometimes. There are scenarios where crashing is much better than, say, data loss or data integrity loss. General rules are always problematic, but though I see his point, I'm sticking with "not crashing on customers" being a good rule generally… except when it's not.
In interest of full disclosure, there's actually a third dealloc pattern, albeit a distant third in popularity, but it has definitely become more popular than when I first head of it. In this approach, you assign your instance variable to a stack local variable, then nil the instance variable before releasing it, like so:
In the release-then-nil approach, after you release, there's a tiny window between the release and the assignment of nil where, in multithreaded code, the invalid pointer could still be theoretically be used. This approach prevents that problem by nil'ing first. This point of view represents the opposite end of the spectrum from the one Daniel stated. In this approach, your goal is to code defensively and never let your app crash if you can help it, even in development. If you're a cautious programmer and believe in the Tao of Defensive Programming, then there you go - that's your approach to dealloc.
For me, personally (warning - I'm about to state an opinion) I can't justify the extra code and time of the defensive approach. It's preemptive treatment of a problem so rare that it's almost silly that this discussion is even happening. I've been writing Objective-C since 1999 and I've only once seen a scenario where the dealloc approach would've made a difference in the actual behavior of the application, and that was a scenario created for a debugging exercise in a workshop, so we're really splitting hairs here.
So, here's my final word on dealloc:
After all, you're the one who has to live with the consequences of your decision.
Daniel Jalkut responded with a very lucid write-up that represents one of the common points of view on this matter. That view might best be summed up as "crash, baby, crash". If you want to find bugs, the best thing you can do is crash when they happen, that way you know there's a problem and know to look for it. This is not a new idea. I seem to remember a system extension back in the old Mac System 6 or System 7 days that would actually cause your system to crash if your app did a certain bad thing, even if that bad thing didn't cause any noticeable problems in your app. I don't honestly remember the specifics, but it was something to do with the trapping mechanism, I think (anybody know what I'm talking about??). Of course, Apple didn't ship that extension to non-developers.
What makes this a difficult argument is that Daniel is absolutely right… sometimes. There are scenarios where crashing is much better than, say, data loss or data integrity loss. General rules are always problematic, but though I see his point, I'm sticking with "not crashing on customers" being a good rule generally… except when it's not.
In interest of full disclosure, there's actually a third dealloc pattern, albeit a distant third in popularity, but it has definitely become more popular than when I first head of it. In this approach, you assign your instance variable to a stack local variable, then nil the instance variable before releasing it, like so:
- (void)dealloc
{
id fooTemp = foo;
foo = nil;
[fooTemp release];
}
In the release-then-nil approach, after you release, there's a tiny window between the release and the assignment of nil where, in multithreaded code, the invalid pointer could still be theoretically be used. This approach prevents that problem by nil'ing first. This point of view represents the opposite end of the spectrum from the one Daniel stated. In this approach, your goal is to code defensively and never let your app crash if you can help it, even in development. If you're a cautious programmer and believe in the Tao of Defensive Programming, then there you go - that's your approach to dealloc.
For me, personally (warning - I'm about to state an opinion) I can't justify the extra code and time of the defensive approach. It's preemptive treatment of a problem so rare that it's almost silly that this discussion is even happening. I've been writing Objective-C since 1999 and I've only once seen a scenario where the dealloc approach would've made a difference in the actual behavior of the application, and that was a scenario created for a debugging exercise in a workshop, so we're really splitting hairs here.
So, here's my final word on dealloc:
- If you already have a strong opinion on which one to use for the type of coding you do, use that one. If not…
- If you prefer that bugs always crash your app so you know about them, use the traditional release-only approach
- If you prefer not to crash if you can help it and prefer to find your bugs using other means, use the newer approach
- If you want your app to crash for you, but not your customers, use the MCRelease() macro or use #if debug pre-processor directives
After all, you're the one who has to live with the consequences of your decision.
Kamis, 23 September 2010
Dealloc
Last week there was a bit of a Twitter in-fight in the iOS community over the "right" way to release your instance variables in dealloc. I think Rob actually started it, to be honest, but I probably shouldn't be bringing that up.
Basically, several developers were claiming that there's never a reason to set an instance variable to nil in dealloc, while others were arguing that you should always do so.
To me, there didn't seem to be a clear and compelling winner between the two approaches. I've used both in my career. However, since we're in the process of trying to decide which approach to use in the next edition of Beginning iPhone Development, I reached out to Apple's Developer Tools Evangelist, Michael Jurewitz, to see if there was an official or recommended approach to handling instance variables in dealloc.
Other than the fact that you should never, ever use mutators in dealloc (or init, for that matter), Apple does not have an official recommendation on the subject.
However, Michael and Matt Drance of Bookhouse Software and a former Evangelist himself, had discussed this issue extensively last week. They kindly shared their conclusions with me and said it was okay for me to turn it into a blog post. So, here it is. Hopefully, I've captured everything correctly.
In a multi-threaded environment, however, there is a very real possibility that the pointer will be accessed between the time that it is deallocated and the time that its object is done being deallocated. Generally speaking, this is only going to happen if you've got a bug elsewhere in your code, but let's face it, you may very well. Anyone who codes on the assumption that all of their code is perfect is begging for a smackdown, and Xcode's just biding its time waiting for the opportunity.
In the first approach, your application will usually crash with an EXC_BAD_ACCESS, though you could also end up with any manner of odd behavior (which we call a "heisenbug") if the released object is deallocated and its memory is then reused for another object owned by your application. In those cases, you may get a selector not recognized exception when the message is sent to the deallocated object, or you may simply get unexpected behavior from the same method being called on the wrong object.
In the other approach, your application will quietly send a message to nil and go about its merry way, generally without a crash or any other immediately identifiable problem.
The former approach is actually good when you're developing, debugging, and doing unit testing, because it makes it easier to find your problematic code. On the other hand, that approach is really, really bad in a shipping application because you really don't want to crash on your users if you can avoid it.
The latter approach, conversely, can hide bugs during development, but handles those bugs more gracefully when they happen, and you're far less likely to have your application go up in a big ball of fire in front of your users.
If you want your application to crash when a released pointer is accessed during development and debugging, the solution is to use the traditional approach in your debug configuration. If you want your application to degrade gracefully for your users, the solution is to use the newer approach in your release and ad hoc configurations.
One somewhat pedantic implementation of this approach would be this:
That code assumes that your debug configuration has a precompiler definition of DEBUG, which you usually have to add to your Xcode project - most Xcode project templates do not provide it for you. There are several ways you can add it, but I typically just use the Preprocessor Macros setting in the project's Build Configuration:

Although the code above does, indeed, give us the best of both worlds - a crash during development and debugging and graceful degrading for customers - it at least doubles the amount of code we have to write in every class. We can do better than that, though. How about a little macro magic? If we add the following macro to our project's .pch file:
We can then use that macro in dealloc, and our best-of-both-worlds code becomes much shorter and more readable:
Once you've got the macro in your project, this option is actually no more work or typing than either of the other dealloc methods.
But, you know what? If you want to keep doing it the way you've always done it, it's really fine, regardless of which way you do it. If you're consistent in your use and are aware of the tradeoffs, there's really no compelling reason to use one over the other outside of personal preference.
So, in other words, it's kind of a silly thing for us all to argue over, especially when there's already politics, religions, and sports to fill that role.
While it is true that in Java and some other languages with garbage collection, nulling out a pointer that you're done with helps the garbage collector know that you're done with that object, so it's not altogether unlikely that Objective-C's garbage collector does the same thing, however any benefit to nil'ing out instance variables once we get garbage collection in iOS would be marginal at best. I haven't been able to find anything authoritative that supports this claim, but even if it's 100% true, it seems likely that when the owning object is deallocated a few instructions later, the garbage collector will realize that the deallocated object is done with anything it was using.
If there is a benefit in this scenario, which seems unlikely, it seems like it would be such a tiny difference that it wouldn't even make sense to factor it into your decision. That being said, I have no firm evidence one way or the other on this issue and would welcome being enlightened.
After writing this, Michael Jurewitz pinged me to confirmed that there's absolutely no benefit in terms of GC to releasing in dealloc, though it is otherwise a good idea to nil pointers you're done with under GC..
Thanks to @borkware and @eridius for pointing out some issues
Update: Don't miss Daniel Jalkut's excellent response.
Basically, several developers were claiming that there's never a reason to set an instance variable to nil in dealloc, while others were arguing that you should always do so.
To me, there didn't seem to be a clear and compelling winner between the two approaches. I've used both in my career. However, since we're in the process of trying to decide which approach to use in the next edition of Beginning iPhone Development, I reached out to Apple's Developer Tools Evangelist, Michael Jurewitz, to see if there was an official or recommended approach to handling instance variables in dealloc.
Other than the fact that you should never, ever use mutators in dealloc (or init, for that matter), Apple does not have an official recommendation on the subject.
However, Michael and Matt Drance of Bookhouse Software and a former Evangelist himself, had discussed this issue extensively last week. They kindly shared their conclusions with me and said it was okay for me to turn it into a blog post. So, here it is. Hopefully, I've captured everything correctly.
The Two Major Approachs
Just to make sure we're all on the same page, let's look at the two approaches that made up the two different sides of the argument last week.Just Release
The more traditional approach is to simply release your instance variables and leave them pointing to the released (and potentially deallocated) object, like so:- (void)dealloc
{
[Sneezy release];
[Sleepy release];
[Dopey release];
[Doc release];
[Happy release];
[Bashful release];
[Grumpy release];
[super dealloc];
}
In this approach, each of the pointers will be pointing to a potentialy invalid object for a very short period of time — until the method returns — at which point the instance variable will disappear along with its owning object. In a single-threaded application (the pointer would have to be accessed by something triggered by code in either this object's implementation of dealloc or in the dealloc of one of its superclasses, there's very little chance that the instance variables will be used before they go away , which is probably what has led to the proclamations made by several that there's "no value in setting instance variables to nil" in dealloc. In a multi-threaded environment, however, there is a very real possibility that the pointer will be accessed between the time that it is deallocated and the time that its object is done being deallocated. Generally speaking, this is only going to happen if you've got a bug elsewhere in your code, but let's face it, you may very well. Anyone who codes on the assumption that all of their code is perfect is begging for a smackdown, and Xcode's just biding its time waiting for the opportunity.
Release and nil
In the last few years, another approach to dealloc has become more common. In this approach, you release your instance variable and then immediately set them to nil before releasing the next instance variable. It's common to actually put the release and the assignment to nil on the same line separated by a comma rather than on consecutive lines separated by semicolons, though that's purely stylistic and has no affect on the way the code is compiled. Here's what our previous dealloc method might look like using this approach:- (void)dealloc
{
[sneezy release], sneezy = nil;
[sleepy release], sleepy = nil;
[dopey release], dopey = nil;
[doc release], doc = nil;
[happy release], happy = nil;
[bashful release], bashful = nil;
[grumpy release], grumpy = nil;
[super dealloc];
}
In this case, if some piece of code accesses a pointer between the time that dealloc begins and the object is actually deallocated, it will almost certainly fail gracefully because sending messages to nil is perfectly okay in Objective-C. However, you're doing a tiny bit of extra work by assigning nil to a bunch of pointers that are going to go away momentarily, and you're creating a little bit of extra typing for yourself in every class.The Showdown
So, here's the real truth of the matter: The vast majority of the time, it's not going to make any noticeable difference whatsoever. If you're not accessing instance variables that have been released, there's simply not going to be any difference in the behavior between the two approaches. If you are, however, then the question is: what do you want to happen when your code does that bad thing?In the first approach, your application will usually crash with an EXC_BAD_ACCESS, though you could also end up with any manner of odd behavior (which we call a "heisenbug") if the released object is deallocated and its memory is then reused for another object owned by your application. In those cases, you may get a selector not recognized exception when the message is sent to the deallocated object, or you may simply get unexpected behavior from the same method being called on the wrong object.
In the other approach, your application will quietly send a message to nil and go about its merry way, generally without a crash or any other immediately identifiable problem.
The former approach is actually good when you're developing, debugging, and doing unit testing, because it makes it easier to find your problematic code. On the other hand, that approach is really, really bad in a shipping application because you really don't want to crash on your users if you can avoid it.
The latter approach, conversely, can hide bugs during development, but handles those bugs more gracefully when they happen, and you're far less likely to have your application go up in a big ball of fire in front of your users.
The Winner?
There really isn't a clear cut winner, which is probably why Apple doesn't have an official recommendation or stance. During their discussion, Matt and Michael came up with a "best of both worlds" solution, but it requires a fair bit of extra code over either of the common approaches.If you want your application to crash when a released pointer is accessed during development and debugging, the solution is to use the traditional approach in your debug configuration. If you want your application to degrade gracefully for your users, the solution is to use the newer approach in your release and ad hoc configurations.
One somewhat pedantic implementation of this approach would be this:
- (void)dealloc
{
#if DEBUG
[Sneezy release];
[Sleepy release];
[Dopey release];
[Doc release];
[Happy release];
[Bashful release];
[Grumpy release];
[super dealloc];
#else
[sneezy release], sneezy = nil;
[sleepy release], sleepy = nil;
[dopey release], dopey = nil;
[doc release], doc = nil;
[happy release], happy = nil;
[bashful release], bashful = nil;
[grumpy release], grumpy = nil;
[super dealloc];
#endif
}
That code assumes that your debug configuration has a precompiler definition of DEBUG, which you usually have to add to your Xcode project - most Xcode project templates do not provide it for you. There are several ways you can add it, but I typically just use the Preprocessor Macros setting in the project's Build Configuration:

Although the code above does, indeed, give us the best of both worlds - a crash during development and debugging and graceful degrading for customers - it at least doubles the amount of code we have to write in every class. We can do better than that, though. How about a little macro magic? If we add the following macro to our project's .pch file:
#if DEBUG
#define MCRelease(x) [x release]
#else
#define MCRelease(x) [x release], x = nil
#endifWe can then use that macro in dealloc, and our best-of-both-worlds code becomes much shorter and more readable:
- (void)dealloc
{
MCRelease(sneezy);
MCRelease(sleepy);
MCRelease(dopey);
MCRelease(doc);
MCRelease(happy);
MCRelease(bashful);
MCRelease(grumpy);
[super dealloc];
}Once you've got the macro in your project, this option is actually no more work or typing than either of the other dealloc methods.
But, you know what? If you want to keep doing it the way you've always done it, it's really fine, regardless of which way you do it. If you're consistent in your use and are aware of the tradeoffs, there's really no compelling reason to use one over the other outside of personal preference.
So, in other words, it's kind of a silly thing for us all to argue over, especially when there's already politics, religions, and sports to fill that role.
The Garbage Collection Angle
There's one last point I want to address. I've heard a few times from different people that setting an instance variable to nil in dealloc acts as a hint to the garbage collector when you're using the allowed-not-required GC option (when the required option is being used, dealloc isn't even called, finalize is). If this were true, forward compatibility would be another possible argument for preferring the newer approach to dealloc over the traditional approach.While it is true that in Java and some other languages with garbage collection, nulling out a pointer that you're done with helps the garbage collector know that you're done with that object, so it's not altogether unlikely that Objective-C's garbage collector does the same thing, however any benefit to nil'ing out instance variables once we get garbage collection in iOS would be marginal at best. I haven't been able to find anything authoritative that supports this claim, but even if it's 100% true, it seems likely that when the owning object is deallocated a few instructions later, the garbage collector will realize that the deallocated object is done with anything it was using.
If there is a benefit in this scenario, which seems unlikely, it seems like it would be such a tiny difference that it wouldn't even make sense to factor it into your decision. That being said, I have no firm evidence one way or the other on this issue and would welcome being enlightened.
After writing this, Michael Jurewitz pinged me to confirmed that there's absolutely no benefit in terms of GC to releasing in dealloc, though it is otherwise a good idea to nil pointers you're done with under GC..
Thanks to @borkware and @eridius for pointing out some issues
Update: Don't miss Daniel Jalkut's excellent response.
Kamis, 19 Agustus 2010
Transparent Grouped Tableviews
I ran into a problem today with a transparent grouped table view. On the table view, I set opaque to NO and set the background color to clearColor so that an image behind the table would show through:

I can't show you the actual app I was working on because I'm under NDA, but I've created a sample project that shows the problem using a really ugly background image. Notice the black around the corners of the group sections:

It took me a while to figure out why this was happening, but with a little crowd-source debugging thanks to Twitter, I've narrowed the culprit down. Even though you can clearly see in the first picture that the table view's background color has been set to clearColor in Interface Builder, that setting is either not being respected, or is being changed somewhere. Now, I'm using stock elements here - no custom views at all - so I know I'm not changing it.
My original fix for this was rather involved, but after a process of elimination where I kept commenting out bits of the fix and and seeing what impact it had, I was able to remove all of the code of my fix except for this line of code:
Turns out all the other parts were red herrings leftover from my various attempts to fix the problem. So, if you're having this problem, just manually re-set the table view's background color to clearColor and you should be good.
(and yes, I'm going to file a bug report now)

I can't show you the actual app I was working on because I'm under NDA, but I've created a sample project that shows the problem using a really ugly background image. Notice the black around the corners of the group sections:

It took me a while to figure out why this was happening, but with a little crowd-source debugging thanks to Twitter, I've narrowed the culprit down. Even though you can clearly see in the first picture that the table view's background color has been set to clearColor in Interface Builder, that setting is either not being respected, or is being changed somewhere. Now, I'm using stock elements here - no custom views at all - so I know I'm not changing it.
My original fix for this was rather involved, but after a process of elimination where I kept commenting out bits of the fix and and seeing what impact it had, I was able to remove all of the code of my fix except for this line of code:
- (void)viewDidLoad
{
[super viewDidLoad];
self.tableView.backgroundColor = [UIColor clearColor];
}Turns out all the other parts were red herrings leftover from my various attempts to fix the problem. So, if you're having this problem, just manually re-set the table view's background color to clearColor and you should be good.
(and yes, I'm going to file a bug report now)
Core Data Starting Data
Yesterday, I tweeted asking for advice about the best way to provide a starter set of data in a Core Data-based application. The app I'm working on had started with just a small set of starter data, so the first time the app was run, I simply pulled in that starter data from a plist in the background and created managed objects based on the contents and all was good. The user never noticed it and the load was complete before they needed to do anything with the data.
Then the data set got quite a bit larger and that solution became too slow. So, I asked the Twitterverse for opinions about the best way to provide a large amount of starting data in a Core Data app. What I was hoping to find out was whether you could include a persistent store in your application bundle and use that instead of creating new persistent store the first time the app was launched.
The answer came back from lots and lots of people that you could, indeed, copy an existing persistent store from your app bundle. You could even create the persistent store using a Mac Cocoa Core Data application as long as it used the same xcdatamodel file as your iPhone app.
Before I go on, I want to thank all the people who responded with suggestions and advice. A special thanks to Dan Pasco from the excellent dev shop Black Pixel who gave very substantive assistance. With the help of the iOS dev community, it took me about 15 minutes to get this running in one of my current apps. Several people have asked for the details over Twitter. 140 characters isn't going to cut it for this, but here's what I did.
First, I created a new Mac / Cocoa project in Xcode. I used the regular Cocoa Application template, making sure to use Core Data. Several people also suggested that you could use a Document-Based Cocoa Application using Core DAta which would allow you to save the persistent store anywhere you wanted. I create the Xcode project in a subfolder of my main project folder and I added the data model file and all the managed object classes from my main project to the new project using project-relative references, making sure NOT to copy the files into the new project folder - I want to use the same files as my original project so any changes made in one are reflected in the other.
If my starting data was changing frequently, I'd probably make this new project a dependency of my main project and add a copy files build phase that would copy the persistent store into the main project's Resources folder, but my data isn't changing very often, so I'm just doing it manually. You definitely can automate the task within Xcode, and I heard from several people who have done so.
In the new Cocoa project, the first thing to do is modify the persistentStoreCoordinator method of the app delegate so it uses a persistent store with the same name as your iOS app. This is the line of code you need to modify:
Make sure you add the .sqlite extension to the filename. By default, the Cocoa Application template uses an XML datastore and no file extension. The filename you enter here is used exactly, so if you want a file extension, you have to specify it.
Since the Cocoa Application project defaults to an XML-based persistent store, you also need to change the Cocoa App's store type to SQLite. That's a few lines later. Look for this line:
Change NSXMLStoreType to NSSQLiteStoreType.
Optionally, you can also change the applicationSupportDirectory method to return a different location if you want to make the persistent store easier to find. By default, it's going to go in
Next, you need to do your data import. This code will inherently be application-specific and will depend on you data model and the data you need to import. Here's a simple pseudo-method for parsing a tab-delimited text file to give an idea what this might look like. This method creates an NSAutoreleasePool and a context so it can be launched in a thread if you desire. You can also call it directly - it won't hurt anything.
You can add the delegate method applicationDidFinishLaunching: to your app delegate and put your code there. You don't even really need to worry about how long it takes - there's no watchdog process on the Mac that kills your app if it isn't responding to events after a period of time. If you prefer, you can have your data import functionality working in the background, though since the app does nothing else, there's no real benefit except the fact that it's the "right" way to code an application.
Now, run your app. When it's done importing, you can just copy the persistent store file into your iOS app's Xcode project in the Resources group. When you build your app, this file will automatically get copied into your application's bundle. Now, you just need to modify the app delegate of your iOS application to use this persistent store instead of creating a new, empty persistent store the first time the app is run.
To do that, you simply check for the existence of the persistent store in your application's /Documents folder, and if it's not there, you copy it from the application bundle to the the /Documents folder before creating the persistent store. In the app delegate, the persistentStoreCoordinator method should look something like this:
Et voilà ! If you run the app for the first time on a device, or run it on the simulator after resetting content and settings, you should start with the data that was loaded in your Cocoa application.
Then the data set got quite a bit larger and that solution became too slow. So, I asked the Twitterverse for opinions about the best way to provide a large amount of starting data in a Core Data app. What I was hoping to find out was whether you could include a persistent store in your application bundle and use that instead of creating new persistent store the first time the app was launched.
The answer came back from lots and lots of people that you could, indeed, copy an existing persistent store from your app bundle. You could even create the persistent store using a Mac Cocoa Core Data application as long as it used the same xcdatamodel file as your iPhone app.
Before I go on, I want to thank all the people who responded with suggestions and advice. A special thanks to Dan Pasco from the excellent dev shop Black Pixel who gave very substantive assistance. With the help of the iOS dev community, it took me about 15 minutes to get this running in one of my current apps. Several people have asked for the details over Twitter. 140 characters isn't going to cut it for this, but here's what I did.
First, I created a new Mac / Cocoa project in Xcode. I used the regular Cocoa Application template, making sure to use Core Data. Several people also suggested that you could use a Document-Based Cocoa Application using Core DAta which would allow you to save the persistent store anywhere you wanted. I create the Xcode project in a subfolder of my main project folder and I added the data model file and all the managed object classes from my main project to the new project using project-relative references, making sure NOT to copy the files into the new project folder - I want to use the same files as my original project so any changes made in one are reflected in the other.
If my starting data was changing frequently, I'd probably make this new project a dependency of my main project and add a copy files build phase that would copy the persistent store into the main project's Resources folder, but my data isn't changing very often, so I'm just doing it manually. You definitely can automate the task within Xcode, and I heard from several people who have done so.
In the new Cocoa project, the first thing to do is modify the persistentStoreCoordinator method of the app delegate so it uses a persistent store with the same name as your iOS app. This is the line of code you need to modify:
NSURL *url = [NSURL fileURLWithPath: [applicationSupportDirectory stringByAppendingPathComponent: @"My_App_Name.sqlite"]];Make sure you add the .sqlite extension to the filename. By default, the Cocoa Application template uses an XML datastore and no file extension. The filename you enter here is used exactly, so if you want a file extension, you have to specify it.
Since the Cocoa Application project defaults to an XML-based persistent store, you also need to change the Cocoa App's store type to SQLite. That's a few lines later. Look for this line:
if (![persistentStoreCoordinator addPersistentStoreWithType:NSXMLStoreType
configuration:nil
URL:url
options:nil
error:&error]){Change NSXMLStoreType to NSSQLiteStoreType.
Optionally, you can also change the applicationSupportDirectory method to return a different location if you want to make the persistent store easier to find. By default, it's going to go in
~/Library/Application Support/[Cocoa App Name]/which can be a bit of a drag to navigate to.
Next, you need to do your data import. This code will inherently be application-specific and will depend on you data model and the data you need to import. Here's a simple pseudo-method for parsing a tab-delimited text file to give an idea what this might look like. This method creates an NSAutoreleasePool and a context so it can be launched in a thread if you desire. You can also call it directly - it won't hurt anything.
- (void)loadInitialData
{
NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
NSManagedObjectContext *context = [[NSManagedObjectContext alloc] init];
[context setPersistentStoreCoordinator:self.persistentStoreCoordinator];
NSString *path = [[NSBundle mainBundle] pathForResource:@"foo" ofType:@"txt"];
char buffer[1000];
FILE* file = fopen([path UTF8String], "r");
while(fgets(buffer, 1000, file) != NULL)
{
NSString* string = [[NSString alloc] initWithUTF8String:buffer];
NSArray *parts = [string componentsSeparatedByString:@"\t"]
MyManagedObjectClass *oneObject = [self methodToCreateObjectFromArray:parts];
[string release];
}
NSLog(@"Done initial load");
fclose(file);
NSError *error;
if (![context save:&error])
NSLog(@"Error saving: %@", error);
[context release];
[pool drain];
}You can add the delegate method applicationDidFinishLaunching: to your app delegate and put your code there. You don't even really need to worry about how long it takes - there's no watchdog process on the Mac that kills your app if it isn't responding to events after a period of time. If you prefer, you can have your data import functionality working in the background, though since the app does nothing else, there's no real benefit except the fact that it's the "right" way to code an application.
- (void)applicationDidFinishLaunching:(NSNotification *)aNotification
{
// Do import or launch threads to do import
// So, do this:
[self loadInitialData];
// or this:
[self performSelectorInBackground:@selector(loadInitialData) withObject:nil];
// But not both for the same method probably
}Now, run your app. When it's done importing, you can just copy the persistent store file into your iOS app's Xcode project in the Resources group. When you build your app, this file will automatically get copied into your application's bundle. Now, you just need to modify the app delegate of your iOS application to use this persistent store instead of creating a new, empty persistent store the first time the app is run.
To do that, you simply check for the existence of the persistent store in your application's /Documents folder, and if it's not there, you copy it from the application bundle to the the /Documents folder before creating the persistent store. In the app delegate, the persistentStoreCoordinator method should look something like this:
- (NSPersistentStoreCoordinator *)persistentStoreCoordinator
{
@synchronized (self)
{
if (persistentStoreCoordinator != nil)
return persistentStoreCoordinator;
NSString *defaultStorePath = [[NSBundle bundleForClass:[self class]] pathForResource:@"My_App_Name" ofType:@"sqlite"];
NSString *storePath = [[self applicationDocumentsDirectory] stringByAppendingPathComponent: @"My_App_Name.sqlite"];
NSError *error;
if (![[NSFileManager defaultManager] fileExistsAtPath:storePath])
{
if ([[NSFileManager defaultManager] copyItemAtPath:defaultStorePath toPath:storePath error:&error])
NSLog(@"Copied starting data to %@", storePath);
else
NSLog(@"Error copying default DB to %@ (%@)", storePath, error);
}
NSURL *storeURL = [NSURL fileURLWithPath:storePath];
persistentStoreCoordinator = [[NSPersistentStoreCoordinator alloc] initWithManagedObjectModel:[self managedObjectModel]];
NSDictionary *options = [NSDictionary dictionaryWithObjectsAndKeys:
[NSNumber numberWithBool:YES], NSMigratePersistentStoresAutomaticallyOption,
[NSNumber numberWithBool:YES], NSInferMappingModelAutomaticallyOption, nil];
if (![persistentStoreCoordinator addPersistentStoreWithType:NSSQLiteStoreType configuration:nil URL:storeURL options:options error:&error])
{
NSLog(@"Unresolved error %@, %@", error, [error userInfo]);
abort();
}
return persistentStoreCoordinator;
}
}Et voilà ! If you run the app for the first time on a device, or run it on the simulator after resetting content and settings, you should start with the data that was loaded in your Cocoa application.
17.28
ipod touch review

