Je transfère un projet vers l'iPhone (à partir de Windows Mobile) et partage une grande partie du code générique C et C++ en utilisant Objective-C++. Cependant, au cours des tests, je suis tombé sur un problème curieux et gênant qui ne se manifeste que lors de l'exécution sur l'appareil. J'ai distillé le code de problème dans un nouveau projet pour prouver la reproductibilité et permettre un partage facile.Curiosités de la mémoire du périphérique/simulateur de l'iPhone avec Objective-C++
// 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;
}
Les fichiers de projet restants ainsi que les paramètres sont fournis par défaut en créant une nouvelle application basée sur la fenêtre iPhone. (J'ai supprimé Interface Builder en supprimant le fichier .xib, en supprimant le "nom de base du fichier nib principal" de MemoryTest-Info.plist et en spécifiant @ "MemoryTestAppDelegate" en tant que 4ème paramètre à UIApplicationMain dans main.mm.)
Le simulateur exécute cet exemple comme prévu, mais le périphérique présente mon problème: En parcourant le code dans applicationDidFinishLaunching, l'objet Widget est construit comme prévu. Cependant, wideHello et narrowHello semblent être corrompus dans la zone de surveillance Locals. (wideHello n'est pas affiché, mais le nombre correct de caractères est indiqué, et narrowHello affiche "` K3 \ x10f \ x11 ".) En examinant les deux chaînes dans la fenêtre mémoire, le contenu correct est visible - 64 octets au-delà de l'adresse supposée de largeHello et narrowHello.
Comme le montre le deuxième appel à la méthode surchargée, Widget :: Go (const wchar_t) const, la chaîne wideHello est affichée via std :: wcout, et lors de l'exécution de applicationDidFinishLaunching, la chaîne est sortie correctement! Cependant, les opérations de copie à l'aide de wcscpy/memcpy lisent à partir des données "pré" 64 octets avant le contenu réel, provoquant de nombreux problèmes pour mon application sur le périphérique réel. Si je remplace l'initialisation d'un Widget sur la pile locale par un Widget * et une allocation dynamique, la mise en forme de la mémoire est comme prévu. J'ai essayé d'autres variations, mais seuls les objets C++ alloués par pile semblent causer le problème.
Toute information serait grandement appréciée, merci de lire.