Best objective-c questions in January 2011

Common programming mistakes for Objective-C developers to avoid ?

26 votes

What are some common mistakes made by Objective-C developers, and how can we avoid them? I would really like to see common memory management mistakes, anti-patterns or any other mistakes on iPhone and OSX platforms as well.

Please justify your answer as well, if applicable and give examples.

If on the other hand you have best practices that is welcome too - it's just the opposite of my question so doing it wrong might answer my question :) ...

A lot of developers coming from Java and C# confuse properties with the instance variables that provide their storage. This results in complaints like "Why do I have to type my members three times?" when what they're doing each time is quite different. Furthermore, a lot of such developers wind up exposing a lot more state to clients of a class than they need to, because they think they need properties everywhere.

For example, they are used to accessing instance variables by dereferencing this, e.g. this.foo = bar;. One should not blindly follow this pattern in Objective-C. When you say self.foo = bar; what you are doing is invoking the -setFoo: method, not simply changing the foo instance variable.

A common idiom among experienced Objective-C developers is to name instance variables with a prefix or suffix different from any associated property. This makes it very clear to readers of code when you're working with an instance variable compared to a property (or even a local variable). For example:

@interface TimelineViewController : NSViewController {
@private
    NSCollectionView *_timelineView;
}
@property (readwrite, retain) IBOutlet NSCollectionView *timelineView;
@end

@implementation TimelineViewController
@synthesize timelineView = _timelineView; // associate property with ivar

- (void)dealloc {
    [_timelineView release]; // use ivar, not property, in -dealloc

    [super dealloc];
}
@end

What does Apple mean when they say that a NSManagedObjectContext is owned by the thread or queue that created it?

12 votes

It seems that in November, Apple updated both the NSManagedObjectContext Class Reference and the Core Data Programming Guide documents to explicitly bless serial GCD Dispatch Queues and NSOperationQueues as acceptable mechanisms for synchronising access to a NSManagedObjectContext. But their advice seems ambiguous and possibly contradictory, and I want to make sure I've understood it properly.

Previously the accepted wisdom seemed to be that a NSManagedObjectContext could only be accessed from the thread that created it, and that using a serial queue for synchronisation was not sufficient; although serial queues only perform one operation at a time, these operations can potentially be scheduled on different threads, and a MOC doesn't like that.

But now, from the programming guide, we have:

You can use threads, serial operation queues, or dispatch queues for concurrency. For the sake of conciseness, this article uses “thread” throughout to refer to any of these.

So far, so good (although their conflation of threads and queues is unhelpful). So I can safely use a single context per (serial) queue, instead of one per operation/block, right? Apple even has a visual depiction of this in the Core Data WWDC sessions.

But... where do you create the context for the queue? In the NSManagedObjectContext documentation, Apple state:

[A context] assumes the default owner is the thread or queue that allocated it—this is determined by the thread that calls its init method. You should not, therefore, initialize a context on one thread then pass it to a different thread.

So now we have the idea of a NSManagedObjectContext needing to know who its owner is. I'm assuming this means that the first operation to be executed in the queue should create the MOC and save a reference to it for the remaining operations to use.

Is this right? The only reason I'm hesitant is that the NSManagedObjectContext article goes on to say:

Instead, you should pass a reference to a persistent store coordinator and have the receiving thread/queue create a new context derived from that. If you use NSOperation, you must create the context in main (for a serial queue) or start (for a concurrent queue).

Apple now seem to be conflating operations with the queues that schedule their execution. This does my head in, and makes me wonder if they really do want you to just create a new MOC for every operation after all. What am I missing?

The NSManagedObjectContext and any managed objects associated with it should be pinned to a single actor (thread, serialized queue, NSOperationQueue with max concurrency = 1).

This pattern is called thread confinement or isolation. There isn't a great phrase for (thread || serialized queue || NSOperationQueue with max concurrency = 1) so the documentation goes on to say "we'll just use 'thread' for the remainder of the Core Data doc when we mean any of those 3 ways of getting a serialized control flow"

If you create a MOC on one thread, and then use it on another, you have violated thread confinement by exposing the MOC object reference to two threads. Simple. Don't do it. Don't cross the streams.

We call out NSOperation explicitly because unlike threads & GCD, it has this odd issue where -init runs on the thread creating the NSOperation but -main runs on the thread running the NSOperation. It makes sense if you squint at it right, but it is not intuitive. If you create your MOC in -[NSOperation init], then NSOperation will helpfully violate thread confinement before your -main method even runs and you're hosed.

We actively discourage / deprecated using MOCs and threads in any other ways. While theoretically possible to do what bbum mentions, no one ever got that right. Everybody tripped up, forgot a necessary call to -lock in 1 place, "init runs where ?", or otherwise out-clevered themselves. With autorelease pools and the application event loop and the undo manager and cocoa bindings and KVO there are just so many ways for one thread to hold on to a reference to a MOC after you've tried to pass it elsewhere. It is far more difficult than even advanced Cocoa developers imagine until they start debugging. So that's not a very useful API.

The documentation changed to clarify and emphasize the thread confinement pattern as the only sane way to go. You should consider trying to be extra fancy using -lock and -unlock on NSManagedObjectContext to be (a) impossible and (b) de facto deprecated. It's not literally deprecated because the code works as well as it ever did. But your code using it is wrong.

Some people created MOCs on 1 thread, and passed them to another without calling -lock. That was never legal. The thread that created the MOC has always been the default owner of the MOC. This became a more frequent issue for MOCs created on the main thread. Main thread MOCs interact with the application's main event loop for undo, memory management, and some other reasons. On 10.6 and iOS 3, MOCs take more aggressive advantage of being owned by the main thread.

Although queues are not bound to specific threads, if you create a MOC within the context of a queue the right things will happen. Your obligation is to follow the public API.

If the queue is serialized, you may share the MOC with succeeding blocks that run on that queue.

So do not expose an NSManagedObjectContext* to more than one thread (actor, etc) under any circumstance. There is one ambiguity. You may pass the NSNotification* from the didSave notification to another thread's MOC's -mergeChangesFromContextDidSaveNotification: method.

  • Ben

Can you use MacRuby to develop applications for the Mac App Store?

10 votes

I have some basic familiarity with Objective-C, but prefer Ruby, so I'm thinking of playing around with MacRuby. Is it possible to use MacRuby to develop applications for the Mac App Store, or do I have to use Objective-C? (Note that I don't care so much about iOS/the iPhone and iPad store right now, just the Mac OS X app store.)

