Best objective-c questions in October 2011

Why use id when we can just use NSObject?

14 votes

I know that when we want to create an unknown value object we use id. However, I'm curious that why did Apple to choose id which decides it's value during runtime, when every object is a subclass of NSObject. So instead of id delegate we could have used NSObject *delegate Does anyone know why? Thanks.

id erases the type and it is equivalent to saying "this object responds to any selector visible to the translation". of course, it is your responsibility to make sure your program is correct when you erase types (and also when you typecast them).

If the type were NSObject, then the compiler would say "NSObject may not respond to selector" if the selector was not declared in NSObject's interface or the protocols it adopts. In that event, you could also add a typecast to cast it to the type you expect.

With strict/correct types, the compiler can kick in and help you out, which is great because ObjC is a very dynamic language.

id is particularly useful when using (or building) collections types. Adding an object would not be a problem unless you defined a new root type (does not inherit from NSObject). Getting a value from the collection would require a typecast if we were to use it as something other than our base class (NSObject).

Objective-C does not support generics - you cannot, for example, declare an NSArray of NSStrings. You can populate an NSArray with NSStrings and pass this through id for a more natural written style when type safety is not preserved (a la generics).

So, let's expand on this with some real code.

Example A

NSString * string = [array objectAtIndex:0]; // << trust me (via id)
return [string length];
-or-
return [[array objectAtIndex:0] length]; // << trust me (via id)

Example B

And now let's say id is not available and we fix all our compiler warnings because it's the right thing to do:

NSString * string  = (NSString*)[array objectAtIndex:0]; // << typecast == trust me
return [string length];
-or-
return [(NSString*)[array objectAtIndex:0] length]; // << typecast == trust me

id doesn't decide its value at runtime, nor doe any NSObject. ObjC objects don't perform implicit promotions, they just cast the pointer through without formal promotion.

Related to your example, I actually declare my delegates and parameters as NSObjects with protocols:

NSObject<MONShapeDelegate>* delegate;

Should IBOutlets be strong or weak under ARC?

13 votes

I am developing exclusively for iOS 5 using ARC. Should IBOutlets to UIViews (and subclasses) be strong or weak?

The following:

@property (nonatomic, weak) IBOutlet UIButton *button;

Would get rid of all of this:

- (void)viewDidUnload
{
    // ...
    self.button = nil;
    // ...
}

Are there any problems doing this? The templates are using strong as are the automatically generated properties created when connecting directly to the header from the 'Interface Builder' editor, but why? The UIViewController already has a strong reference to its view which retains its subviews.

Summarized from the developer library:

From a practical perspective, in iOS and OS X outlets should be defined as declared properties. Outlets should generally be weak, except for those from File’s Owner to top-level objects in a nib file (or, in iOS, a storyboard scene) which should be strong. Outlets that you create should will therefore typically be weak by default, because:

  • Outlets that you create to, for example, subviews of a view controller’s view or a window controller’s window, are arbitrary references between objects that do not imply ownership.

  • The strong outlets are frequently specified by framework classes (for example, UIViewController’s view outlet, or NSWindowController’s window outlet).

    @property (weak) IBOutlet MyView *viewContainerSubview;
    @property (strong) IBOutlet MyOtherClass *topLevelObject;
    

iCloud basics and code sample

11 votes

As a beginner, I'm struggling with iCloud. There are some samples, but they are usually quite detailed (on the developer forum there is one for iCloud and CoreData which is massive). The apple docs are OK, but I still can't see the big picture. So please bear with me, some of these questions are quite fundamental, but possibly easy to answer.

Context: I have a very simple iCloud app running (full sample code below). There is only one UITextView shown to the user and his/her input is saved in a file called text.txt.

enter image description here

The txt file is pushed to the cloud and made available to all devices. Works perfectly, but:

Main problem: What about users who do not use iCloud?

When I launch my app (see code below), I check if the user has iCloud enabled. If iCloud is enabled, everything is fine. The app goes ahead and looks for text.txt in the cloud. If found, it will load it and display it to the user. If text.txt is not found in the cloud, it will simply create a new text.txt and will display that to the user.

