samedi 17 mars 2012

Core Data Odds and Ends

J'ai fait un peu juste de travailler avec Core Data dernièrement, et ont ramassé quelques trucs pour rendre ma vie plus facile, alors j'ai pensé que je partagerais.

Mo 'alimentation


Retour dans le EOF jours, il y avait un grand morceau peu de logiciels tiers appelé EOGenerator que quelque chose appelé la mise en œuvre design pattern fossé des générations. Essentiellement, ce petit programme serait de créer vos classes personnalisées des EOGenericRecord (équivalent EOF de NSManagedObject) pour vous. Il ne serait pas juste de créer cela, cependant, il serait en fait créer deux classes pour chaque entité dans votre EOModel.

Un de ces deux classes a été le vôtre pour modifier comme bon vous semblait. Il ne serait jamais écrasé, même si vous avez exécuté le script EOGenerator encore, encore et encore. L'autre classe contient tout le code généré dérivé à partir de votre EOModel et a été conçu pour ne pas être touché par le programmeur. Vous avez eu votre classe, EOGenerator avait sa catégorie, et vous pourriez aussi bien le travail sans crainte de marcher sur les pieds. Vous pouvez ajouter des attributs et des entités de votre modèle de données et régénérer les classes lisibles à la machine sans aucune chance de perdre votre validation personnalisée ou de méthodes. Elle était belle.

Loup Rentzsch a réimplémenté cette idée pour Core Data, et le résultat est appelé MOGenerator. Si vous passez du temps avec les données de base et ne sont pas familiers avec MOGenerator, allez vous familiariser avec elle. Il vous rendra la vie beaucoup, beaucoup, beaucoup mieux.

SQLite pour vérifier les données


Le type de stockage par défaut persistant pour Core Data sous iOS est un magasin de SQLite, ce qui signifie que les données de base conserve l'ensemble de ses données à l'intérieur d'une seule base de données SQLite dans le dossier de votre application / Documents. Maintenant, le format du magasin persistant est sans papiers et vous ne devriez jamais, jamais, jamais, jamais, ne jamais changer les données dans le magasin persistant ou faire de suppositions sur la façon dont les données de base stocke les données. Cela fait partie des raisons pour lesquelles nous utilisons les données de base: Il s'agit d'une couche d'abstraction qui nous permet de ne pas se soucier des détails de stockage sous-jacent.

Mais, parfois, il peut être vraiment utile de savoir si des données est d'entrer dans le magasin persistant ou non. Je travaille souvent sur mon modèle de données avant que mon interface utilisateur, afin d'être en mesure de jeter un regard dans le magasin de données pour s'assurer que tout est bien, c'est vraiment utile.

La première chose que vous pourriez envisager de le faire, si vous prévoyez de le faire, est de créer un fichier config SQLite. Ceci est un fichier de commandes qui va exécuter chaque fois que vous lancez SQLite. Pour ce faire, il suffit de créer un fichier appelé .sqliterc dans votre répertoire home. Voici ce que la mine ressemble à:
.mode column
.header on
.table
Dans la mine, la première ligne indique SQLite pour imprimer les noms des colonnes quand je émettre une commande SELECT. La deuxième ligne indique SQLite pour présenter les données sous forme tabulaire. La dernière ligne affiche une liste de toutes les tables dans la base de données avant qu'il me montre la commande SQL rapide. C'est pratique, étant donné que les noms de table ne sont généralement pas correspondre exactement à votre entité ou les noms de classe.