This comes up sporadically; beyond the guidelines that @NSResponder mentioned, the reality is that you'll just be spending a lot of extra & unnecessary time if you decide to use MacRuby unless you plan on using some large chunk of already written Ruby code in your app.

Learning Objective-C is about 2% of the overall learning needed to be an effective Cocoa developer. The other 98% is everything from memory management model to standard idioms like KVC/KVO/delegation to NIB files to the myriad of classes & APIs throughout the system.

All of those things are written in Objective-C (or C) with documentation targeting Objective-C and examples written in Objective-C.

As well, the entire tool chain is focused on validating, compiling, indexing, debugging, and manipulating Objective-C.

If you want to use MacRuby to build a real Cocoa app, you are going to have to know Objective-C through and through anyway. And you are going to have to deal with whatever bugs and impedance mismatch is in the bridge layer, too.

Solving the NSInteger <-> NSNumber problem

10 votes

I've written a large social networking iPhone application, and one of the biggest issues I run into is the fact that NSInteger (and all the other NS-non-object types) are not first class citizens. This problem stems from the fact that, obviously, they have no representation for a nil value.

This creates two main problems:

  1. Tons of overhead and opaqueness to convert to and from NSNumber when storing/retrieving from a collection.
  2. Can't represent nil. Oftentimes, I want to be able to represent an "unset" value.

One way to solve this is to use NSNumber all the time, but that gets extremely confusing. In a User model object, I would have about 20 different NSNumbers, and no easy way to tell if each one is a float, integer, bool, etc.

