如何将 NSMutable 数组中的数据存储在 Core Data 中?

Posted

技术标签:

【中文标题】如何将 NSMutable 数组中的数据存储在 Core Data 中?【英文标题】:How do you store data from NSMutable Array in Core Data? 【发布时间】:2014-10-14 11:38:01 【问题描述】:

我正在制作的应用程序根据来自CLLocationManager 的用户坐标绘制Polyline(这些都保存在NSMutableArray 中)

当应用关闭时,当前折线消失。如何在应用程序启动时将点数组存储在 CoreData 中以进行检索?自启动应用程序以来用户所采取的所有过去“路线”都应恢复并覆盖在地图上(可能很多,因此 CoreData 似乎是我收集的最佳选择)。

这是我用来从加载的坐标创建 MKPolyline 的代码

-(IBAction)didClickLoadCoordinates:(id)sender 
// get a reference to the appDelegate so you can access the global managedObjectContext
AppDelegate *appDelegate = [UIApplication sharedApplication].delegate;

NSFetchRequest *request = [[NSFetchRequest alloc] initWithEntityName:@"Route"];
NSError *error;
id results = [appDelegate.managedObjectContext executeFetchRequest:request error:&error];

if ([results count]) 
    polyLine = (Route *)(results[0]);
    NSArray *coordinates = polyLine.coordinates;
    int ct = 0;
    for (CLLocation *loc in coordinates) 
        NSLog(@"location %d: %@", ct++, loc);


    


    // this copies the array to your mutableArray
    _locationsArray = [coordinates mutableCopy];



NSInteger numberOfSteps = _locationsArray.count;

//convert to coordinates array to construct the polyline

CLLocationCoordinate2D clCoordinates[numberOfSteps];
for (NSInteger index = 0; index < numberOfSteps; index++) 
    CLLocation *location = [_locationsArray objectAtIndex:index];
    CLLocationCoordinate2D coordinate2 = location.coordinate;
    clCoordinates[index] = coordinate2;


MKPolyline *routeLine = [MKPolyline polylineWithCoordinates:clCoordinates count:numberOfSteps];
[_mapView addOverlay:routeLine];

【问题讨论】:

我使用可转换属性:samuelwford.com/mutable-transformable-properties-in-core-data。我实际上使用不可变属性,因为我保存到磁盘的情况并不经常发生。如果您确切知道何时保存到磁盘(即在应用程序关闭或注销时),您基本上可以将您的数组转换为 NSData 对象并将其存储到您的核心数据对象中。如果您想了解更多关于我使用的模式(包括对象上的类别),我可以发布它。 好的,谢谢。我将在应用程序关闭之前或关闭时保存数组(点)。你能给我更多关于将数组转换为 NSData 对象并将其保存在 Core Data 中的信息吗? @mitrenegade 好的,看起来你正在拉出核心数据中的第一条折线并显示它。当用户移动/创建新坐标时,您是否将其添加到同一个数组中?您是否有一个单独的数组,您可以将加载的坐标放入其中,然后添加新坐标?您可以每次将一组坐标保存到一条新的折线中并将其存储到核心数据中。然后,您可以存储一个 polylines = (NSArray *)results 数组,而不是仅使用 polyLine = (Route *)(results[0])。 或者,如果您不需要存储旧的折线,只需绘制它们,只需将我给您的代码放入一个迭代结果的数组中即可。 for ((Route *)route in results) ... 做你为 results(0) 所做的一切 所有的 cllocations 都存储在 locationsArray 中(并且加载的坐标也被复制到这个数组中)。由此,我得到了 CLLocationCoordinates2D。所以是的,只有一个数组加载了新坐标,并且也添加了新坐标。我想这就是加载的多段线与新更新的配置多段线连接的原因......我只需要将这些线分开,但仍然需要所有旧的多段线。把我的应用想象成一个巨大的折线网络,无论用户在哪里——但是当应用关闭时,显然会有差距 【参考方案1】:

我创建了一个简单的类,它在应用关闭时用数组保存折线,并在应用返回时查询所有折线的核心数据。我假设您知道如何设置核心数据和托管对象上下文。此模式允许您直接将 NSArray 对象设置和获取到核心数据对象中。其背后的基本原理在这里:https://coderwall.com/p/mx_wmq