If the user does not have iCloud enabled, nothing will happen. How will I make it possible that non-iCloud users can still work with my text app? Or do I simply ignore them? Would I need to write separate functions for non-iCloud users? I.e. functions in which I simply load a text.txt from the documents folder?

Apple writes:

Treat files in iCloud the same way you treat all other files in your app sandbox.

However, in my case there is no 'normal' app sandbox anymore. It's in the cloud. Or do I always load my text.txt from disk first and then check with iCloud if there is something more up-to-date?

Related problem: File structure - Sandbox vs. Cloud

Perhaps my main problem is a fundamental misunderstanding of how iCloud is supposed to work. When I create a new instance of an UIDocument, I'll have to overwrite two methods. First - (BOOL)loadFromContents:(id)contents ofType:(NSString *)typeName error:(NSError **)outError to get files from the cloud and then -(id)contentsForType:(NSString *)typeName error:(NSError **)outError to get files into the cloud.

Do I have to incorporate separate functions which will also save a local copy of text.txt into my sandbox? Will this work for non-iCloud users? As I understand iCloud, it will save a local copy of text.txt automatically. So there shouldn't be any need for me save anything into the 'old' sandbox of my app (i.e. as it used to be in the old, pre-iCloud days). Right now, my sandbox is totally empty, but I don't know if this is correct. Should I keep another copy of text.txt in there? This feels like cluttering my data structure... as there is one text.txt in the cloud, one in the iCloud sandbox on my device (which will work even if I am offline), and a third one in the good old sandbox of my app...


MY CODE: A simple iCloud sample code

This is loosely based on an example I found in the developer forum and on the WWDC session video. I stripped it down to the bare minimum. I'm not sure that my MVC structure is any good. The model is in the AppDelegate which isn't ideal. Any suggestions to make it better are welcome.


EDIT: I tried to extract the main question and posted it [here].4


OVERVIEW:

Overview

The most important bit which loads the text.txt from the cloud:

//  AppDelegate.h
//  iCloudText

#import <UIKit/UIKit.h>

@class ViewController;
@class MyTextDocument;

@interface AppDelegate : UIResponder <UIApplicationDelegate> {
    NSMetadataQuery *_query;
}

@property (strong, nonatomic) UIWindow *window;
@property (strong, nonatomic) ViewController *viewController;
@property (strong, nonatomic) MyTextDocument *document;

@end

//  AppDelegate.m
//  iCloudText

#import "AppDelegate.h"
#import "MyTextDocument.h"
#import "ViewController.h"

@implementation AppDelegate

@synthesize window = _window;
@synthesize viewController = _viewController;
@synthesize document = _document;

- (void)dealloc
{
    [_window release];
    [_viewController release];
    [super dealloc];
}

- (void)loadData:(NSMetadataQuery *)query {

    // (4) iCloud: the heart of the load mechanism: if texts was found, open it and put it into _document; if not create it an then put it into _document

    if ([query resultCount] == 1) {
        // found the file in iCloud
        NSMetadataItem *item = [query resultAtIndex:0];
        NSURL *url = [item valueForAttribute:NSMetadataItemURLKey];

        MyTextDocument *doc = [[MyTextDocument alloc] initWithFileURL:url];
        //_document = doc;
        doc.delegate = self.viewController;
        self.viewController.document = doc;

        [doc openWithCompletionHandler:^(BOOL success) {
            if (success) {
                NSLog(@"AppDelegate: existing document opened from iCloud");
            } else {
                NSLog(@"AppDelegate: existing document failed to open from iCloud");
            }
        }];
    } else {
        // Nothing in iCloud: create a container for file and give it URL
        NSLog(@"AppDelegate: ocument not found in iCloud.");

        NSURL *ubiq = [[NSFileManager defaultManager] URLForUbiquityContainerIdentifier:nil];
        NSURL *ubiquitousPackage = [[ubiq URLByAppendingPathComponent:@"Documents"] URLByAppendingPathComponent:@"text.txt"];

        MyTextDocument *doc = [[MyTextDocument alloc] initWithFileURL:ubiquitousPackage];
        //_document = doc;
        doc.delegate = self.viewController;
        self.viewController.document = doc;

        [doc saveToURL:[doc fileURL] forSaveOperation:UIDocumentSaveForCreating completionHandler:^(BOOL success) {
            NSLog(@"AppDelegate: new document save to iCloud");
            [doc openWithCompletionHandler:^(BOOL success) {
                NSLog(@"AppDelegate: new document opened from iCloud");
            }];
        }];
    }
}

