views:

360

answers:

2

My application has been rejected because the application did not function when reviewed by the iPhone App Review Team. The application is supposed to load words into a table view, but did not do so during the review. However, the application functions perfectly on my iPhone. Can you please give me any suggestions as to why an application would work perfectly on one iPhone but not on the iPhone used in the review process?

Here are the technical details:

  1. The project was compiled on a MacBook Pro running Mac OS X 10.6.2 (10C540)
  2. Xcode version 3.2.1 64-bit Xcode IDE: 1613.0 Xcode Core: 1614.0 ToolSupport: 1591.0
  3. Base SDK version 3.1.3
  4. Optimization Level: Fastest, Smallest [-Os]
  5. My iPhone is a 3GS with 3.1.3 OS

I suspect that the method -(UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath is not being called. This method is in the delegate defined as @interface WordTableViewController : GenericTableViewController where the class GenericTableViewController is defined as @interface GenericTableViewController : UITableViewController .

I suspect the above to be the case because at one point in my development, debugging the release version on the iPhone revealed that -(UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath was not being called, though it was being called when debugging the debug version on the same iPhone. I resolved that issue after days of investigation. I resolved it by simply changing the optimization level in the release configuration and then changing it back to its original value. This was very strange to me.

For the most recent app submission in question, I recreated the distribution configuration by duplicating the functioning release configuration. I then created an ad-hoc configuration by duplicating that distribution configuration. Now, the application functions perfectly on my iPhone for the release, debug and ad-hoc distribution. It does not function at Apple, however.

Your comments will be VERY highly appreciated!

+1  A: 

One thing to consider. NSUserDefaults.

When you do your test cases, to you have test cases where the UserDefaults are:

  1. Empty.
  2. Corrupted.

Apple will be starting the app with no user defaults and, if you do not clear them from time to time, your test bed with both the simulator and target hardware will have defaults stored.

If you are not using NSUserDefaults to store state data, we will need more information.

Steven Noyes
Thank you Steven. I am not using NSUserDefaults. However, I wonder if my last comment to Brad's response makes sense. I believe that I may have been getting away with something that I should have not been allowed to do. Namely, the releasing of the NSMutableArray and then filling it again with each search. I will put some code in my next comment. (I'm almost out of space in this block of text.)
// Used to do a release here:[list removeAllObjects];while(sqlite3_step(statement) == SQLITE_ROW for(int i = 0; i < 7; i++) { NSString *value; char *column = (char *)sqlite3_column_text(statement, i);if (column == nil) { value = [[NSString alloc] initWithUTF8String:""];} else { value = [[NSString alloc] initWithUTF8String:column];} [columns addObject:value]; [value release];} [list addObject:columns]; [columns release];}[sql closeDatabase];[sql dealloc];}
OK. Most of this looks OK. I do have a question on who owns the memory from the "sqlite3_column_text(statement, i);" call. Likewise, the [[NSMutableArray alloc] init] really should be [[NSMutableArray alloc] initWithCapacity:8]. The NSMutableArray did NOT override init, there is a chance some code is not being called that really should be. Also, use the convenience methods of "arrayWithCapacity:", "stringWithUTF8String:". They will remove lots of releases from your code and make your life easier.
Steven Noyes
Thank you again. I made the change to initWithCapacity:8. Also, the SQLite doc states that memory space used to hold strings and BLOBs is freed automatically. Do not pass the pointers returned sqlite3_column_blob(), sqlite3_column_text(), etc. into sqlite3_free(). So that's OK. Good point with arrayWithCapacity since it is basically equivalent to [[[NSMutableArray alloc] initWithCapacity: 1] autorelease], it's more convenient. stringWithUTF8String is similar. I'll be using those often!
A: 

Aside from starting clean, like Steven suggest (delete your application completely from your iPhone and reinstall it), the biggest causes of inconsistent behavior between devices seem to be memory- and threading-related.

You state that you are using an iPhone 3G S, which has significantly more memory than the older models of iPhone / iPod touch. If you can, test out your application on one of these older devices yourself, or find someone willing to beta-test your application (you mentioned having a working ad-hoc build, so you're most of the way there for this). If you can't get access to one of these devices, run your application on your device using the Memory Monitor (not ObjectAlloc) instrument and observe the peak memory usage of your application. If it rises above 20 MB at any time, it may be getting sent low memory warnings on older devices, which your application may not be handling properly. You can artificially induce low memory warnings in the Simulator using Hardware | Simulate Memory Warning as well and see what happens.

If you use any background threads for processing, they also can cause nondeterministic behavior. The iPhone 3G S has a much faster processor than early model iPhones / iPod touches, so things might be executing in a different order on your device than theirs. Verify that you are properly locking access to shared resources and not performing UI updates on a background thread.

Brad Larson
Thank you very much! 1) I cleaned the old app off my iPhone. 2) I was not able to borrow a 3G. I also was not able to find a tool called "Memory Manager" You emphasized that you were referring to something other than "Object Allocations", so I assumed that you were referring to "Leaks". (I hope I understood you correctly). Therefore, I used Instruments on my Release build while it ran on my iPhone. There are no leaks. At startup the app uses 610KB. The functionality that fails at Apple uses 1.92MB. The most intensive feature uses 6.20MB. There are no explicit UI updates on a background thread.
The fact that the optimizations setting had any effect at all, tells me that you are right in that things may be executing in a different order. I am looking at my tableview code now. I suspect that the datasource (a mutable array that is a member of my tableview's base class) may be related to the problem. When a user does a search in my app, it fills the table with results. I used to release the mutable array and then re-alloc and fill it again with each search. I just changed that and alloc once when the view loads and then do a removeAllObjects and re-fill the array with each search.
I'm referring to the Memory Monitor instrument. In Instruments, go to the Library and drag the Memory Monitor instrument over to your list of instruments and restart your application (or choose to launch it from the Default Target pulldown). The readout it gives is the true memory usage of your application (ObjectAlloc and the others hide much of your UI memory usage).
Brad Larson
Simply releasing the NSMutableArray and immediately recreating it shouldn't cause a problem if done on the main thread, because that should block any UI updates until the whole operation is finished. However, I have seen order-of-execution problems before with UITableView on 3.0 (Apple has workarounds listed in the UITableView class reference for one such issue).
Brad Larson
Great help! I will be using that memory instrument a lot. It revealed that the peak memory usage during the functionality in question hit approximately 12MB. In a much more memory and processor intensive feature of the application (not part of this issue), the peak real memory hit 32MB (virtual approximately 300MB). MobileSafari and MobileMail hits peaks around that high as well. I saw no leaks. All of this said, I don't think I am committing any gross memory sins (please correct me if I'm wrong). However, I will be looking at the UITable on 3.0 issue you mention. The journey continues.
On an original model iPhone, you start getting memory warnings at around 20 MB and your application is hard killed at 30 MB, so I wouldn't ignore that part of your application, even if it isn't the problem.
Brad Larson
The built-in applications have capabilities beyond the ones we can write, so they don't have the same limitations. In any case, on a 3G S they may just make the most of the extra memory they have available. For discussion of iPhone business opportunities and related issues, I direct you to the iPhoneSB mailing list: http://groups.google.com/group/iphonesb/
Brad Larson
Thanks Brad. I will check out the site. I am really concerned about my memory usage now! This is a real problem for me. First of all, either the iPhone libraries are leaky or I am grossly misunderstanding something. My textDidChange event looks like @try { return; ...(I put a return in so I can step by step see where I'm starting to eat up memory.) The same with searchButtonClicked. A tableview with searchview at app startup. I am using 5.04MB. As soon as I type letters into the searchbar (or backspaces), real memory goes to 8MB+ and never gets reclaimed! Why? I'm not even in my code yet.