views:

197

answers:

1

I am porting a project to the iPhone (from Windows Mobile) and sharing much of the generic C and C++ code as possible, using Objective-C++. However, during testing I have stumbled upon a curious and troublesome problem that only manifests when running on the device. I have distilled the problem code in to a new project to prove reproducibility and allow easy sharing.

// MemoryTestAppDelegate.mm
#include "MemoryTestAppDelegate.h"
#include "Widget.h"

@implementation MemoryTestAppDelegate

@synthesize window;

- (void)applicationDidFinishLaunching: (UIApplication*)application {
    Widget widget;
    const wchar_t wideHello[] = L"Hello, world!";
    const char narrowHello[] = "Hello, world!";
    widget.Go();
    widget.Go(wideHello);

    [window makeKeyAndVisible];
}

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

@end

 

// MemoryTestAppDelegate.h
#import <UIKit/UIKit.h>

@interface MemoryTestAppDelegate : NSObject <UIApplicationDelegate> {
    UIWindow* window;
}

@property (nonatomic, retain) IBOutlet UIWindow* window;

@end

 

// Widget.h
#include <iostream>

class Widget {
public:
    Widget() { };
    ~Widget() { };

    void Go() const { std::wcout << L"Widget is GO." << std::endl; };
    void Go(const wchar_t* message) const { std::wcout << message << std::endl; };
};

 

// main.mm
#import <UIKit/UIKit.h>

int main(int argc, char* argv[]) {
    NSAutoreleasePool* pool = [[NSAutoreleasePool alloc] init];
    int retVal = UIApplicationMain(argc, argv, nil, @"MemoryTestAppDelegate");
    [pool release];
    return retVal;
}

 

The remaining project files as well the settings are default provided by creating a new iPhone Window-based application. (I removed Interface Builder by deleting the .xib, removing the "Main nib file base name" from MemoryTest-Info.plist, and specifying @"MemoryTestAppDelegate" as the 4th parameter to UIApplicationMain in main.mm.)

The simulator runs this example as expected, but the device presents my problem: Stepping through the code in applicationDidFinishLaunching, the Widget object is constructed as expected. However, wideHello and narrowHello both appear to be corrupt in the Locals watch area. (wideHello is not displayed, but the correct number of characters is indicated, and narrowHello displays as "`K3\x10f\x11".) Examining the two strings in the Memory Window, the correct content is visible - 64 bytes beyond the supposed address of wideHello and narrowHello.

As shown by the second call to the overloaded method, Widget::Go(const wchar_t*) const, the wideHello string is displayed via std::wcout, and upon stepping through applicationDidFinishLaunching the string is output correctly! However, copy operations using wcscpy/memcpy read from the "pre" data 64 bytes ahead of actual content, causing many problems for my application on the actual device. If I replace initializing a Widget on the local stack with a Widget* and dynamic allocation, the memory layout is as expected. I have tried other variations but only stack allocated C++ objects seem to cause the problem.

Any information would be greatly appreciated, thanks for reading.

+1  A: 

Even apple themselves has acknowledged that there are some dissimilarities between their simulator and the device.

Sadly I cannot help you with your problem, however keep in mind that simulator code compiles and run literally on your mac's processor, as in, compiled and running on your intel, i386 architecture and when you compile for device its completely different, compiling to use the armv6 of the device.

Any quirks you discover that sound like they are i386 arch problems will probably never appear on the device, and vice versa.

Yarek T