- (void)queryDidFinishGathering:(NSNotification *)notification {

    // (3) if Query is finished, this will send the result (i.e. either it found our text.dat or it didn't) to the next function

    NSMetadataQuery *query = [notification object];
    [query disableUpdates];
    [query stopQuery];

    [self loadData:query];

    [[NSNotificationCenter defaultCenter] removeObserver:self name:NSMetadataQueryDidFinishGatheringNotification object:query];
    _query = nil; // we're done with it
}

-(void)loadDocument {

    // (2) iCloud query: Looks if there exists a file called text.txt in the cloud

    NSMetadataQuery *query = [[NSMetadataQuery alloc] init];
    _query = query;
    //SCOPE
    [query setSearchScopes:[NSArray arrayWithObject:NSMetadataQueryUbiquitousDocumentsScope]];
    //PREDICATE
    NSPredicate *pred = [NSPredicate predicateWithFormat: @"%K == %@", NSMetadataItemFSNameKey, @"text.txt"];
    [query setPredicate:pred];
    //FINISHED?
    [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(queryDidFinishGathering:) name:NSMetadataQueryDidFinishGatheringNotification object:query];
    [query startQuery];

}

- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
{
    NSLog(@"AppDelegate: app did finish launching");
    self.window = [[[UIWindow alloc] initWithFrame:[[UIScreen mainScreen] bounds]] autorelease];

    // Override point for customization after application launch.
    if ([[UIDevice currentDevice] userInterfaceIdiom] == UIUserInterfaceIdiomPhone) {
        self.viewController = [[[ViewController alloc] initWithNibName:@"ViewController_iPhone" bundle:nil] autorelease];
    } else {
        self.viewController = [[[ViewController alloc] initWithNibName:@"ViewController_iPad" bundle:nil] autorelease];
    }

    self.window.rootViewController = self.viewController;
    [self.window makeKeyAndVisible];

    // (1) iCloud: init

    NSURL *ubiq = [[NSFileManager defaultManager] URLForUbiquityContainerIdentifier:nil];
    if (ubiq) {
        NSLog(@"AppDelegate: iCloud access!");
        [self loadDocument];
    } else {
        NSLog(@"AppDelegate: No iCloud access (either you are using simulator or, if you are on your phone, you should check settings");
    }


    return YES;
}

@end

The UIDocument

//  MyTextDocument.h
//  iCloudText

#import <Foundation/Foundation.h>
#import "ViewController.h"

@interface MyTextDocument : UIDocument {

    NSString *documentText;
    id delegate;

}

@property (nonatomic, retain) NSString *documentText;
@property (nonatomic, assign) id delegate;

@end

//  MyTextDocument.m
//  iCloudText

#import "MyTextDocument.h"
#import "ViewController.h"

@implementation MyTextDocument

@synthesize documentText = _text;
@synthesize delegate = _delegate;

// ** READING **

- (BOOL)loadFromContents:(id)contents ofType:(NSString *)typeName error:(NSError **)outError
{
    NSLog(@"UIDocument: loadFromContents: state = %d, typeName=%@", self.documentState, typeName);

    if ([contents length] > 0) {
        self.documentText = [[NSString alloc] initWithBytes:[contents bytes] length:[contents length] encoding:NSUTF8StringEncoding];
    }
    else {
        self.documentText = @"";
    }

    NSLog(@"UIDocument: Loaded the following text from the cloud: %@", self.documentText);


    // update textView in delegate...
    if ([_delegate respondsToSelector:@selector(noteDocumentContentsUpdated:)]) {
        [_delegate noteDocumentContentsUpdated:self];
    }

    return YES;

}

// ** WRITING **

-(id)contentsForType:(NSString *)typeName error:(NSError **)outError
{
    if ([self.documentText length] == 0) {
        self.documentText = @"New Note";
    }

    NSLog(@"UIDocument: Will save the following text in the cloud: %@", self.documentText);

    return [NSData dataWithBytes:[self.documentText UTF8String] length:[self.documentText length]];
}
@end