So here are my thoughts for potential solutions and the pros/cons. I'm not really sold on any of them, so I thought I'd ask for feedback and/or alternative solutions to this problem.

  1. Continue to use NSInteger types, and just use NSIntegerMax to represent nil.
    PRO - Less memory overhead
    PRO - Clear typing
    CON - NSIntegerMax is not really nil. If programmers aren't careful or don't know this convention, invalid values could leak into the display layer.
    CON - Can't store them in a collection without conversions in and out

  2. Use NSNumber and designate types using hungarian notation (eg NSNumber fHeight, NSNumber iAge)
    PRO - First-class citizens
    PRO - Nil problem solved
    CON - Increased memory overhead
    CON - Lose compiler type checking
    CON - Hungarian notation is contentious

  3. Write my own first-class primitive object types (think Java http://developer.android.com/reference/java/lang/Integer.html)
    PRO - First-class citizens
    PRO - Nil problem solved
    PRO - Keeps compiler type checking
    PRO - Objects will be simpler than NSNumber. Internal storage will specific to data type.
    CON - Increased memory overhead
    CON - Sacrifices a bit of code portability and compatibility

Looking for a convincing argument in favor of one of these techniques, or one I haven't thought of if you've got one.


UPDATE

I've gone ahead and started an open source project (Apache 2.0), into which I'll be pulling a number of our internal classes as I have time. It currently includes object wrappers for some of the more common native data types (BOOL, CGFloat, NSInteger, NSUInteger). We chose to do this because it upgrades these data types to first class citizens with strict typing. Maybe you disagree with this approach, but it has worked well for us, so feel free to use it if you want.

I'm adding other classes we've found uses for, including a disk-backed LRU cache, a "Pair" object, a low memory release pool, etc.

Enjoy github - Zoosk/ZSFoundation

The most common convention for representing the idea of nil as an NSInteger is to use the NSNotFound value. This is, in fact, equal to NSIntegerMax, though it tends to be more obvious to the reader that this is a sentinel value representing the lack of a number. There are many cases where this is used throughout Cocoa. One common case is as the location field of an NSRange as a return value from -rangeOfString: et al.

Best architecture for an iOS application that makes many network requests?

9 votes

I'm in the process of rethinking my approach to the request architecture of a large app I'm developing. I'm currently using ASIHTTPRequest to actually make requests, but since I need many different types of requests as a result of many different actions taken in different view controllers, I'm trying to work out the best system of organizing these requests.

I'm currently building singleton "requesters" that are retained by the app delegate and sit around listening for NSNotifications that signal a request needs to be made; they make the request, listen for the response, and send out a new NSNotification with the response data. This solves most of my problems, but doesn't elegantly handle failed requests or simultaneous requests to the same singleton requester.

Anyone have any success devising a clear, OO architecture for making many different types of requests in an iOS app?

After having tried several approaches, this is one architecture that is giving me excellent results, is easy to document, understand, maintain and extend:

  • I have a single object taking care of network connectivity, let's call it a "network manager". Typically this object is a singleton (created using Matt Gallagher's Cocoa singleton macro).
  • Since you use ASIHTTPRequest (which I always do, wonderful API) I add an ASINetworkQueue ivar inside my network manager. I make the network manager the delegate of that queue.
  • I create subclasses of ASIHTTPRequest for each kind of network request that my app requires (typically, for each backend REST interaction or SOAP endpoint). This has another benefit (see below for details :)
  • Every time one of my controllers requires some data (refresh, viewDidAppear, etc), the network manager creates an instance of the required ASIHTTPRequest subclass, and then adds it to the queue.
  • The ASINetworkQueue takes care of bandwidth issues (depending on whether you are on 3G, EDGE or GPRS or Wifi, you have more bandwidth, and you can process more requests, etc). This is done by the queue, which is cool (at least, that's one of the things I understand this queue does, I hope I'm not mistaken :).
  • Whenever a request finishes or fails, the network manager is called (remember, the network manager is the queue's delegate).
  • The network manager doesn't know squat about what to do with the result of each request; hence, it just calls a method on the request! Remember, requests are subclasses of ASIHTTPRequest, so you can just put the code that manages the result of the request (typically, deserialization of JSON or XML into real objects, triggering other network connections, updating Core Data stores, etc). Putting the code into each separate request subclass, using a polymorphic method with a common name accross request classes, makes it very easy to debug and manage IMHO.
  • Finally, I notify the controllers above about interesting events using notifications; using a delegate protocol is not a good idea, because in your app you typically have many controllers talking to your network manager, and then notifications are more flexible (you can have several controllers responding to the same notification, etc).

Anyway, this is how I've been doing it for a while, and frankly it works pretty well. I can extend the system horizontally, adding more ASIHTTPRequest subclasses as I need them, and the core of the network manager stays intact.

Hope it helps!

9 votes

Hi all,

I've recently been developing an app that processes a large amount of data very frequently (~15 times a minute). To do so, I allocated a large chunk of memory, then freed it for each batch of data.

Here's a screen of my Memory Allocations from Instruments: The memory

The Memory usage oscillates from about 3MB to about 30MB pretty quickly. I was just wondering, is this "healthy," per se for the iPhone.

Is it risky to allocate and free so much memory so quickly? Is it unsustainable, or just bad practice?

Thanks!

It is neither risky nor necessarily bad practice. Allocating and freeing memory takes time, so doing it very frequently vs. doing it once and re-using the allocated memory is a trade-off between memory usage effiency (using the lowest amount of memory at every single moment) and performance.

If the performance of your app doesn't suffer at the moment, you have probably made the correct choice regarding this tradeoff for your app.

Generally speaking, using 30 MB of memory is quite a large amount for older devices (iPhone 3G and older). You cannot be sure that your app has that much memory available so be prepared to received memory warnings. If your app cannot reduce its memory usage when it receives a memory warning, the OS might kill it.

collision detection in cocos2d.

8 votes

i want to detect collision detection two times in same row.

for example:-(see the below image)

the ellipse and rectangle or detcted. after that my ellipse will travelling in the straight line path to down and detect the another rectangle.

first one is( travelled in trajectory path ) working fine. second one i want to pass in straight line to down for collision detection.

how to do this process.

alt text

As i know cocos2d have now collision detection of sprites because it's not a phys engine. If you want the collision be detected automatically use Box2D or chipmunk physics engine, supported by cocos2d.

If the number of object you want to check for collision is small you can just run over your object and check if some of them (or only one if it's enough for you) overlaps with the others.

Making more complex collision detection will bring you for writing a collision detection part of a physics engine. It's much simpler to use en existing one

When to use -retainCount ?

8 votes

I would like to know in what situation did you use -retainCount so far, and eventually the problems that can happen using it.

Thanks.

You should never use -retainCount, because it never tells you anything useful. The implementation of the Foundation and AppKit/UIKit frameworks is opaque; you don't know what's being retained, why it's being retained, who's retaining it, when it was retained, and so on.

For example:

  • You'd think that [NSNumber numberWithInt:1] would have a retainCount of 1. It doesn't. It's 2.
  • You'd think that @"Foo" would have a retainCount of 1. It doesn't. It's 1152921504606846975.
  • You'd think that [NSString stringWithString:@"Foo"] would have a retainCount of 1. It doesn't. Again, it's 1152921504606846975.

Basically, since anything can retain an object (and therefore alter its retainCount), and since you don't have the source to most of the code that runs an application, an object's retainCount is meaningless.

If you're trying to track down why an object isn't getting deallocated, use the Leaks tool in Instruments. If you're trying to track down why an object was deallocated too soon, use the Zombies tool in Instruments.

But don't use -retainCount. It's a truly worthless method.

edit

Please everyone go to http://bugreport.apple.com and request that -retainCount be deprecated. The more people that ask for it, the better.

8 votes

I'm trying to make a bunch of buttons behave somewhat like float:left in CSS. So whenever the view changes size, on orientation change for example, the buttons should adjust so they fit within their container view.

In landscape mode, this UIScrollView should scroll horizontally, in portrait mode, it should scroll vertically.

I'm trying to make an ScrollView similar to the "Featured" tab in the iPad YouTube app. Landscape has 4 columns, portrait: 3 columns, "Subscriptions" tab, portrait, the same view has 2 columns.

alt text

I have implemented a layout system to do things like this. There is a demo project on how to use it checked into that repository. I'd be happy to answer any questions about it. This is much more lightweight than AQGridView, so if you don't need the extra functionality he is providing, I would recommend an approach similar to mine.

LLVM vs. GCC for iOS development

7 votes

In latest iOS SDK, Apple provides three compiler options: GCC, LLVM with Clang and LLVM-GCC. I understand more or less what these 3 mean, what LLVM and Clang are, and so on. What I don't know is what this means in practice for iPhone developers. Which of these should I use at this point, as of January 2011? Is LLVM mature enough that I can use it safely without stumbling on bugs in it too often? Does switching to LLVM have any other disadvantages? If it does, then does the speed advantage outweigh them? Are there any other reasons to switch except speed?

Ok, I think none of the answers below tell the whole story, so here's my take on a response to my question:

  • LLVM compiles code faster than GCC, may create code that runs faster, and the Clang frontend provides more accurate error messages than GCC – so there are definitely reasons for switching;

  • that said, the version provided with latest stable Xcode (LLVM 1.6) is not 100% stable yet, you can run into some minor bugs if you're unlucky. So if you want to be safe, you should either compile the latest LLVM (2.0) from source, or stick to GCC for the next few months;

  • in a few months, probably when Apple releases Xcode 4, LLVM 2.0 will be the version that ships with Xcode by default, and then we should all be able to switch to it safely.

Thanks to everyone who responded, feel free to correct me if I got something wrong.

bounding a sprite in cocos2d

7 votes

am making a canon to fire objects. back of the canon the plunger is attached. plunger acts for set speed and angle. canon rotates 0-90 degree and plunger moves front and back for adjust speed. when am rotates the canon by touches moved its working fine. when plunger is pull back by touches moved and it rotates means the plunger is bounds outside of the canon.

how to control this:-

my code for plunger and canon rotation on touches moved. ( para3 is the canon , para6 is my plunger):-

    CGPoint touchLocation = [self convertTouchToNodeSpace:touch];
    CGPoint oldTouchLocation = [touch previousLocationInView:touch.view];
    oldTouchLocation = [[CCDirector sharedDirector] convertToGL:oldTouchLocation];
    oldTouchLocation = [self convertToNodeSpace:oldTouchLocation];
    if (CGRectContainsPoint(CGRectMake(para6.position.x-para6.contentSize.width/2, para6.position.y-para6.contentSize.height/2, para6.contentSize.width, para6.contentSize.height), touchLocation) && (touchLocation.y-oldTouchLocation.y == 0))
    {
        CGPoint diff = ccpSub(touchLocation, oldTouchLocation);
        CGPoint currentpos = [para6 position];
        NSLog(@"%d",currentpos);
        CGPoint destination = ccpAdd(currentpos, diff);
        if (destination.x < 90 && destination.x >70)
        {
            [para6 setPosition:destination];
            speed = (70 + (90-destination.x))*3.5 ;
        }

    }
if(CGRectIntersectsRect((CGRectMake(para6.position.x-para6.contentSize.width/8, (para6.position.y+30)-para6.contentSize.height/10, para6.contentSize.width, para6.contentSize.height/10)),(CGRectMake(para3.position.x-para3.contentSize.width/2, para3.position.y-para3.contentSize.height/2, para3.contentSize.width, para3.contentSize.height))))

            {

        [para3 runAction:[CCSequence actions:
                          [CCRotateTo actionWithDuration:rotateDuration angle:rotateDiff],
                          nil]];
        CGFloat plungrot = (rotateDiff);
        CCRotateTo *rot = [CCRotateTo actionWithDuration:rotateDuration angle:plungrot];
        [para6 runAction:rot];
    }
}

how about u do this that you use the [CCMoveTo actionWithDuration: position:] method??

Through this method you can easily control the speed by the "actionWithDuration" argument which takes integer values of time in seconds, while direction can be adjusted through "position" argument which takes ccp(x,y) as the values to the point you want your plunger to move to.

You can use it like this....

    CCSprite *plunger = [[CCSprite alloc] initWithFile:@"plunger.png"];
    plunger.position = ccp(240,240);
    [self addChild:plunger z:10];
    [plunger release];

    id = [CCMoveTo actionWithDuration:3 position:ccp(300,240)];

The values given are of my choice. You may use them to your accordance.

Hope it helps you....

Is it possible to port an iOS app to Windows using GNUstep? (for learning purposes)

7 votes

I am an Objective-C newbie, who still does not have a Mac, but still I want to practice the language. I heard that in the non-Mac world, GNUstep offers a good alternative to Cocoa, and can be used as a lerning tool for new objective-c developers. My question is, since GNUstep ports a lot of the Cocoa classes, what are the chances of me porting an iOS game + its development framework. I am talking about Canabalt for iOS (https://github.com/ericjohnson/canabalt-ios) which is based on the iOS version of the Flixel framework. I would like to know whether there is even the slightest chance of being able to port & run this game on windows using GNUstep. Remember, that this is entirely for educational purposes, so please, do not look for any practical value in it, besides me getting better with ObjC :)

I guess that it should be technically possible. In general, what are the chances of porting any iOS app to Win using GNUstep?

I think you'd be better off porting a Mac application than an iOS application. While the iOS frameworks (UIKit mostly) are close to those of Mac OS, they are still quite different. Beyond the code differences there are HUGE paradigm shifts between iOS and Mac, in the limited screen space and the lack of a mouse.

GNUstep is close to Cocoa, so you'd be able to port a Mac app with a bit of work, but not so much an iOS app.

Detecting if iOS app is run in debugger

7 votes

I set up my application to either send debugging output to console or a log file. Now, I'd like to decide with in the code whether

  • it is run in the debugger (or simulator) and have thus a console window where I would like to read the output directly or if
  • there is no console window and thus, the output should be redirected to a file.

Is there a way to determine if the app runs in the debugger?

There's a function from Apple to detect wether a Mac program is being debugged. It seems it works just fine on iOS as well, have just tested it on an iPad with iOS 4.2.

crash log in device

6 votes

I need help to understand the crash log. The app works fine on my simulator but it crashes in device.

my simulator is run with:-iPhone Simulator 225, iPhone OS 4.1 (iPhone 4/8B5091b)

this is my device crash log:-

Incident Identifier: CD0E8B93-5CF9-402C-9787-4B175C51A690
CrashReporter Key:   1961913be3204fe8cb5a39c1e00ac0f03a452876
Hardware Model:      iPhone1,2
Process:         My Game[1115]
Path:            /var/mobile/Applications/2968E5FB-96DD-443D-B386-D68F08E9345E/My Game.app/My Game
Identifier:      My Game
Version:         ??? (???)
Code Type:       ARM (Native)
Parent Process:  launchd [1]

Date/Time:       2010-12-29 23:39:15.753 -0500
OS Version:      **iPhone OS 4.2.1 (8C148)**
Report Version:  104

Exception Type:  EXC_CRASH (SIGABRT)
Exception Codes: 0x00000000, 0x00000000
Crashed Thread:  0

Thread 0 Crashed:
0   libSystem.B.dylib              0x35de3ad0 0x35d5a000 + 563920
1   libSystem.B.dylib              0x35de3abe 0x35d5a000 + 563902
2   libSystem.B.dylib              0x35de3ab2 0x35d5a000 + 563890
3   libSystem.B.dylib              0x35dfad5e 0x35d5a000 + 658782
4   libstdc++.6.dylib              0x374f2a00 0x3748d000 + 416256
5   libobjc.A.dylib                0x32d9d8d8 0x32d95000 + 35032
6   libstdc++.6.dylib              0x374f0100 0x3748d000 + 405760
7   libstdc++.6.dylib              0x374f0178 0x3748d000 + 405880
8   libstdc++.6.dylib              0x374f02a0 0x3748d000 + 406176
9   libobjc.A.dylib                0x32d9bf28 0x32d95000 + 28456
10  CoreFoundation                 0x3759dabc 0x374f9000 + 674492
11  Foundation                     0x351a3e6c 0x35151000 + 339564
12  My Game               0x0006325c 0x1000 + 402012
13  My Game               0x00003c98 0x1000 + 11416
14  My Game               0x00062108 0x1000 + 397576
15  My Game               0x00003b08 0x1000 + 11016
16  My Game               0x000074d8 0x1000 + 25816
17  CoreFoundation                 0x375466fc 0x374f9000 + 317180
18  CoreFoundation                 0x375465d6 0x374f9000 + 316886
19  My Game               0x0005c818 0x1000 + 374808
20  My Game               0x000596a4 0x1000 + 362148
21  CoreFoundation                 0x37542a3c 0x374f9000 + 301628
22  My Game               0x000b692c 0x1000 + 743724
23  My Game               0x000b7550 0x1000 + 746832
24  My Game               0x000c2a7c 0x1000 + 793212
25  UIKit                          0x358f4ea8 0x358d3000 + 138920
26  UIKit                          0x358f44dc 0x358d3000 + 136412
27  UIKit                          0x358d7c94 0x358d3000 + 19604
28  UIKit                          0x358d73ac 0x358d3000 + 17324
29  GraphicsServices               0x33e77c80 0x33e72000 + 23680
30  CoreFoundation                 0x3752f5c4 0x374f9000 + 222660
31  CoreFoundation                 0x3752f582 0x374f9000 + 222594
32  CoreFoundation                 0x3752182e 0x374f9000 + 165934
33  CoreFoundation                 0x37521504 0x374f9000 + 165124
34  CoreFoundation                 0x37521412 0x374f9000 + 164882
35  GraphicsServices               0x33e76d1c 0x33e72000 + 19740
36  UIKit                          0x3591d574 0x358d3000 + 304500
37  UIKit                          0x3591a550 0x358d3000 + 292176
38  My Game               0x000030a4 0x1000 + 8356
39  My Game               0x00003010 0x1000 + 8208

Thread 1:
0   libSystem.B.dylib              0x35d8f974 0x35d5a000 + 219508
1   libSystem.B.dylib              0x35e5e2fc 0x35d5a000 + 1065724
2   libSystem.B.dylib              0x35e5dd68 0x35d5a000 + 1064296
3   libSystem.B.dylib              0x35e5d788 0x35d5a000 + 1062792
4   libSystem.B.dylib              0x35de6970 0x35d5a000 + 575856
5   libSystem.B.dylib              0x35ddd2fc 0x35d5a000 + 537340

Thread 2:
0   libSystem.B.dylib              0x35d5b3b0 0x35d5a000 + 5040
1   libSystem.B.dylib              0x35d5d894 0x35d5a000 + 14484
2   CoreFoundation                 0x37521f7c 0x374f9000 + 167804
3   CoreFoundation                 0x37521780 0x374f9000 + 165760
4   CoreFoundation                 0x37521504 0x374f9000 + 165124
5   CoreFoundation                 0x37521412 0x374f9000 + 164882
6   WebCore                        0x3318bd14 0x33070000 + 1162516
7   libSystem.B.dylib              0x35de5b44 0x35d5a000 + 572228
8   libSystem.B.dylib              0x35dd77a4 0x35d5a000 + 513956

Thread 0 crashed with ARM Thread State:
    r0: 0x00000000    r1: 0x00000000      r2: 0x00000001      r3: 0x3e74f308
    r4: 0x00000006    r5: 0x00238cfc      r6: 0x00238ff0      r7: 0x2fdfdd2c
    r8: 0x3eba21b8    r9: 0x0000000a     r10: 0x3eba21bc     r11: 0x0022fb00
    ip: 0x00000025    sp: 0x2fdfdd2c      lr: 0x35de3ac5      pc: 0x35de3ad0
  cpsr: 0x000a0010

Binary Images:
    0x1000 -    0xebfff +My Gamearmv6  <15bbbead83159dac341a987c660d2b28> /var/mobile/Applications/2968E5FB-96DD-443D-B386-D68F08E9345E/My Game.app/My Game
  0x1f8000 -   0x1f9fff  dns.so armv6  <88b569311cca4a9593b2d670051860d1> /usr/lib/info/dns.so
0x2fe00000 - 0x2fe29fff  dyld armv6  <617f6daf4103547c47a8407a2e0b90de> /usr/lib/dyld
0x30229000 - 0x30268fff  MBXGLEngine armv6  <9d60c44b1ddc55387a0cb77f90660b37> /System/Library/Frameworks/OpenGLES.framework/MBXGLEngine.bundle/MBXGLEngine
0x3027c000 - 0x3027efff  IOMobileFramebuffer armv6  <f42bbbf67195a7b98d67ad021bba4784> /System/Library/PrivateFrameworks/IOMobileFramebuffer.framework/IOMobileFramebuffer
0x3027f000 - 0x3038dfff  CFNetwork armv6  <d6eeee83216ee9c553134f069f37cbc2> /System/Library/Frameworks/CFNetwork.framework/CFNetwork
0x303ef000 - 0x303f4fff  CaptiveNetwork armv6  <f41df4b358b77b29ff85e0eaea88ee1d> /System/Library/PrivateFrameworks/CaptiveNetwork.framework/CaptiveNetwork
0x303f5000 - 0x30444fff  Security armv6  <cf625b4dc7ea928891313444ef64a7cb> /System/Library/Frameworks/Security.framework/Security
0x30445000 - 0x3055cfff  libicucore.A.dylib armv6  <8968ff3f62d7780bb1bd75026a7628d0> /usr/lib/libicucore.A.dylib
0x3055d000 - 0x30561fff  ApplePushService armv6  <0560b630d26e261e205fc58942e1885c> /System/Library/PrivateFrameworks/ApplePushService.framework/ApplePushService
0x3059d000 - 0x305a8fff  MobileWiFi armv6  <c7532e63e083a1dd2a0ef7352b85749d> /System/Library/PrivateFrameworks/MobileWiFi.framework/MobileWiFi
0x305aa000 - 0x30612fff  libvDSP.dylib armv6  <9d264733fc675943c082bd3b9b567b59> /System/Library/Frameworks/Accelerate.framework/Frameworks/vecLib.framework/libvDSP.dylib
0x30613000 - 0x3064dfff  MobileCoreServices armv6  <beb473ce80390554bb4af21554522286> /System/Library/Frameworks/MobileCoreServices.framework/MobileCoreServices
0x3065c000 - 0x3066efff  libbsm.0.dylib armv6  <51e7bb18da9afa44a33e54e42fbd0707> /usr/lib/libbsm.0.dylib
0x3066f000 - 0x306c6fff  CoreMedia armv6  <cd5e9398c161f129146931e888e1c92e> /System/Library/Frameworks/CoreMedia.framework/CoreMedia
0x306f0000 - 0x306fefff  libz.1.dylib armv6  <84592e96bae1a661374b0f9a5d03a3a0> /usr/lib/libz.1.dylib
0x306ff000 - 0x30729fff  PrintKit armv6  <74f9710fa01a33b5bb04c4aeabd6be7d> /System/Library/PrivateFrameworks/PrintKit.framework/PrintKit
0x3072e000 - 0x307d0fff  AVFoundation armv6  <da9d96f32791f51ecb439c5eaeeff59a> /System/Library/Frameworks/AVFoundation.framework/AVFoundation
0x307d7000 - 0x3082afff  IOKit armv6  <20da5e822f21a8d0a7c5b3e149330efd> /System/Library/Frameworks/IOKit.framework/Versions/A/IOKit
0x30831000 - 0x3083bfff  AccountSettings armv6  <eca67ab04f724e1fa7c6406c88e75433> /System/Library/PrivateFrameworks/AccountSettings.framework/AccountSettings
0x30a04000 - 0x30aa3fff  ProofReader armv6  <2734920b62f174c17aeeb15f371615ef> /System/Library/PrivateFrameworks/ProofReader.framework/ProofReader
0x30ad6000 - 0x30b1afff  AddressBook armv6  <1f30c3370dad27331a491ba4b190813c> /System/Library/Frameworks/AddressBook.framework/AddressBook
0x30b3d000 - 0x30b9cfff  CoreAudio armv6  <ccc4bace0d6eca79a32ed84d566f72e9> /System/Library/Frameworks/CoreAudio.framework/CoreAudio
0x32d7d000 - 0x32d89fff  libkxld.dylib armv6  <f74f359de7bbe3ccdc37fa6f332aebf4> /usr/lib/system/libkxld.dylib
0x32d95000 - 0x32e5cfff  libobjc.A.dylib armv6  <429841269f8bcecd4ba3264a8725dad6> /usr/lib/libobjc.A.dylib
0x32e5d000 - 0x32ecdfff  libsqlite3.dylib armv6  <87b9bb47687902d9120d03d1da9eb9fc> /usr/lib/libsqlite3.dylib
0x32f0c000 - 0x32f1ffff  libmis.dylib armv6  <dba9c086b49bd9540930ff27211570d6> /usr/lib/libmis.dylib
0x33055000 - 0x33061fff  SpringBoardServices armv6  <fd0c472436b3306f5b56118c93c8a423> /System/Library/PrivateFrameworks/SpringBoardServices.framework/SpringBoardServices
0x33062000 - 0x3306ffff  MobileBluetooth armv6  <2b68516e1321011a4efbee2947d463c6> /System/Library/PrivateFrameworks/MobileBluetooth.framework/MobileBluetooth
0x33070000 - 0x338bffff  WebCore armv6  <aa3b6827f051da7a3494c9bee4ebe290> /System/Library/PrivateFrameworks/WebCore.framework/WebCore
0x33ab4000 - 0x33ab4fff  Accelerate armv6  <cdde24a7ad004b2b2e600cd4f3ac5eb7> /System/Library/Frameworks/Accelerate.framework/Accelerate
0x33bbc000 - 0x33c0afff  CoreText armv6  <16c9582fdffb598178287c6ce9fd6897> /System/Library/Frameworks/CoreText.framework/CoreText
0x33c16000 - 0x33d73fff  libGLProgrammability.dylib armv6  <aec6b54ffd532bb607aab4acbab679b6> /System/Library/Frameworks/OpenGLES.framework/libGLProgrammability.dylib
0x33d85000 - 0x33e71fff  QuartzCore armv6  <77cd91ff21fe6c58c309f2c82eb95ca5> /System/Library/Frameworks/QuartzCore.framework/QuartzCore
0x33e72000 - 0x33e81fff  GraphicsServices armv6  <af20aba0ec96e7b7c42bb55ac763c784> /System/Library/PrivateFrameworks/GraphicsServices.framework/GraphicsServices
0x33ead000 - 0x33f6efff  ImageIO armv6  <0c1b6f466667ff345f2399d8142a9d10> /System/Library/Frameworks/ImageIO.framework/ImageIO
0x33f78000 - 0x33f79fff  CoreSurface armv6  <5e290514380c626e9b0f9f9985b9dc7a> /System/Library/PrivateFrameworks/CoreSurface.framework/CoreSurface
0x34137000 - 0x34156fff  EAP8021X armv6  <fa56845b5396c3ebb368c2368331643c> /System/Library/PrivateFrameworks/EAP8021X.framework/EAP8021X
0x343a0000 - 0x343bffff  Bom armv6  <f41bef81e23e2bff59155e5ce46762d3> /System/Library/PrivateFrameworks/Bom.framework/Bom
0x343c0000 - 0x344bdfff  JavaScriptCore armv6  <3547c92c1efc0522b087e7f10eba7728> /System/Library/PrivateFrameworks/JavaScriptCore.framework/JavaScriptCore
0x344be000 - 0x34500fff  ManagedConfiguration armv6  <397723a33c19c3487d304d69580acbfc> /System/Library/PrivateFrameworks/ManagedConfiguration.framework/ManagedConfiguration
0x34b52000 - 0x34f1ffff  libLAPACK.dylib armv6  <0eb734c91165416224b98c943ff6476b> /System/Library/Frameworks/Accelerate.framework/Frameworks/vecLib.framework/libLAPACK.dylib
0x34f20000 - 0x35014fff  libiconv.2.dylib armv6  <01916d6784f4de8f3746978faae9c5fa> /usr/lib/libiconv.2.dylib
0x35015000 - 0x35022fff  CoreVideo armv6  <7b100fd5fdf98db1cd0f0649e7f6f316> /System/Library/Frameworks/CoreVideo.framework/CoreVideo
0x35151000 - 0x35272fff  Foundation armv6  <6bdeb19a1fcb93e2930dadb50416f881> /System/Library/Frameworks/Foundation.framework/Foundation
0x3529b000 - 0x352a6fff  libbz2.1.0.dylib armv6  <6aa8a4ed0906a495d059ace9125f525d> /usr/lib/libbz2.1.0.dylib
0x352dc000 - 0x35342fff  libBLAS.dylib armv6  <11a3677a08175a30df1b3d66d7e0951a> /System/Library/Frameworks/Accelerate.framework/Frameworks/vecLib.framework/libBLAS.dylib
0x35406000 - 0x35406fff  vecLib armv6  <8f914b3e8a581d49fb21d2c0ff75be03> /System/Library/Frameworks/Accelerate.framework/Frameworks/vecLib.framework/vecLib
0x35407000 - 0x3540afff  MobileInstallation armv6  <456ed7fe6dd9fcd8e78df425085b1452> /System/Library/PrivateFrameworks/MobileInstallation.framework/MobileInstallation
0x354be000 - 0x354dcfff  OpenAL armv6  <e86dc71ad650db8a13e4785e9c35a4b9> /System/Library/Frameworks/OpenAL.framework/OpenAL
0x35541000 - 0x35547fff  MBX2D armv6  <fad4955cab36e0179df6f8f27d365b8f> /System/Library/PrivateFrameworks/MBX2D.framework/MBX2D
0x35815000 - 0x3581afff  AssetsLibraryServices armv6  <224b3cf992a01814f91481244e3213eb> /System/Library/PrivateFrameworks/AssetsLibraryServices.framework/AssetsLibraryServices
0x3581b000 - 0x35877fff  libGLImage.dylib armv6  <7c1049f20c4e64591c09d3ac00c7d3ab> /System/Library/Frameworks/OpenGLES.framework/libGLImage.dylib
0x358bc000 - 0x358c3fff  liblockdown.dylib armv6  <f470dea180ddf23886df75eb256d3888> /usr/lib/liblockdown.dylib
0x358cc000 - 0x358cffff  libgcc_s.1.dylib armv6  <bed95ed187350ce27d22ed241ef892ea> /usr/lib/libgcc_s.1.dylib
0x358d3000 - 0x35d4ffff  UIKit armv6  <14ec6c926b8bda71b73136f6e1a6ac1b> /System/Library/Frameworks/UIKit.framework/UIKit
0x35d5a000 - 0x35e98fff  libSystem.B.dylib armv6  <70571c1e697e2ae7f7a9b1a499453bb6> /usr/lib/libSystem.B.dylib
0x35f7e000 - 0x35fc2fff  VideoToolbox armv6  <101dbbcd34cc3231a8be3fd6392556aa> /System/Library/PrivateFrameworks/VideoToolbox.framework/VideoToolbox
0x35fdb000 - 0x36162fff  CoreGraphics armv6  <9a1d72fa9549d83abc1e735ba37a4dc2> /System/Library/Frameworks/CoreGraphics.framework/CoreGraphics
0x36179000 - 0x36255fff  WebKit armv6  <83da207070be989ba81dba3a83d5206a> /System/Library/PrivateFrameworks/WebKit.framework/WebKit
0x36269000 - 0x36278fff  OpenGLES armv6  <37eda5ddcff210dd321157da35a87a5e> /System/Library/Frameworks/OpenGLES.framework/OpenGLES
0x363f3000 - 0x363f9fff  MobileKeyBag armv6  <2d83bf6a43bab972d77a1a6e0f3b03d2> /System/Library/PrivateFrameworks/MobileKeyBag.framework/MobileKeyBag
0x365db000 - 0x365f9fff  libresolv.9.dylib armv6  <9c94634beea733e754dc115737b6e63c> /usr/lib/libresolv.9.dylib
0x36746000 - 0x3683cfff  libxml2.2.dylib armv6  <9c44d05cc67f1ebabd795903e581724e> /usr/lib/libxml2.2.dylib
0x3683e000 - 0x36888fff  libCGFreetype.A.dylib armv6  <cfc94cfa17958f2f94c9eff208a7dace> /System/Library/Frameworks/CoreGraphics.framework/Resources/libCGFreetype.A.dylib
0x3694c000 - 0x3694ffff  libAccessibility.dylib armv6  <74e0f77cc276a9412be268c795fdcbca> /usr/lib/libAccessibility.dylib
0x36955000 - 0x36a1ffff  Celestial armv6  <11172a6ee53bdf067548cd4496bc5fe0> /System/Library/PrivateFrameworks/Celestial.framework/Celestial
0x36a2d000 - 0x36a30fff  CrashReporterSupport armv6  <00bc60f690e6328b64e7a7b718edf45a> /System/Library/PrivateFrameworks/CrashReporterSupport.framework/CrashReporterSupport
0x36a31000 - 0x36a74fff  CoreTelephony armv6  <cabbce0fa7630065dc7e7d3ca3bc616c> /System/Library/Frameworks/CoreTelephony.framework/CoreTelephony
0x36c1c000 - 0x36c26fff  AggregateDictionary armv6  <f7429444c955e4f13c6761d20032ab52> /System/Library/PrivateFrameworks/AggregateDictionary.framework/AggregateDictionary
0x36c2b000 - 0x36de1fff  AudioToolbox armv6  <bb65e8ed531fe5923eb8ac00a7c0d87d> /System/Library/Frameworks/AudioToolbox.framework/AudioToolbox
0x36de2000 - 0x36e16fff  AppSupport armv6  <783e14db9585fd063c0c2a755cd121b6> /System/Library/PrivateFrameworks/AppSupport.framework/AppSupport
0x36e17000 - 0x36e2dfff  PersistentConnection armv6  <006723906b8ac250c1681a1821fbe94d> /System/Library/PrivateFrameworks/PersistentConnection.framework/PersistentConnection
0x37141000 - 0x37184fff  SystemConfiguration armv6  <207f362e707871e74a292cfd1ea7893d> /System/Library/Frameworks/SystemConfiguration.framework/SystemConfiguration
0x372aa000 - 0x37477fff  MediaToolbox armv6  <21ceabd0e5de17ad4e883c85fcd34d51> /System/Library/PrivateFrameworks/MediaToolbox.framework/MediaToolbox
0x37478000 - 0x3747dfff  IOSurface armv6  <ffd66ca04dfe7d382d6961f0df3839ff> /System/Library/PrivateFrameworks/IOSurface.framework/IOSurface
0x3748d000 - 0x374f8fff  libstdc++.6.dylib armv6  <eccd1d7183e73587b2c0aa5755a19c39> /usr/lib/libstdc++.6.dylib
0x374f9000 - 0x375e4fff  CoreFoundation armv6  <ab0eac0ddd5b4ae1bf8541116e3c0bd1> /System/Library/Frameworks/CoreFoundation.framework/CoreFoundation
0x3760a000 - 0x3760bfff  DataMigration armv6  <d2de7c0db77278484236669c2cdccabb> /System/Library/PrivateFrameworks/DataMigration.framework/DataMigration
0x37731000 - 0x37736fff  libGFXShared.dylib armv6  <bd1c480607cc286288db1ca1aec64180> /System/Library/Frameworks/OpenGLES.framework/libGFXShared.dylib
0x377f6000 - 0x37817fff  libRIP.A.dylib armv6  <22c6da37f3adf325f99c3a0494e04c02> /System/Library/Frameworks/CoreGraphics.fram

You need the dSYM file that Xcode creates during the build to symbolicate the crash log. Read this article for more information.

Another way is using the "atos" command line command. For example I have a crash in my app "MyApp" and there is a line in the crash file saying: "2 MyAPP 0x00041fd4 0x1000 + 266196". After using the atos command with

atos -arch arm -o ~/workspace/svn/MyApp/build/Distribute-Adhoc-iphoneos/MyApp.app.dSYM/Contents/Resources/DWARF/MyApp 0x00041fd4

it will return:-[PushSMSTableViewController tableView:cellForRowAtIndexPath:] (in MyApp) (PushSMSTableViewController.m:93)

You had to replace the path to the dSYM file with your own. The subpath to the DWARF folder in the dSYM file is always available.

Be aware that you use the corresponding dSYM file to you crashed app. If you are using the wrong dSYM file you will get the wrong line number.

6 votes

I'm attempting to narrow down a bug to a minimum reproducible case and found something odd.

Consider this code:

static NSString *staticString = nil;
int main (int argc, const char * argv[]) {
    NSAutoreleasePool * pool = [[NSAutoreleasePool alloc] init];

    if (staticString == nil) {
        staticString = [[NSArray arrayWithObjects:@"1", @"2", @"3", nil] componentsJoinedByString:@","];
    }   

    [pool drain];

    NSLog(@"static: %@", staticString);
    return 0;
}

I'm expecting this code to crash. Instead, it logs:

2011-01-18 14:41:06.311 EmptyFoundation[61419:a0f] static: static: 

However, if I change the NSLog() to:

NSLog(@"static: %s", [staticString UTF8String]);

Then it does crash.

edit a bit more info:

After draining the pool:

NSLog(@"static: %@", staticString);  //this logs "static: static: "
NSLog(@"static: %@", [staticString description]); //this crashes

So apparently invoking a method on the string is good enough to get it to crash. In that case, why doesn't logging the string directly cause it to crash? Shouldn't NSLog() be invoking the -description method?

Where is the second "static: " coming from? Why isn't this crashing?


Results:

Both Kevin Ballard and Graham Lee are correct. Graham's correct in realizing that NSLog() is not invoking -description (as I was erroneously assuming), and Kevin is almost definitely correct that this is a weird stack-related issue with copying a format string and a va_list around.

  1. NSLogging and NSString does not invoke -description. Graham elegantly showed this, and if you trace through the Core Foundation sources that do the logging, you'll see that this is the case. Any backtrace originating inside NSLog shows that it invokes NSLogv => _CFLogvEx => _CFStringCreateWithFormatAndArgumentsAux => _CFStringAppendFormatAndArgumentsAux. _CFStringAppendFormatAndArgumentsAux() (line 5365) is where all of the magic is going on. You can see that it's manually going through to find all the % substitutions. It only ends up invoking the description copy function if the type of the substitution is a CFFormatObjectType, the description function is non-nil, and the substitution hasn't already been handled by another type. Since we've shown that the description is not getting copied, it's reasonable to assume that an NSString gets handled earlier (in which case it's probably going to be doing a raw byte copy), which leads us to believe...
  2. There's a stack error going on here, as Kevin surmises. Somehow the pointer that was pointing to the autoreleased string is getting substituted to a different object, which happens to be an NSString. So, it doesn't crash. Weird. However, if we change the type of the static variable to something else, like an NSArray, then the -description method does get called, and the program does crash as expected.

How truly and utterly strange. Points go to Kevin for being the most correct about the root cause of the behavior, and kudos to Graham for correcting my fallacious thinking. I wish I could accept two answers...

My best guess for what you're seeing is that NSLog() copies the format string (probably as a mutable copy), and then parses the arguments. Since you've dealloc'd staticString, it just so happens that the copy of the format string is being placed into the same location. This is causing you to see the "static: static: " output that you described. Of course, this behavior is undefined - there's no guarantee it will always use the same memory location for this.

On the other hand, your NSLog(@"static: %s", [staticString UTF8String]) is accessing staticString before the format string copy happens, which means it's accessing garbage memory.

NSFetchedResultsController - KVO, UITableView and a "Tree"

6 votes

I'm using a NSFetchedResultsController to implement KVO for my UITableView (which is obvious). What I can't figure out is, how to use multiple Entities - sort of a tree structure - to be present(ed) simultaneously.

Here is my setup:

  • Entity1
    • DisplayName
    • RelationToEntity2
  • Entity2
    • DisplayName

Now I can fetch the data of either to be presented - so far so good. What I want is to have a one-sectioned TableView (like a flattened view) with the following structure:

  • Entity1 (Entry 1)
  • Entity2 (Entry 1)
  • Entity2 (Entry 2)
  • Entity1 (Entry 2)

Though it might look like a thing to be done via sections, it's not. Both Entities should be a UITableViewCells. Can someone please point me the right direction to flatten the without loosing the actual hierarchy.

It sounds like you need to maintain your own 'flattened' datasource. Perhaps the following will work:

When NSFetchedResultController tells you a new Entity1 has been inserted, you insert Entity1 and its associated Entity2s into say, _flattenedArray so it looks like this:

[<Entity1>, <related Entity2>, <related Entity2>...]

Where you insert them is up to you - it pretty much comes down to:

  1. construct a subarray containing the new Entity1 and associated Entity2 objects
  2. decide where in _flattenedArray to insert the new subarray.
  3. call reloadData or some other means to inform the tableView of the new data

When an Entity1 object is removed, remove it and all subsequent Entity2 objects until you encounter the end of _flattenedArray or run into another Entity1 object. This assumes Entity2 is never a "top level" object. If it is you will need to only remove those Entity2 objects in the relation.

When an Entity1 object gains or loses an Entity2 object, you can first delete the Entity1 object from _flattenedArray then reinserting it. If this is too efficient, do a merge instead.

App Export Compliance using the Dropbox API

6 votes

This question (or variations of this question) has been asked before, but as Apple's export compliance rules change relatively frequently, and no one seems to ever get a straight answer, I thought I would ask.

I write an iPhone application that uses version 0.2 of the Dropbox API.

I have emailed Apple concerning use of this specific API, and I will be sure to update this question as I learn more and hear back from Apple. In the meantime, if any developer is using the Dropbox API in their iPhone application, did you mark your application as using encryption?

Edit: Upon closer inspection, it looks like the file data is also transferred using SSL. Since their API is using the NSMutableURLRequest class over HTTPS though, I still can't determine whether or not this API "uses encryption." If in the App Store submission page I mark that it does include encryption, Apple then asks if I'm using greater than a 64-bit symmetric encryption key.

If your app uses SSL (HTTPS), then yes it does include encryption. The export compliance rules changed last year though, so you will need an Encryption Registration Number instead of a CCATS number. See this blog post for details.