首先,创建具有可转换属性(您的数组)和数据属性的模型。

接下来,在折线对象上创建一个类别。

您现在应该在文件浏览器中拥有这些项目

折线+TransformableAttributes.h

#import "Polyline.h"

@interface Polyline (TransformableAttributes)

#pragma mark transformables

-(NSArray *)coordinates;
-(void)setCoordinates:(id)coordinates;

@end

折线+TransformableAttributes.m

@implementation Polyline (TransformableAttributes)

#pragma mark Transformables
-(NSArray *)coordinates 
    if (!self.coordinates_data)
        return nil;

    return [NSKeyedUnarchiver unarchiveObjectWithData:self.coordinates_data];


-(void)setCoordinates:(id)coordinates 
    NSData *coordinates_data = [NSKeyedArchiver archivedDataWithRootObject:coordinates];
    [self setValue:coordinates_data forKey:@"coordinates_data"];


@end

Appdelegate.m

- (void)applicationDidBecomeActive:(UIApplication *)application

    NSFetchRequest *request = [[NSFetchRequest alloc] initWithEntityName:@"Polyline"];
    NSError *error;
    id results = [self.managedObjectContext executeFetchRequest:request error:&error];
    Polyline *polyline = (Polyline *)(results[0]);
    NSArray *coordinates = polyline.coordinates;



- (void)applicationWillResignActive:(UIApplication *)application

    NSManagedObject *object = [NSEntityDescription insertNewObjectForEntityForName:@"Polyline" inManagedObjectContext:self.managedObjectContext];
    Polyline *polyline = (Polyline *)object;
    [polyline setCoordinates:@[@"a", @"b", @"c"]];
    NSError *error;
    if ([self.managedObjectContext save:&error]) 
        NSLog(@"Saved");
    
    else 
        NSLog(@"Error: %@", error);
    

请让我知道它是否适合您。如果需要,我会更新我的答案,以便它有用。我不记得我最初是在哪里找到这个模式的,但它真的很有帮助,并且得到了高度评​​价

编辑 1:添加了 GPS 视图

这是我添加的新控制器:

GPSViewController.h:

#import <UIKit/UIKit.h>
#import <CoreLocation/CoreLocation.h>
#import "Polyline+TransformableAttributes.h"

@interface GPSViewController : UIViewController <CLLocationManagerDelegate>

    NSMutableArray *_locationsArray;
    Polyline *polyLine;
    CLLocationManager *locationManager;


-(IBAction)didClickStartGPS:(id)sender;
-(IBAction)didClickSaveCoordinates:(id)sender;
-(IBAction)didClickLoadCoordinates:(id)sender;

我的 GPSViewController.m 中的代码:

初始化数组以存储我的坐标。

- (void)viewDidLoad

    [super viewDidLoad];
    // Do any additional setup after loading the view.

    _locationsArray = [NSMutableArray array];

当您单击 GPS 按钮时,它会转到此处。 locationManager 是类的实例变量。

-(IBAction)didClickStartGPS:(id)sender 
    locationManager = [[CLLocationManager alloc] init];
    [locationManager setDelegate:self];
    [locationManager startUpdatingLocation];

这会将坐标保存到折线中并保持不变。注意:使用这段代码,我不做任何特定的搜索描述符,所以如果你多次点击保存,你会在你的核心数据中得到一堆折线,而且每次可能只会加载第一个。如果您将某个 id 或日期添加到折线对象,您可以执行诸如搜索它们的操作。