THE VIEWCONTROLLER

//
//  ViewController.h
//  iCloudText

#import <UIKit/UIKit.h>

@class MyTextDocument;

@interface ViewController : UIViewController <UITextViewDelegate> {

    IBOutlet UITextView *textView;

}

@property (nonatomic, retain) UITextView *textView;
@property (strong, nonatomic) MyTextDocument *document;

-(void)noteDocumentContentsUpdated:(MyTextDocument *)noteDocument;

@end

//  ViewController.m
//  iCloudText

#import "ViewController.h"
#import "MyTextDocument.h"

@implementation ViewController

@synthesize textView = _textView;
@synthesize document = _document;

-(IBAction)dismissKeyboard:(id)sender {

    [_textView resignFirstResponder];

}

-(void)noteDocumentContentsUpdated:(MyTextDocument *)noteDocument
{
    NSLog(@"VC: noteDocumentsUpdated");
    _textView.text = noteDocument.documentText;
}

-(void)textViewDidChange:(UITextView *)theTextView {

     NSLog(@"VC: textViewDidChange");
    _document.documentText = theTextView.text;
    [_document updateChangeCount:UIDocumentChangeDone];

}

I've been using your example and I like it for helping me grasp the basics of iCloud. Now I'm wrangling with your question for my own app which has to support existing users of the app with locally stored content who may or may not be using iCloud creating these cases as far as I can tell:

Cases:

  1. New user
    • has icloud - create documents in icloud
    • no icloud - create documents locally
  2. Existing user
    • has icloud
      • just added - migrate local docs to icloud
      • not just added - open/save docs to icloud
    • no icloud
      • just removed - migrate former icloud docs to local
      • not just removed - open/save docs to local

If someone removes iCloud - wouldn't the calls to ubiquitous URL return nil? If that's the case how do I migrate the docs back to local storage? I'll create a user pref for now but seems a bit of a workaround.

I feel like I'm missing something obvious here so if anyone can see it, please chime in.

Should NSLocking usage always be wrapped in @try/@finally?

10 votes

Given a Cocoa NSLocking object (like an NSLock) and some non-trivial code to be executed while the lock is held:

To ensure a lock is always released, should the following idiom always be used?

NSLock *mutex = // get lock from somewhere
@try {
    [mutex lock];
    // do non-trivial stuff
} 
@finally {
    [mutex unlock];
}

This seems prudent (and common in Java), but I haven't seen any Cocoa code do this.

Should this idiom be used? Why or why not?

To ensure a lock is always released, should the following idiom always be used?

Yes, where program correctness after that scope ('non-trivial stuff') is required, and assuming your program can properly recover from the exceptions it encounters.

Should this idiom be used? Why or why not?

If you can recover, then yes, unlocking is necessary to continue execution normally. Otherwise, your program will be executing in an invalid state.

  • Example 1: When the lock is destroyed (during dealloc), the attempt to destroy it will fail because it is still locked. Whether the implementation continues to destroy the lock or ignores the error is not defined (I would guess it would persist, meaning it would never exit from dealloc).

  • Example 2: When it is locked from another thread (or the same thread, if not reentrant) then you will never acquire the lock and another error, deadlock, or exception may be produced as a result. An implementation could also (eventually) proceed with no lock acquired. All that is guaranteed is logging when/if an error is detected.

pthread_mutexes and the implementations that depend on them may not behave very gracefully if you have such a lock imbalance; this always falls back to a programmer error.

Correct and defensive locking in pure objc and c is not very pretty. The Java idiom is correct, and equally applicable to Foundation APIs. The reason you don't see it as much may be because exceptions are a less popular/used error handling mechanism within Cocoa APIs and programs that depend on them (compared to Java). see also Bavarious' note in the comments

How to keep the header comments up to date in Xcode

10 votes

Xcode has a habit of putting all kinds of (redundant) information at the top of each code file it creates, containing copyright notices, class names, project names and client names. Like it or not, once you create a new class "A", then refactor it to be called "B", the information is wrong already. The comments will keep saying that this is "A.h" or "A.m". In addition, if you reuse classes from one project in a next, it will also state the wrong project name.