Une fois que vous avez un fichier de configuration en place, vous pouvez ouvrir n'importe quel magasin persistante en utilisant la commande sqlite3 dans Terminal.app, comme ceci:
sqlite3 MyApplication.sqlite
Vous ne pouvez pas, bien sûr, exécuter cette base de données des fichiers directement contre sur votre téléphone, mais vous pouvez le faire dans le simulateur ainsi que sur les fichiers copiés à partir de votre téléphone en utilisant Xcode Organizer. Si vous utilisez le simulateur, vous pouvez rechercher vos bases de données SQLite à l'emplacement suivant:
/Users/[your account name]/Library/Application Support/iPhone Simulator/[SDK version #]/Applications/[application UUID]/Documents/[Application Name].sqlite


Lorsque je me connecte à une base de données avec mon fichier de config SQLite, c'est ce que je vois:
-- Loading resources from /Users/jeff/.sqliterc
ZHERO ZPOWER Z_METADATA Z_PRIMARYKEY
SQLite version 3.6.12
Enter ".help" for instructions
Enter SQL statements terminated with a ";"
sqlite>
Remarquez qu'il a jeté les noms de tous mes tableaux. Il est assez facile de trouver la table qui contient les données une fois qu'ils sont juste en face de vous. L'exemple ci-dessus est le stockage persistant de l'application du SuperDB Plus l'iPhone 3 de développement. L' Hero objets d'entité gérés sont stockés dans Zhero et les objets d'entité Puissance gérés sont stockés dans ZPOWER. Pour regarder le contenu d'une table, il vous suffit d'exécuter une commande SQL SELECT. Le plus simple est juste SELECT * FROM TABLENAME;, qui imprime le contenu entier de la table à la console, comme ceci:
sqlite> select * from ZPOWER;
Z_PK Z_ENT Z_OPT ZHERO ZSOURCE ZNAME
---------- ---------- ---------- ---------- ---------- ----------
1 2 2 1 Mutation Fire
sqlite>
Le format des données est assez facile à comprendre, mais là encore, vous devriez vraiment regarder et ne pas toucher. Ce serait très facile de détruire accidentellement un magasin persistant. Un changement pourrait apparemment sans obtenir les métadonnées détraqué et empêcher votre application d'utiliser le stockage persistant plus.

Mieux Descrption


J'ai remarqué il ya quelques jours que les Wil Shipley se plaignait du fait que beaucoup de UIKit livré Apple et objets de la Fondation n'ont pas annulé la description-méthode. Cette méthode est importante car c'est celle qui est utilisée dans les chaînes de format, NSLog (), et quand vous tapez la commande postale dans GDB. Au lieu d'obtenir une description significative de nombreux objets livrés, vous obtenez à la place du nom de classe et l'adresse mémoire de l'instance, ce qui est mise en œuvre NSObject offre, qui n'est pas très grande aide pendant le débogage.

Malheureusement, NSManagedObject est l'un des nombreux, de nombreuses classes qui ne possède pas sa propre implémentation significative de-description. Cela ne signifie pas que vous ne pouvez pas fournir un, cependant.

Une chose que vous pouvez faire est de créer une sous-classe abstraite de NSManagedObject et de mettre la description-il la méthode. Ensuite, si vous sous-classe de cette classe abstraite, tous vos objets gérés héritent de la méthode de la classe abstraite. Si j'ai d'autres fonctionnalités communes, je vais souvent faire exactement cela. Mais si je n'ai pas (et souvent vous ne serez pas) Personnellement, je déteste d'encombrer ma hiérarchie d'objets à ajouter des outils de débogage. Alors j'ai fait va tricher et faire quelque chose que vous n'êtes pas vraiment censé faire, ce qui est remplacer une méthode utilisant une catégorie. La raison c'est mauvais de faire cela parce que le langage Objective-C ne précise pas quelle méthode doit être utilisée, ce qui peut conduire à un comportement imprévisible. Mais, quand une classe hérite d'une méthode d'une superclasse, j'ai trouvé que c'est primordial dans une catégorie fonctionne toujours. Je ne suggère pas que vous devriez faire cela dans une application d'expédition, cependant. J'ai ma méthode envelopper dans le pré-compilateur définitions afin qu'il ne soit pas compilé dans la version de mon application.

Ma version de-description suppose que vous générez des propriétés de vos objets gérés (je fais toujours) et suppose également que vous utilisez les sous-classes personnalisées (mais vous êtes, parce que vous utilisez MOGenerator, Non??). La méthode itère sur toutes les propriétés de la classe et toutes ses super à NSManagedObject, puis les imprime sur la console. Les catégories que j'utilise ressemble à ceci:
#if DEBUG
@implementation NSObject(MCManagedObjectDebug)
+ (NSMutableArray *)MCproperties
{
NSMutableArray *properties = nil;

if ([self superclass] != [NSManagedObject class])
properties = [[self superclass] MCproperties];
else
properties = [NSMutableArray array];


unsigned int propCount;
objc_property_t * propList = class_copyPropertyList([self class], &propCount);
int i;

for (i=0; i < propCount; i++)
{
objc_property_t oneProp = propList[i];
NSString *propName = [NSString stringWithUTF8String:property_getName(oneProp)];
if (![properties containsObject:propName])
[properties addObject:propName];
}

return properties;
}

@end


@implementation NSManagedObject(MCManagedObjectDebug)
- (NSString *)description
{
NSArray *properties = [[self class] MCproperties];
NSMutableString *ret = [NSMutableString stringWithFormat:@"%@:", [self className]];
NSDictionary *myAttributes = [[self entity] attributesByName];

for (NSString *oneProperty in properties)
{
NSAttributeDescription *oneAttribute = [myAttributes valueForKey:oneProperty];
if (oneAttribute != nil) // If not, it's a relationship or fetched property
{
id value = [self valueForKey:oneProperty];
[ret appendFormat:@"\n\t%@ = %@", oneProperty, value];
}


}

return ret;
}


@end
#endif
La méthode MCProperties + utilise le runtime Objective-C à découvrir et à itérer sur les propriétés dans la définition de la classe, y compris les propriétés dynamiques @ qui ont été créés à l'exécution. Parce que les appels d'exécution bien ne comprennent pas les propriétés héritées de classes parentes, la méthode agit de façon récursive, itérer jusqu'à la hiérarchie des objets jusqu'à ce qu'il arrive à NSManagedObject. La méthode de description réelle-affiche simplement chacun des attributs et la valeur actuellement stockée pour cet attribut pour cette instance de l'objet. Une fois que vous faites cela, si vous utilisez NSLog () ou postale, le résultat est beaucoup plus riche d'informations sur les objets se imprimé sur la console. Cette version ne tient pas compte des relations et des propriétés récupérées, mais il ne serait pas difficile d'ajouter ceux si vous besoin d'eux.

Aucun commentaire: