Skip to content

Archive

Tag: Beginner

After almost a year, my third update to the ObjC beginner caveats blurb. 8 new caveats added today (marked with [NEW])

Language/SDK

1. NSArray/NSMutableArray cannot hold ints or NSInteger
>> although NSInteger is notationally similar to NSNumber, NSNumber is an object while NSInteger is an alias for int or long (depending on whether you’re running a 32/64 bits architecture). NSArray can only hold objects. If you want to add a number to an NSArray, use NSNumber and initialise your number with something like [NSNumber numberWithInt:26].
(Yes, you can use standard C arrays, there are quite a few posts about this around)

2. NSNumber doesn’t auto-unbox
>> If you try to assign NSNumber to an int (or, better, an NSInteger), you’re assigning a pointer, not the actual number. To get the actual number, use something like: [MyNSNumber integerValue]

3. Strings don’t concatenate with [+] or [.]
>>
If something like “Score: “+score is what you’re used to, try making friends with:
[NSString stringWithFormat:@"Score: %i", score];

4. In, (void)applicationDidFinishLaunching:(UIApplication *)application {…}not all IBOutlets depending on your nib file will initialize until your view is added to the main window
after [window makeKeyAndVisible]; it should be safe to access all IBOutlets attached to your viewController.

5. I updated the frame property of a subview, but the coordinates look incorrect.
>> after updating the UIView.frame property on a subview, invoke setNeedsLayout on the parent view.

6. NSTimer fires at the wrong time / fires too many times
>> When you allocate/init NSTimer with the usual code, you don’t need to invoke fire(). NSTimer is setup as soon as it’s initialized and fire() shouldn’t be called directly as it, *doh!* fires the timer.
>> Whatever you name your callback method, it should take a unique NSTimer argument ( the selector argument would typically look like @selector(myCallback:) with the semi-colon at the end).
What happened to me is that my timer kept firing over and over even though repeat:NO was set. Adding the timer argument to the callback fixed the problem.

7. You cannot declare new variables inside a switch statement

switch (foo) {

case BAR:

char foobar=’*'; // compile error

break;

}

Use this instead:

char foobar;

switch (foo) {

case BAR:

foobar=’*'; // OK

break;

}

Memory Management

8. Use NSZombieEnabled to crash and get a call stack when your program attempts accessing a deallocated object [NEW]

Google it…

9. You need a symbolic breakpoint to hit malloc_error_break and get a call stack [NEW]

Google it (I feel lazy).

10. Unless otherwise stated, objects allocated as a side effect of calling methods other than [alloc] are autoreleased.

This is especially applicable to objects created using factory methods. Take the following example:

[NSMutableArray arrayWithCapacity:5]; // will be released automatically

[[NSMutableArray alloc]initWithCapacity:5]; // we just created a zombie!

At first, it would seem that [NSMutableArray arrayWith...] forms are just shorthands for [[NSMutableArray alloc] initWith…]. Hell, not quite.

[NSMutableArray arrayWith...] and other, similar factory methods, ‘emulate stack allocation’ for reference types. This means that we don’t need to worry about releasing them, as the so-called autorelease pool will take care of them for us.

When we check our code for memory issues, knowing which objects are kept in memory (on the heap) is essential. One way to learn about these is to scan for alloc, retain and release statements. The so called ‘shorthand forms’ allow using objects transiently, without worrying about memory issues, so this convention can help reduce the time spent on memory management.

There are downsides to this for unfortunate beginners:

(1) When we start with objective c, the [alloc[init form appears verbose and cumbersome. So we're likely to create our own shortcuts -- generating 'silent allocations' that make memory management harder.
(2) If we use the 'shorthand' forms, we quickly end up with weird, hard to fix bugs, because we might end up assigning auto-releaseable objects to variables, then these objects get deleted implicitly, and finally we end up accessing... ...garbage(!!!).

11. Beware of  abusing autorelease [NEW]

Autorelease is useful and in some cases you cannot avoid using it:

  • Objects returned by factory methods
    A(n essential) convention dictates that objects created using [alloc] have a reference count of 1. In contrast, callers of factory methods are not responsible for releasing returned objects unless they retain them (see #10)
    Since we can’t use [release] before returning an object we just created, we need to use [autorelease]
  • An object on the call stack may get deallocated if [release] is called.
    Now, it may be argued that only bad design can cause this to happen. Nevertheless one way to solve the problem (it can get nasty) is to use [autorelease]

Now that this is out of the way, beware of practices involving using [autorelease] as a ‘default safe way’. Here’s why:

If an object deallocates unexpectedly following a call to [autorelease] the call stack above [dealloc] doesn’t tell you what caused this object to deallocate.

I don’t see how a coding style involving loosing track of deallocation events can be safe.

12. You can crash the leaks tool in Instruments (seen in Instruments 4.0, 4.1) [NEW]

Recently I managed quirky memory management code that didn’t crash on device or in the simulator, yet crashed instruments. Checking the details, I stumbled on something like ‘invalid leaks data’.

I was about to use up one of my support tickets but reflected that sending my code over and waiting for the ticket to process would take a lot longer than reverting my changes and use the weak muscle to figure it out.

On the downside I got lucky before I managed to narrow it down.

Even if you’re not part of a team, consider using versioning (SVN or whatever new-fangled stuff you’d like).

Kind reminder:

No versioning
=> no diff tool
=> no way to accurately revert changes
=> panic
=> loss of money
=> loss of sleep
=> loss of hair(*)

(*)If you are bald I trust this won’t be an issue.

13. WTF my code crashes when ‘unplugged’ [NEW]

Consider the following case:

  • Your code doesn’t crash when debugging on-device.
  • Your code doesn’t crash when debugging in the simulator.
  • Your code crashes when running in the simulator after pressing the app icon.
  • Your code crashes on-device when running after pressing the app icon.

Then try this:

  • Disable NSZombieEnabled. Quite possibly you’ll see a fairly non-descript crash in the debugger when running the same code.
  • Try to isolate the stack that causes the crash (it’s not meant to be fun but using a dichotomy you might get it done in less than an hour)
  • Re-enable NSZombieEnabled. At this point you may see inconsistent variable assignments in the debug window.
  • Set breakpoints on [dealloc]. Quite possibly you’re deallocating an object that’s already ‘somewhere up’ on the call stack. On sunny days this crashes the device/simulator (hey, it’s actually a GOOD thing). On rainy days it drives the debugger crazy.

Build errors & warnings

14. Warning: Multiple Build Commands for output file …

In XCode, the name of a resource (e.g. a picture to include in the build as resource) is different from the path the resource is retrieved from. Several resources can have the same name, linked with different paths. So for example we can have:

  • foo.jpg (/images/foo.jpg)
  • foo.jpg (/mypics/draft/foo.jpg)

In the final bundle, however these resources are in conflict, they both target /foo.jpg. XCode will issue a warning if resources conflict in this way.

Unsurprisingly, this typically happens when reorganizing project resources.

15. Linking C++ libraries [NEW]

If your project depends on C++/ObjC++ libraries, you may need to add -lstdc++ to other linker flags to your build (see here).

Nibbling UIs (Interface Builder)

16. Action mappings are one-to-many

Yesterday I copied a button from my UI and bound an action to it. Then I was rather puzzled to find that, when I clicked on this button, my game started off with an ominous GL error code. I thought binding an action to a button overrides the previously bound action. It doesn’t. The same user action on the same widget can bind several IBAction targets. In my case this caused the game’s start method to be called twice.

Asset management

17. Use the blue folders [NEW]

If you have hundreds of assets, adding them manually will be a pain. However you can link a whole folder; in XCode’s ‘add files wizard’ select your folder and tick create folder references automatically…

XCode is getting better at detecting changed/added assets between builds (but see #18, below)

18. Sometimes you need to remove your app from the simulator/test device [NEW]

If your code relies on a mechanism allowing to retrieve a resource from one of several locations, you need to delete your app from the simulator / device whenever you remove assets from a blue folder.

Let’s say your sound folder is organized like this:

default_sounds/
---- KO_sound.caf
---- goblin_sounds/
-------- KO_sound.caf

Now suppose you deleted KO_sound.caf under goblin_sounds. Well the goblin specific sound recorded from your little sister’s performance will still load and play because it doesn’t get removed automatically.

Between builds, no files are deleted from package contents. I don’t know whether it’s a bug or not. It’s often annoying.

NSLog is a trace function. In short this emulates the concision of System.out.println() (java) without losing anything to the elegance of trace() (AS2/3).

Using NSLog

There seems to be scores of forum posts about NSLog not working with XCode, and even fixes for the problem. But hey, for the rest of us, NSLog works perfectly with XCode and CocoaTouch. You include something like:

// Print 'Hello 1st console output' to the console
NSLog("Hello %ist console output",1);

While a project is open, select Run >> Console to view NSLog output.

NSLog with Macros

I suggested earlier that there might be reasonable uses of macros. I’m reading somewhere that the C preprocessor is a *steaming turd*, so let’s have it. Try this:

#define print(...) NSLog(@__VA_ARGS__)
// Print 'Hello 2nd console output' to the console
print("Hello %ind console output",2);

The little more than notational convenience here is that you can easily and efficiently remove all logs from a production build by commenting out NSLog(@__VA_ARGS__) in the macro definition.

Removing annoying log output

By default, NSLog will output information about the current process along with a time stamp for every output from NSLog. Usually this is clutter. If you dump the “JANSLogHack” code in an .m file associated with your project, that will remove all this stuff. This is gracefully provided by Jens Ayton as a public domain utility.

#ifndef NDEBUG
#import <Foundation/Foundation.h>
#import <stdio.h>

extern void _NSSetLogCStringFunction(void (*)(const char *string, unsigned length, BOOL withSyslogBanner));

static void PrintNSLogMessage(const char *string, unsigned length, BOOL withSyslogBanner){
   puts(string);
}

static void HackNSLog(void) __attribute__((constructor));
static void HackNSLog(void){
  _NSSetLogCStringFunction(PrintNSLogMessage);
}
#endif

Stripping traces off release builds

Keeping traces in releases builds isn’t a good idea as they can seriously slow down code execution. I’m not covering this here, but you could check this article from the iphone development blog.

How about using the debugger

You might be inspired to try the XCode debugger in many situations where you would trace stuff. The debugger lets you set breakpoints to interrupt your program while running and – seriously useful – check the content of variables and objects by hovering over your source with the mouse. I often find this significantly faster and cleaner than using traces.

So much for commonsense. What’s the difference between:

NSArray *foo=[NSArray arrayWithObjects:a,b,c];

and

NSArray *bar=[NSArray alloc] initWithObjects:a,b,c];

Should be pretty much the same thing, right?

As it stands, I populated NSArray/NSMutableArray with UIImage pointers. Trying to use any UIImage* from the resulting arrays consistently crashed the iPhone simulator using …arrayWithObjects:… .

From now on I will always use …alloc]initWithObjects:…
instead.

Don’t take my word for it, try this at home :)

Get Cocoa source code compiling and running on your iSomething in no more than 24 hours. Read this post.

I. Get a Mac, get a life

1) Register to the iPhone Developer program. it’s not free (~$100), but you won’t be able to put your app on the app store or even test on an actual device if you don’t.
Do this first, it may take up to 24 hours to complete. If your country doesn’t have an online apple store, consider getting remote help as offline registration may take up to 4 weeks to complete, including annoyances.