//
//  A.h
//  ProjectName
//
//  Created by Author on 19-06-11.
//  Copyright 2011 CompanyName. All rights reserved.
//

There must be a reason there aren't many people complaining about this. What is your trick to keep the header comments up to date? Is there a tool that auto-corrects it all? Is there a hidden setting?

Cheers, EP.

There may be a way to update your comments but it will be tricky.

As far a customizing the template, this is not as bad. it is just a text file located in /Developer/Platforms/iPhoneOS.platform/Developer/Library/Xcode/Templates/File Templates/Cocoa Touch/

Don't edit the files here, they will get overwritten when you update, or reinstall xcode. Place your custom templates here, in your home directory.

~/Library/Developer/Xcode/Templates/File Templates/

High Order Bit explains further.

Adding non NSObjects to NSMutableArray

10 votes

This recent SO discussion has confused me. The NSMutableArray prototype for addObject: is

- (void)addObject:(id)anObject

and id is defined in objc.h as

typedef struct objc_class *Class;
typedef struct objc_object {
    Class isa;
} *id; 

When I add an NSObject or subclass to an NSMutableArray, its retain count is incremented, and when I remove it from an NSMutableArray it is decremented. Does this mean that if an id type which is not an NSObject or subclass is added to an NSMutableArray, it has to respond to retain and release messages? The definition of id does not seem to force this. Is it an objective C directive that any id type should respond to standard memory management messages?

The hard truth about most Foundation containers (and by extent most Apple-developed classes, and by extent also most classes developed by third parties) is that when a method accepts the id type, it should really read id<NSObject>, which means any type that responds to the NSObject protocol. Instances of classes that aren't part of the NSObject hierarchy are unlikely to respond to -retain and -release, which is especially inconvenient when trying to add them to a container. They're also unlikely to respond to -hash, -isEqual:, -description, -copy, and to all the other methods Foundation containers can use on their contents for whatever reason.

For instance, if you attempt to add Class objects to a Foundation container (other than NSMapTable since this one was designed with a lot of flexibility in mind), you'll hit a wall because "modern" ObjC classes are expected to inherit from NSObject, or at least implement the NSObject protocol.

This is a pretty rare situation, though. Class is pretty much the only useful class around that doesn't inherit from NSObject.

Force a window to redraw itself using Core Graphics?

7 votes

I have developed injection system and have hooked some quartz API's to create some nice effects with windows on Mac OS X. For example, when user sets a color to red in window.. it is red glossy red.

But, when I inject in application which is already running, I cannot give it desired effects as window is already painted. So, I am looking for something in quartz/core graphics which can allow me to redraw whole window or some technique which can allow me to send some event/call some function which will make system repaint whole window again.

I mean every thing on window is to be draw again so that my hooked API's will execute in order to create proper effects, shades and colors. Here order in which window gets created & painted is important.

I am using technique similar to inject&interpose and injection code is C/C++ code.

Does anyone have an idea how can I achieve this?

-[NSView setNeedsDisplayInRect:] and -[NSView setNeedsDisplay:] are the direct equivalents of invalidateRect.

I don't know what you mean by that you need it in Quartz/CoreGraphics. Cocoa is already using them for drawing.

If you want to call some magic CGxxx() function that will make the window painted again, it can't be done. The window's title and frame are painted by the system, but as for the content, there is no way for lower level APIs to know what should be painted there. The only one who knows how to draw a view is the view itself. (Maybe there is something cached in window's backing store, but I don't know any public or undocumented APIs to access it).

Whatever you find just has to based on asking the NSWindow object to redraw its views. If you're already injected into a process, it may involve the following steps:

  • locating obj-c runtime (you will need at least objc_msgSend function)
  • locating NSApplication class
  • using +[NSApplication sharedApplication] and -[NSApplication windows] to find NSWindow* object pointer
  • using contentView, display etc. to redraw

Bulk translation of C# to Objective C

7 votes

Our department has inherited two new code bases. One is in C#, the other is in Objective C. The first has a bunch of functionality that we need in the second as well.

I realize that there isn't going to be a 1-to-1 relationship that we can run a simple translator to move from C# to Objective C, but is there some tool to do a rough conversion of the syntax.

We're mostly looking for a tool that would do some of the mindless part. I'm not looking forward to manually translating 1000 function headers, for example, when the format for both is so well defined.