-(IBAction)didClickSaveCoordinates:(id)sender 
    /*
     NSInteger numberOfSteps = _locationsArray.count;
     // you don't need to convert it to a coordinates array.
     CLLocationCoordinate2D coordinates[numberOfSteps];
     for (NSInteger index = 0; index < numberOfSteps; index++) 
     CLLocation *location = [_locationsArray objectAtIndex:index];
     CLLocationCoordinate2D coordinate2 = location.coordinate;
     coordinates[index] = coordinate2;
     
     */

    // get a reference to the appDelegate so you can access the global managedObjectContext
    AppDelegate *appDelegate = [UIApplication sharedApplication].delegate;

    // creates a new polyline object when app goes into the background, and stores it into core data.
    if (!polyLine) 
        NSManagedObject *object = [NSEntityDescription insertNewObjectForEntityForName:@"Polyline" inManagedObjectContext:appDelegate.managedObjectContext];
        polyLine = (Polyline *)object;
    

    [polyLine setCoordinates:_locationsArray];
    NSError *error;
    if ([appDelegate.managedObjectContext save:&error]) 
        NSLog(@"Saved");
    
    else 
        NSLog(@"Error: %@", error);
    

这会从核心数据加载第一个折线对象并将其转换为 CLLocations 的 _locationArray。我不会对你可以从他们那里得到的 CLLocationCoordinate2D 做任何事情。

-(IBAction)didClickLoadCoordinates:(id)sender 
    // get a reference to the appDelegate so you can access the global managedObjectContext
    AppDelegate *appDelegate = [UIApplication sharedApplication].delegate;

    NSFetchRequest *request = [[NSFetchRequest alloc] initWithEntityName:@"Polyline"];
    NSError *error;
    id results = [appDelegate.managedObjectContext executeFetchRequest:request error:&error];

    if ([results count]) 
        polyLine = (Polyline *)(results[0]);
        NSArray *coordinates = polyLine.coordinates;
        int ct = 0;
        for (CLLocation *loc in coordinates) 
            NSLog(@"location %d: %@", ct++, loc);
        

        // this copies the array to your mutableArray
        _locationsArray = [coordinates mutableCopy];
    


- (void)locationManager:(CLLocationManager *)manager didUpdateLocations:(NSArray *)locations 
    CLLocation *currentLocation = [locations lastObject];
    CLLocationDegrees latitude = currentLocation.coordinate.latitude;
    CLLocationDegrees longitude = currentLocation.coordinate.longitude;
    CLLocationCoordinate2D locationCoordinates = CLLocationCoordinate2DMake(latitude, longitude);

    //store latest location in stored track array;
    [_locationsArray addObject:currentLocation];

这段代码更新在我的github上:

github.com/bobbyren/***Test.git

编辑:为每条折线添加新的 MKPolyline:

NSArray *polylines = [fetchedResultsController allObjects];
for (Polyline *polyline in polylines) 
    MKPolyline *mkPolyline = [MKPolyline polylineWithCoordinates:polyline.coordinates count:ct]; // assuming you have written out how to return polyline.coordinates as a CLLocationCoordinate2D[]
    [mapView addOverlay:line];


[mapView reloadData];

【讨论】:

我是一个初学者,所以我目前正在阅读核心数据和管理对象等。标记为已回答图像和详尽的细节 - 将很快阅读和实施。非常感谢! 好的,那么我认为对于您的情况,请在此处尝试更简单的解决方案:coderwall.com/p/mx_wmq。您基本上在保存核心数据时将数组转换为 nsdata,并在加载它并需要访问数组时取消存档。 我认为数组不能直接存储在核心数据中? ***.com/questions/4546811/… 在你的核心数据对象模型中,你可以有一个 NSData 属性。这本质上是将您的数组编码为原始数据字节。每次从 sql 数据库保存和加载核心数据时,对其进行编码或解码并将其设置为数组。我的模式使您可以访问 NSArray 属性,该属性不会保存到核心数据对象中,但可以作为 polyline.coordinateArray 直接访问。 我已经在我的项目中实现了 Core Data 框架,我只是想知道是否需要一个自定义类来绘制折线。在我的应用程序中,这一切都是通过 ViewController 中的方法完成的……您建议创建一个自定义类吗?谢谢

以上是关于如何将 NSMutable 数组中的数据存储在 Core Data 中?的主要内容,如果未能解决你的问题,请参考以下文章

如何将NSMutable数组附加到现有文件中

如何将 NSString 值设置为 NSMutable 字典?

NSUserDefaults NSMutable 数组未在会话之间保存

将对象添加到排序的 NSMutable 数组并回答索引路径

如何复制位于不同选项卡中的数组对象

过滤自定义对象的 NSMutable 数组