2) Get a Mac. If you don’t have a mac, you can’t do iPhone. Your mac should  be running Leopard 10.5 or higher. Before you head for the corner shop, download the iPhone/iTouch SDK. Avoid using a WiFi connection – download is about 1.2GB.
I got a ‘bigger’ Mac Mini for about $800. That works like a breeze. Rumour has it that the apple dev environment is a memory hog.

3) Install the SDK

4) Dump useless icons from the dock.

5) Download the MoveMe sample application

6) Start the apple dev environment, xcode and open the MoveMe project

7) Follow that tutorial (or just run the example)

II – wotf**k

You will use Objective-C and the Cocoa framework for development.
If you come frome Java, JavaScript, ActionScript, PHP, Python (or
like me, all of the above) you may experience some inconvenience.

The syntax of Objective-C will surprise you – different method calls, different
signatures, never mind using .h files.

There’s a couple of comprehensive tutorials I found useful:

If you haven’t done much programming, Objective-C is probably not the best place to start. On the other hand, stretching yourself a little might be rewarding as there are many things you can do with apple’s wysiwyg interface builder and not too much programming – either way, you’ve been warned.

I wrote a little note about how to send/view debug output in the console.

III – get your app running on your iPhone/iTouch

1) Follow this link and start the development provisioning assistant. Follow the steps… All this probably deserves an explanation but we’ll keep that for later

2) In xcode, set your environment (top left drop down) to iPhone Device 3.x (3.0, 3.1, …, or whatever works) and  hit Build & Run, then one of the following should occur:
- a. the app loads on your iPhone
- b. you get a quizzical exception
- c. you get a message suggesting the phone’s OS is too old.
At this stage, the easiest may be to upgrade the device OS. Obviously
it *should* be possible to build and run on an older target OS, but  for now, quickest and fastest. If you plugged your iPhone/iTouch, iTunes would normally offer to upgrade to the latest OS supported by your device.

That’s it. You’re now an iPhone developper :). Next time I’ll look into the Cocoa Touch MVC (Model View Controller) implementation and suggest ways you can design your application.