EDIT

Even something we could run on an individual methods one at a time would speed up the process significantly.

Are you getting rid of the C# code base? If not then it seems like you can look at this from a different angle - rather than undertaking the huge effort of converting thousands of classes that rely on probably scores of API's, how about exposing your existing functionality as services that can be called from your Objective-C code? You can do this using web services.

Alternatively you can take a look at Mono/Cocoa#, though I doubt this is going to be a viable solution for your problem.

iOS App crashing before entering main() with Xcode 4.2 & iOS 5

7 votes

Background

After upgrading / to / I am experiencing crashes while the App is loading and before it even enters main().

I have set a break point in main() but it is never reached.

main() break point

  • Compiling the project in Xcode 4.1 with a Base SDK of 4.3 works fine on iOS 4.x and iOS 5.
  • Compiling the same project in Xcode 4.2 with a Base SDK of 5.0 works fine on 4.x but crashes in iOS 5, both on the simulator and on a device.

Simulator Crash

iOS 5 Simulator LLDB Crash Xcode 4.2 Output

Crashes with EXC_BAD_ACCESS

Stack calls

List of calls, all system calls, not even the main() has not been called yet.

My best guess is a problem loading a library, but have know idea how to track it down!

Attempts at a resolution

  1. Turned on Zombies
  2. Turned on all logging
  3. Added different versions of system libraries (libz.1.2.5.dylib and libz.dylib)
  4. Cleaned the project
  5. Deleted the App form the simulator
  6. Delete the Derived Data folder

What is your deployment target?

My deployment target was iOS4.0. I changed it to iOS4.3 and the issue is resolved! (Building against iOS5 GM SDK, of course.) My app now runs in the iOS5 simulator.

I got this idea from an answer in another SO thread that said ARC is supported in iOS4.3 and above. My app doesn't use ARC, nor do any of it's dependent libraries, as far as I can tell. The answer also said something about weak reference zeroing, which seemed... perhaps relevent since a lot of people have had success in removing specific linker directives concerning weak references to libSystem.B.dylib.

It bothers me a little that I have to move up my base deployment target beyond 4.0 because that feels like I am cutting out a lot of potential users. Despite Apple's hope that everyone will always upgrade their devices, many people do not. Oh well.

EDIT

It's worth mentioning that this project was originally done under Xcode3, so there is likely just some bizarre cruft in the project itself that is both unneeded and causing this problem. But I'll be damned if I can find it!

EDIT 2

Well, well, well... upon further inspection... I found 2 errant references to libSystem.B.dylib in my project.pbxproj file that were not visible through Xcode's build settings, but that I had to remove by hand with a text editor!

Once doing this, I reset the base deployment version to 4.0, built for the iOS5 simulator, and the app ran without issue.

Amazing.

The lesson: Never underestimate the chances for there to be garbage in your project file.

EDIT 3

Xcode project edit

Removing the 3 occurrences of these lines in the project.pbxprojfile inside the Xcode project package (right click and show package contents).

show package contents

App Store code runs different from Xcode/Device code on iPhone 3G

7 votes

I'm not sure how to explain this one. I submitted an update of one of my apps to the store yesterday. The first screenshot is how one particular screen appears running on my iPhone 3G, running iOS 4.2.1, downloaded from the App Store:

And the second image below is the exact same code, no changes have been made since the original submission, running on the exact same device tethered through Xcode.

The application runs fine on my iPhone 4, running iOS 5, downloaded from the App Store. So to recap:

  1. App obtained from App Store is flawed on iPhone 3G
  2. App obtained from App Store is good on iPhone 4
  3. App tethered through Xcode is good on iPhone 3G

These are not the only graphical inconsistencies, but they're all related to custom UITableViewCell code, which doesn't do anything more than push labels a few pixels in each directory, and has been working fine since day one. I have filed a report with idp-dts, and am waiting to hear back from them, but as the waiting list is usually a week or more, I'd rather figure this out on my own.

Any help/tips/guesses would be very appreciated!

iPhone 3G running App Store version of my application:

enter image description here

iPhone 3G running Xcode tethered version of my application:

enter image description here

Edit: This sounds quite similar to this problem: Building with LLVM and any optimization causes app to crash on startup The customer who contacted me was using a 2nd generation iPod Touch, the only other hardware other than the iPhone 3G that uses armv6.

Edit 2: Here's the snippet of code that sets the bounds of the color bar on the left side. There doesn't seem to be anything fishy going on in the code:

- (void)layoutSubviews {
    CGRect colorViewFrame = self.bounds;
    colorViewFrame.size.width = 6;
    colorViewFrame.origin.y += 3;
    colorViewFrame.origin.x -= 1;
    colorViewFrame.size.height -= 8;

    colorView.frame = colorViewFrame;
    ...
}

So here's what I found under Build Settings:

enter image description here

Which, having taken a class or two in compiler optimization, makes sense. Code debugging on a device should be left in its original state for debugging purposes, and code released should be optimized for speed and efficiency.

So here's the fun part. Changing my Debug setting to Fastest, Smallest:

enter image description here

Causes the issue to occur on my device while running tethered in Xcode.

Before filing a Radar or making any rash decisions and submitting non-optimized code to the App Store, is there anything else I should consider investigating? Was the compiler optimization really the underlying cause of the layout issues?

Edit: And if the optimization level is the issue, why does the optimized code run properly on my iPhone 4, but not properly on my iPhone 3G?

Edit 2: This problem sounds extremely similar to this answer: Building with LLVM and any optimization causes app to crash on startup

Edit 3: Heard back from Apple Radar, this is a known issue. Will be fixed in a future version of Xcode. Thanks for all the help guys!

Back button does not appear in navigation bar until you rotate

7 votes

I have three view controllers: A -> B -> C managed by a navigation controller. A is a transient view controller. It asks the server for something. If the server says everyhing is OK, then A pushes B onto the stack. B must hide the back button because I don't want users to manually go back to A.

// B view controller
- (void)viewDidLoad 
{
    [super viewDidLoad];
    self.navigationItem.hidesBackButton = YES;
    self.title = @"B";
}

B then pushes C onto the stack when the user taps a table cell.

// B view controller
- (void)tableView:(UITableView *)tableView 
didSelectRowAtIndexPath:(NSIndexPath *)indexPath 
{
    C *c = [[C alloc] 
        initWithStyle:UITableViewStyleGrouped
    ];
    [self.navigationController 
        pushViewController:c 
        animated:YES
    ];
    [c release];
}

.

// C view controller
- (void) viewDidLoad
{
    [super viewDidLoad];
    self.navigationItem.hidesBackButton = NO;
    self.title = @"C";
}

If all goes well, the flow should look like this:

-------------    -------------    -------------
|_____A_____|    |_____B ____|    | <B|__ C___|
|           | => |           | => |           |
| loading...|    |   cells   |    |   detail  |
|           |    |           |    |           |
-------------    -------------     -----------

For some reason, C does not show a back button to go back to B until I rotate the device. Once rotated, the back button appears in all orientations. The problem seems to stem from B hiding the back button and C trying to reveal it again, because If I don't let B hide it, I don't have this problem. So how do I get C to show the back button without forcing the user to rotate the device like a monkey?

Update

  • Broken on two different Verizon iPhone 4 both on iOS 4.2.10
  • Fine on AT&T iPhone 3GS on iOS 5.0
  • Fine on AT&T iPhone 4 on iOS 4.3

After some searching I found this solution for iPhone 4.2 (since you posted that it works on later versions) on some old forum post.

-(void)viewDidLoad { 
                     [super viewDidLoad];
                     self.navigationItem.hidesBackButton = YES; 
                   }

-(void)viewDidAppear:(BOOL)animated { 
                                       [super viewDidAppear:animated];
                                       self.navigationItem.hidesBackButton = NO; 
                                    }

Mayhaps this will help you out. (Check this out: Back button don't appear in navigationController)

Mixing C pre/post increment/decrement with Objective-C dot operator works?

7 votes

Say I have a class with a scalar property type:

@property (nonatomic, assign) int myInt;

And for clarity, synthesized like:

@synthesize myInt = _myInt;

If someone had asked me if the following line would work:

self.myInt++;

I would have said "No". The rationale being that we all know that the dot operator is just syntactic sugar for calling a compiler-generated getter method. So that line is literally:

[self myInt]++;

If you type that second line into Xcode, it won't compile, stating: "Assigning to 'readonly' return result of an objective-c message not allowed". This makes perfect sense, and it's what I would have expected. Even if that compiled, I would have expected the outcome to increment a copy of the backing ivar on the stack, not the ivar itself.

But, the instruction self.myInt++ does compile, and it works. It works just as if that dot operator were directly accessing _myInt. By supplying my own getters and setters, I can see that both the getter and the setter are used in the process, in that order, like it was actually:

[self setMyInt:[self myInt] + 1];

So, is this an exception to the rule that the dot operator is exactly the same as a method call, or are the {--, ++, +=, -=} operators given special attention by the Objective-C compiler when used with dot notation? I've always thought of them as a C language features with no special considerations for Objective-C. I could see that simple line being very confusing to someone unfamiliar with Objective-C dot notation.

You can look at the assembler output and see that it generates two _objc_msgSend calls.

I'd guess it's more a case of applying the rule that a++ is syntactic sugar for a = a + 1.

Using C macros to create C-style code which maps to Objective-C message calls?

6 votes

I'm sure I'll get 20 people saying "why would you want to do that anyways"... but, I'm going to ask my question none-the-less because it's somewhat academic in nature.

I'd like to use C macros to redefine [ClassName new] into something like: new(ClassName), and I'm wondering how to do this. I'm not super comfortable with C macros to begin with (I know - embarrassing - I should be) - and I'm definitely not comfortable mixing them in with my Objective-C code. So, on with the question...

First, being it's a preprocessor thing, can I do a simple substitution like such:

#define new(x) [x new]

or, for whatever reason, do I need to drop down to the objective-c runtime, and do something more akin to:

#define new(x) objc_msgSend(class_createInstance(x, 0), sel_registerName("init"))

What are the downfalls of doing something like this?

Is this kind of thing used often by others, or, would someone look at it and say "what the heck are you doing there"? (and should I care)

Thanks

EDIT:

It occurred to me after posting this, that I have, in fact, see this kind of thing before - in the Three20 lib, where they do things like this:

#define TT_RELEASE_SAFELY(__POINTER) { [__POINTER release]; __POINTER = nil; }
#define TT_INVALIDATE_TIMER(__TIMER) { [__TIMER invalidate]; __TIMER = nil; }

// Release a CoreFoundation object safely.
#define TT_RELEASE_CF_SAFELY(__REF) { if (nil != (__REF)) { CFRelease(__REF); __REF = nil; } }

So probably my question becomes simply; What are the downfalls of doing this, and is it a relatively accepted practice, or something that's going to get me into more trouble than it's worth?

Macros are processed first, and they are operating on the plain text source code. So yes, you can have your new macro generate Objective-C syntax or plain C syntax or even invalid syntax if you like.

The downfalls of using a macro in general is that, because it is parsed and processed in a separate step, it's possible to write macros that don't behave how you expect even when everything looks fine.

For example, this macro:

#define MAX(x,y) x > y ? x : y

Looks OK, but say you used it like this:

z = MAX(a,MAX(b,c));

It would be expanded by the preprocessor into something like this:

z = a > b > c ? b : c ? a : b > c ? b : c;

Which won't actually give you the max of the three arguments. To solve this you need to liberally sprinkle parenthesis in your macro definition, even where you don't think it is needed:

#define MAX(x,y) ((x) > (y) ? (x) : (y));

That fixes it, except I added a semicolon to the end, which is an understandable habit from writing a lot of C code, except now our macro expands to:

z = ((a) > (((b) > (c) ? (b) : (c));) ? (a) : (((b) > (c) ? (b) : (c));));;

Syntax errors!

If you look at how MAX is actually defined in Objective-C, it's pretty mess, but that's what you have to do to write macros safely. And you also need to consider that:

z = MAX(expensiveComputation(), reallyExpensiveComputation())

Will, unlike a function, actually execute one of those functions twice, unless you use a trick in your macro to basically emulate parameter passing.

So, to answer your question, yes it is totally possible, but writing safe macros is really hard. And you're doing this so you can pretend your Objective-C code is actually code in another language... Why would you want to do that, anyways?