认识CoreData-基础使用

作者: 刘小壮 | 来源:发表于2016-07-22 19:16 被阅读15508次
    该文章属于<简书 — 刘小壮>原创,转载请注明:

    <简书 — 刘小壮> http://www.jianshu.com/p/0ddfa35c7898


    第一篇文章中并没有讲CoreData的具体用法,只是对CoreData做了一个详细的介绍,算是一个开始和总结吧。

    这篇文章中会主要讲CoreData的基础使用,以及在使用中需要注意的一些细节。因为文章中会插入代码和图片,内容可能会比较多,比较考验各位耐心。

    文章中如有疏漏或错误,还请各位及时提出,谢谢!😊`


    占位图

    创建自带CoreData的工程

    在新建一个项目时,可以勾选Use Core Data选项,这样创建出来的工程系统会默认生成一些CoreData的代码以及一个.xcdatamodeld后缀的模型文件,模型文件默认以工程名开头。这些代码在AppDelegate类中,也就是代表可以在全局使用AppDelegate.h文件中声明的CoreData方法和属性。

    系统默认生成的代码是非常简单的,只是生成了基础的托管对象模型、托管对象上下文、持久化存储调度器,以及MOCsave方法。但是这些代码已经可以完成基础的CoreData操作了。

    系统生成代码

    这部分代码不应该放在AppDelegate中,尤其对于大型项目来说,更应该把这部分代码单独抽离出去,放在专门的类或模块来管理CoreData相关的逻辑。所以我一般不会通过这种方式创建CoreData,我一般都是新建一个“干净”的项目,然后自己往里面添加,这样对于CoreData的完整使用流程掌握的也比较牢固。


    CoreData模型文件的创建

    构建模型文件

    使用CoreData的第一步是创建后缀为.xcdatamodeld的模型文件,使用快捷键Command + N,选择Core Data -> Data Model -> Next,完成模型文件的创建。

    创建完成后可以看到模型文件左侧列表,有三个选项EntitiesFetch RequestsConfigurations,分别对应着实体、请求模板、配置信息。

    模型文件

    添加实体

    现在可以通过长按左侧列表下方的Add Entity按钮,会弹出Add EntityAdd Fetch RequestAdd Configuration选项,可以添加实体、请求模板、配置信息。这里先选择Add Entity来添加一个实体,命名为Person

    添加Person实体后,会发现一个实体对应着三部分内容,AttributesRelationshipsFetched Properties,分别对应着属性、关联关系、获取操作。

    空实体

    现在对Person实体添加两个属性,添加age属性并设置typeInteger 16,添加name属性并设置typeString

    添加属性
    实体属性类型

    在模型文件的实体中,参数类型和平时创建继承自NSObject的模型类大体类似,但是还是有一些关于类型的说明,下面简单的列举了一下。

    • Undefined: 默认值,参与编译会报错
    • Integer 16: 整数,表示范围 -32768 ~ 32767
    • Integer 32: 整数,表示范围 -2147483648 ~ 2147483647
    • Integer 64: 整数,表示范围 –9223372036854775808 ~ 9223372036854775807
    • Float: 小数,通过MAXFLOAT宏定义来看,最大值用科学计数法表示是 0x1.fffffep+127f
    • Double: 小数,小数位比Float更精确,表示范围更大
    • String: 字符串,用NSString表示
    • Boolean: 布尔值,用NSNumber表示
    • Date: 时间,用NSDate表示
    • Binary Data: 二进制,用NSData表示
    • Transformable: OC对象,用id表示。可以在创建托管对象类文件后,手动改为对应的OC类名。使用的前提是,这个OC对象必须遵守并实现NSCoding协议

    添加实体关联关系

    创建两个实体DepartmentEmployee,并且在这两个实体中分别添加一些属性,下面将会根据这两个实体来添加关联关系。

    创建实体

    Employee实体添加关系,在Relationships的位置点击加号,添加一个关联关系。添加关系的名称设为department,类型设置为DepartmentInverse设置为employee(后面会讲解这个inverse的作用)。

    添加Relationships

    选择Department实体,点击Relationships位置的加号,添加关联关系。(需要注意的是,inverse需要设置好Relationships之后才能设置)

    Department实体添加Relationships的操作和Employee都一样,区别在于用红圈标出的Type,这里设置的To Many一对多的关系。这里默认是To One一对一,上面的Employee就是一对一的关系。也就符合一个Department可以有多个Employee,而Employee只能有一个Department的情况,这也是符合常理的。

    添加Relationships

    Relationships类似于SQLite的外键,定义了在同一个模型中,实体与实体之间的关系。可以定义为对一关系或对多关系,也可以定义单向或双向的关系,根据需求来确定。如果是对多的关系,默认是使用NSSet集合来存储模型。

    Inverse是两个实体在Relationships中设置关联关系后,通过设置inverse为对应的实体,这样可以从一个实体找到另一个实体,使两个实体具有双向的关联关系。

    Fetched Properties

    在实体最下面,有一个Fetched Properties选项,这个选项用的不多,这里就不细讲了。

    Fetched Properties用于定义查询操作,和NSFetchRequest功能相同。定义fetchedProperty对象后,可以通过NSManagedObjectModel类的fetchRequestFromTemplateWithName:substitutionVariables:方法或其他相关方法获取这个fetchedProperty对象。

    fetched Property

    获取这个对象后,系统会默认将这个对象缓存到一个字典中,缓存之后也可以通过fetchedProperty字典获取fetchedProperty对象。

    Data Model Inspector

    选中一个实体后,右侧的侧边栏(Data Model Inspector)还有很多选项,这些选项可以对属性进行配置。根据不同的属性类型,侧边栏的显示也不太一样,下面是一个String类型的属性。

    Data Model Inspector
    属性设置
    • default Value: 设置默认值,除了二进制不能设置,其他类型几乎都能设置。

    • optional: 在使用时是否可选,也可以理解为如果设置为NO,只要向MOC进行save操作,这个属性是否必须有值。否则MOC进行操作时会失败并返回一个error,该选项默认为YES

    • transient: 设置当前属性是否只存在于内存,不被持久化到本地,如果设置为YES,这个属性就不参与持久化操作,属性的其他操作没有区别。transient非常适合存储一些在内存中缓存的数据,例如存储临时数据,这些数据每次都是不同的,而且不需要进行本地持久化,所以可以声明为transient的属性。

    • indexed: 设置当前属性是否是索引。添加索引后可以有效的提升检索操作的速度。但是对于删除这样的操作,删除索引后其他地方还需要做出相应的变化,所以速度会比较慢。

    • Validation: 通过Validation可以设置Max ValueMin Value,通过这两个条件来约定数据,对数据的存储进行一个验证。数值类型都有相同的约定方式,而字符串则是约定长度,date是约定时间。

    • Reg. Ex.(Regular Expression): 可以设置正则表达式,用来验证和控制数据,不对数据自身产生影响。(只能应用于String类型)

    • Allows External Storage: 当存储二进制文件时,如果遇到比较大的文件,是否存储在存储区之外。如果选择YES,存储文件大小超过1MB的文件,都会存储在存储区之外。否则大型文件存储在存储区内,会造成SQLite进行表操作时,效率受到影响。

    Relationships设置
    • delete rule: 定义关联属性的删除规则。在当前对象和其他对象有关联关系时,当前对象被删除后与之关联对象的反应。这个参数有四个枚举值,代码对应着模型文件的相同选项。

    NSNoActionDeleteRule 删除后没有任何操作,也不会将关联对象的关联属性指向nil。删除后使用关联对象的关联属性,可能会导致其他问题。
    NSNullifyDeleteRule 删除后会将关联对象的关联属性指向nil,这是默认值。
    NSCascadeDeleteRule 删除当前对象后,会将与之关联的对象也一并删除。
    NSDenyDeleteRule 在删除当前对象时,如果当前对象还指向其他关联对象,则当前对象不能被删除。

    • Type: 主要有两种类型,To OneTo Many,表示当前关系是一对多还是一对一。
    实体
    • Parent Entity: 可以在实体中创建继承关系,在一个实体的菜单栏中通过Parent Entity可以设置父实体,这样就存在了实体的继承关系,最后创建出来的托管模型类也是具有继承关系的。注意继承关系中属性名不要相同。
      使用了这样的继承关系后,系统会将子类继承父类的数据,存在父类的表中,所有继承自同一父类的子类都会将父类部分存放在父类的表中。这样可能会导致父类的表中数据量过多,造成性能问题。

    Fetch Requests

    在模型文件中Entities下面有一个Fetch Requests,这个也是配置请求对象的。但是这个使用起来更加直观,可以很容易的完成一些简单的请求配置。相对于上面讲到的Fetched Properties,这个还是更方便使用一些。

    Fetch Requests

    上面是对Employee实体的height属性配置的Fetch Request,这里配置的height小于2米。配置之后可以通过NSManagedObjectModel类的fetchRequestTemplateForName:方法获取这个请求对象,参数是这个请求配置的名称,也就是EmployeeFR

    Editor Style

    这是我认为CoreData最大的优势之一,可视化的模型文件结构。可以很清楚的看到实体和属性的关系,以及实体之间的对应关系。

    Editor Style

    一个.xcdatamodeld模型文件的展示风格有两种,一种是列表的形式(Table),另一种是图表的形式展示(Graph)。

    图表看起来更加直观,而图表在操作上也有一些比Table更方便的地方。例如在Table的状态下添加两个实体的关联关系,如果只做一次关联操作,默认是单向的关系。而在Graph的状态下,按住Control对两个图表进行连线,两个实体的结果就是双向关联的关系。

    手动创建实体

    假设不使用.xcdatamodeld模型文件,全都是纯代码,怎么在项目里创建实体啊?这样的话就需要通过代码创建实体描述、关联描述等信息,然后设置给NSManagedObjectModel对象。而使用模型文件的话一般都是通过NSManagedObjectModel对象来读取文件。

    如果是纯代码的话,苹果更推荐使用KVC的方式存取值,然后所有托管对象都用NSManagedObject创建。但是这样存在的问题很多,开发成本比较大、使用不方便等等。最大的问题就是写属性名的key字符串,很容易出错,而且这样失去了CoreData原有的优点。所以还是推荐使用.xcdatamodeld模型文件的开发方式。

    创建托管对象类文件

    创建文件

    创建实体后,就可以根据对应的实体,生成开发中使用的基于NSManagedObject类的托管对象类文件。

    还是按照上面DepartmentEmployee的例子,先创建一个Department实体。因为Department实体有对多关系,生成托管对象类文件的关联属性不一样,可以体现出和对一关系的区别,所以使用Department实体生成文件。

    点击后缀名为.xcdatamodeld的模型文件,选择XcodeEditor -> Create NSManagedObject Subclass -> 选择模型文件 -> 选择实体,生成Department实体对应的托管对象类文件。

    生成的托管对象类文件

    可以看到上面生成了四个文件,以实体名开头的.h.m文件,另外两个是这个实体的Category文件。为什么生成Category文件?一会再说,先打开类文件进去看看。

    Category

    实体Category

    可以看到类文件中有两个Category,分别是CoreDataPropertiesCoreDataGeneratedAccessors。其中如果没有设置对多关系的实体,只会有CoreDataProperties,而设置了对多关系的实体系统会为其生成CoreDataGeneratedAccessors

    CoreDataProperties中会生成实体中声明的AttributesRelationships中的属性,其中对多关系是用NSSet存储的属性,如果是对一的关系则是非集合的对象类型属性。再看.m文件中,所有属性都用@dynamic修饰,CoreData会在运行时动态为所有Category中的属性生成实现代码,所以这里用@dynamic修饰。

    对多属性生成的CoreDataGeneratedAccessors,是系统自动生成管理对多属性集合的方法,一般都是一个属性对应四个方法,方法的实现也是在运行时动态实现的,方法都是用来操作集合对象的。

    托管对象类文件

    点击系统生成的托管对象类文件,此类是继承自NSManagedObject类的。可以看到里面非常干净,没有其他逻辑代码。

    根据苹果的注释代码:Insert code here to declare functionality of your managed object subclass,提示应该在这个文件中编写此类相关的逻辑代码。这里就是编写此类逻辑代码的地方,当然也可以什么都不写,看需求啦。

    任意类型属性

    实体支持创建任意继承自NSObject类的属性,例如项目中手动创建的类。项目中创建的类在下拉列表中并不会体现,可以在属性类型选择transformable类型,然后生成托管对象类文件的时候,系统会将这个属性声明为id类型,在创建类文件后,可以直接手动更改这个属性的类型为我们想要的类型。

    对于手动设置的属性有一个要求,属性所属的类必须是遵守NSCoding协议,因为这个属性要被归档到本地。

    标量类型

    创建托管对象类文件时,实体属性的类型无论是选择的integer32还是float,只要是基础数据类型,最后创建出来的默认都是NSNumber类型的,这是Xcode默认的。

    如果需要生成的属性类型是基础数据类型,可以在创建文件时勾选Use scalar properties for primitive data types选项,这样就告诉系统需要生成标量类型属性,创建出来的属性就是int64_tfloat这样的基础数据类型。

    标量类型

    更新文件

    当前模型对应的实体发生改变后,需要重新生成模型Category文件。生成步骤和上面一样,主要是替换Category文件,托管对象文件不会被替换。生成文件时不需要删除,直接替换文件


    CoreData增删改查

    下面关于CoreData的相关操作,还是基于上面DepartmentEmployee的例子。并且引入了Company当做.xcdatamodeld模型文件,前面两个实体被包含在Company中。

    先讲讲NSManagedObjectContext

    iOS5之前创建NSManagedObjectContext对象时,都是直接通过init方法来创建。iOS5之后苹果更加推荐使用initWithConcurrencyType:方法来创建,在创建的时候指定当前是什么类型的并发队列,初始化方法参数是一个枚举值。这里简单说说MOC,后面多线程部分还会涉及MOC多线程相关的东西。

    NSManagedObjectContext初始化方法的枚举值参数主要有三个类型:

    • NSConfinementConcurrencyType 如果使用init方法初始化上下文,默认就是这个并发类型。在iOS9之后已经被苹果废弃,不建议用这个API,调用某些比较新的CoreDataAPI可能会导致崩溃。

    • NSPrivateQueueConcurrencyType 私有并发队列类型,操作都是在子线程中完成的。

    • NSMainQueueConcurrencyType 主并发队列类型,如果涉及到UI相关的操作,应该考虑使用这个参数初始化上下文。

    如果还使用init方法,可能会对后面推出的一些API不兼容,导致多线程相关的错误。例如下面的错误,因为如果没有显式的设置并发类型,默认是一个已经弃用的NSConfinementConcurrencyType类型,就会导致新推出的API发生不兼容的崩溃错误。

    Terminating app due to uncaught exception 'NSInvalidArgumentException', reason: 'NSConfinementConcurrencyType context
    

    创建MOC

    下面是根据Company模型文件,创建了一个主队列并发类型的MOC

    // 创建上下文对象,并发队列设置为主队列
    NSManagedObjectContext *context = [[NSManagedObjectContext alloc] initWithConcurrencyType:NSMainQueueConcurrencyType];
    
    // 创建托管对象模型,并使用Company.momd路径当做初始化参数
    NSURL *modelPath = [[NSBundle mainBundle] URLForResource:@"Company" withExtension:@"momd"];
    NSManagedObjectModel *model = [[NSManagedObjectModel alloc] initWithContentsOfURL:modelPath];
    
    // 创建持久化存储调度器
    NSPersistentStoreCoordinator *coordinator = [[NSPersistentStoreCoordinator alloc] initWithManagedObjectModel:model];
    
    // 创建并关联SQLite数据库文件,如果已经存在则不会重复创建
    NSString *dataPath = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES).lastObject;
    dataPath = [dataPath stringByAppendingFormat:@"/%@.sqlite", @"Company"];
    [coordinator addPersistentStoreWithType:NSSQLiteStoreType configuration:nil URL:[NSURL fileURLWithPath:dataPath] options:nil error:nil];
    
    // 上下文对象设置属性为持久化存储器
    context.persistentStoreCoordinator = coordinator;
    

    这段代码创建了一个MOC,我们从上往下看这段代码。

    momd文件

    关于MOC的并发队列类型上面已经简单说了,MOC下面出现了momd的字样,这是什么东西?

    momd文件

    在创建后缀为.xcdatamodeld的模型文件后,模型文件在编译期将会被编译为后缀为.momd的文件,存放在.app中,也就是Main Bundle中。在存在多个模型文件时,我们需要通过加载不同的.momd文件,来创建不同的NSManagedObjectModel对象,每个NSManagedObjectModel对应着不同的模型文件。

    NSManagedObjectModel类中包含了模型文件中的所有entitiesconfigurationsfetchRequests的描述。虽然.momd文件是支持存放在.app中的,其他人可以通过打开.app包看到这个文件。但是这个文件是经过编码的,并不会知道这个.momd文件中的内容,所以这个文件是非常安全的。通过NSManagedObjectModel获取模型文件描述后,来创建和关联数据库,并交给PSC管理。

    如果不指定NSManagedObjectModel对应哪个模型文件,直接使用init方法初始化NSManagedObjectModel类,系统会默认将所有模型文件的表都放在一个SQLite数据库中。所以需要使用mainBundle中的不同.momd文件,对不同的NSManagedObjectModel进行初始化,这样在创建数据库时就会创建不同的数据库文件。

    持久化存储调度器(PSC)

    NSManagedObjectModel下面就是NSPersistentStoreCoordinator,这个类在CoreData框架体系中起到了“中枢”的作用。对上层起到了提供简单的调用接口,并向上层隐藏持久化实现逻辑。对下层起到了协调多个持久化存储对象(NSPersistentStore),使下层只需要专注持久化相关逻辑。

    持久化存储调度器

    addPersistentStoreWithType: configuration: URL: options: error:方法是PSC创建并关联数据库的部分,关联本地数据库后会返回一个NSPersistentStore类型对象,这个对象负责具体持久化存储的实现。可以看到这个方法是一个实例方法,也就是可以添加多个持久化存储对象,并且多个持久化存储对象都关联一个PSC,这是允许的,在上面的图中也看到了这样的结构。但是这样的需求并不多,而且管理起来比较麻烦,一般都不会这样做。

    PSC有四种可选的持久化存储方案,用得最多的是SQLite的方式。其中BinaryXML这两种方式,在进行数据操作时,需要将整个文件加载到内存中,这样对内存的消耗是很大的。

    • NSSQLiteStoreType : SQLite数据库
    • NSXMLStoreType : XML文件
    • NSBinaryStoreType : 二进制文件
    • NSInMemoryStoreType : 直接存储在内存中

    插入操作

    // 创建托管对象,并指明创建的托管对象所属实体名
    Employee *emp = [NSEntityDescription insertNewObjectForEntityForName:@"Employee" inManagedObjectContext:context];
    emp.name = @"lxz";
    emp.height = @1.7;
    emp.brithday = [NSDate date];
    
    // 通过上下文保存对象,并在保存前判断是否有更改
    NSError *error = nil;
    if (context.hasChanges) {
        [context save:&error];
    }
    
    // 错误处理
    if (error) {
        NSLog(@"CoreData Insert Data Error : %@", error);
    }   
    

    通过NSEntityDescriptioninsert类方法,生成并返回一个Employee托管对象,并将这个对象插入到指定的上下文中。

    MOC将操作的数据存放在缓存层,只有调用MOCsave方法后,才会真正对数据库进行操作,否则这个对象只是存在内存中,这样做避免了频繁的数据库访问。

    删除操作

    // 建立获取数据的请求对象,指明对Employee实体进行删除操作
    NSFetchRequest *request = [NSFetchRequest fetchRequestWithEntityName:@"Employee"];
    
    // 创建谓词对象,过滤出符合要求的对象,也就是要删除的对象
    NSPredicate *predicate = [NSPredicate predicateWithFormat:@"name = %@", @"lxz"];
    request.predicate = predicate;
    
    // 执行获取操作,找到要删除的对象
    NSError *error = nil;
    NSArray<Employee *> *employees = [context executeFetchRequest:request error:&error];
    
    // 遍历符合删除要求的对象数组,执行删除操作
    [employees enumerateObjectsUsingBlock:^(Employee * _Nonnull obj, NSUInteger idx, BOOL * _Nonnull stop) {
        [context deleteObject:obj];
    }];
    
    // 保存上下文
    if (context.hasChanges) {
        [context save:nil];
    }
    
    // 错误处理
    if (error) {
        NSLog(@"CoreData Delete Data Error : %@", error);
    }
    

    首先获取需要删除的托管对象,遍历获取的对象数组,逐个删除后调用MOCsave方法保存。

    修改操作

    // 建立获取数据的请求对象,并指明操作的实体为Employee
    NSFetchRequest *request = [NSFetchRequest fetchRequestWithEntityName:@"Employee"];
    
    // 创建谓词对象,设置过滤条件
    NSPredicate *predicate = [NSPredicate predicateWithFormat:@"name = %@", @"lxz"];
    request.predicate = predicate;
    
    // 执行获取请求,获取到符合要求的托管对象
    NSError *error = nil;
    NSArray<Employee *> *employees = [context executeFetchRequest:request error:&error];
    [employees enumerateObjectsUsingBlock:^(Employee * _Nonnull obj, NSUInteger idx, BOOL * _Nonnull stop) {
        obj.height = @3.f;
    }];
    
    // 将上面的修改进行存储
    if (context.hasChanges) {
        [context save:nil];
    }
    
    // 错误处理
    if (error) {
        NSLog(@"CoreData Update Data Error : %@", error);
    }
    

    和上面一样,首先获取到需要更改的托管对象,更改完成后调用MOCsave方法持久化到本地。

    查找操作

    // 建立获取数据的请求对象,指明操作的实体为Employee
    NSFetchRequest *request = [NSFetchRequest fetchRequestWithEntityName:@"Employee"];
    
    // 执行获取操作,获取所有Employee托管对象
    NSError *error = nil;
    NSArray<Employee *> *employees = [context executeFetchRequest:request error:&error];
    [employees enumerateObjectsUsingBlock:^(Employee * _Nonnull obj, NSUInteger idx, BOOL * _Nonnull stop) {
        NSLog(@"Employee Name : %@, Height : %@, Brithday : %@", obj.name, obj.height, obj.brithday);
    }];
    
    // 错误处理
    if (error) {
        NSLog(@"CoreData Ergodic Data Error : %@", error);
    }
    

    查找操作最简单粗暴,因为是演示代码,所以直接将所有Employee表中的托管对象加载出来。在实际开发中肯定不会这样做,只需要加载需要的数据。后面还会讲到一些更高级的操作,会涉及到获取方面的东西。

    总结

    CoreData中所有的托管对象被创建出来后,都是关联着MOC对象的。所以在对象进行任何操作后,都会被记录在MOC中。在最后调用MOCsave方法后,MOC会将操作交给PSC去处理,PSC将会将这个存储任务指派给NSPersistentStore对象。

    上面的增删改查操作,看上去大体流程都差不多,都是一些最基础的简单操作,在下一篇文章中将会将一些比较复杂的操作。


    好多同学都问我有Demo没有,其实文章中贴出的代码组合起来就是个Demo。后来想了想,还是给本系列文章配了一个简单的Demo,方便大家运行调试,后续会给所有博客的文章都加上Demo

    Demo只是来辅助读者更好的理解文章中的内容,应该博客结合Demo一起学习,只看Demo还是不能理解更深层的原理Demo中几乎每一行代码都会有注释,各位可以打断点跟着Demo执行流程走一遍,看看各个阶段变量的值。

    Demo地址刘小壮的Github


    这两天更新了一下文章,将CoreData系列的六篇文章整合在一起,做了一个PDF版的《CoreData Book》,放在我Github上了。PDF上有文章目录,方便阅读。

    如果你觉得不错,请把PDF帮忙转到其他群里,或者你的朋友,让更多的人了解CoreData,衷心感谢!😁

    相关文章

      网友评论

      • Stroman:你不应该简单明了的图示之间穿插那么多文字
      • 最晴天:我创建了一个Person基类,Student和Teacher继承自Person,在生成实体对应的托管对象类文件。Student+CoreDataClass的.h文件中,Person.h报错not found,怎么解决? 另外假设在项目中对网络请求拿到的数据已经解析成了某个Model,那么我在coreData中创建了一个类似的model,能否将这两个model关联?
        刘小壮:@青草不枯 如果新建一个工程,然后设置Parent Entity呢。我感觉可能是其他地方影响的。
        最晴天:@刘小壮 设置Parent Entity 确实报错了!尝试过好几次都是这样
        刘小壮:1. 通过设置Parent Entity的方式,不会报错。
        2. 请求下来并解析为Model后,先save再取出来。据我所知,应该是没有两个Model关联的API。
      • King_iOS:改 查功能好像失效了 能更新下吗?
        King_iOS:@刘小壮 是GitHub上的demo 改查功能方法打断点不走
        刘小壮:这些API应该好长时间没变了,不会失效吧。我有时间试一下,多谢提醒。
        刘小壮:你跑我Github上Demo了吗,那个失效了?
      • ifelseboyxx:找了很多,这篇入门最详细!建议好文章如果有空可以更新下,毕竟现在已经快 iOS 11 了~:smile:
        刘小壮:感谢肯定,最近在学英语,过段时间统一更新一下。
      • yinxing29:您好,博主,我按照你写的然后进行到“更新文件”这一步的时候,我修改了.xcdatamodeld中实体的参数,然后重新 Create NSManagedObject Subclass 生成文件,然后运行,会直接崩溃,说没有持久储存。
        yinxing29:@刘小壮 好的,谢谢。
        刘小壮:这个涉及到数据迁移的问题,你可以再往后看看,后面讲了新加实体属性。这是一系列文章,总共六篇。
      • 麒麟欢: 楼主 麻烦问一下! xcode8 用 coreData 然后 仅仅是 用xcode 的 create NSManagerObject subclass 就会出现编译错误!linker command failed with exit code 1 .请问下 这是为什么啊?
        刘小壮:@麒麟欢 :smile:
        麒麟欢:@刘小壮 恩 是 Entity 的 codegen 系统默认为是 class Definition 我改成了 Manual/None 就好了! 😁
        刘小壮:错误原因是文件报错,可能是因为文件重复、只有Category之类的,你可以检查一下。
      • 九月哈哈哈大魔王:你好我想问下CoreData存一个实体类数组的时候退出程序数组内容会丢失,必须在applicationDidEnterBackground:(UIApplication *)application这个方法里面重新调一边context save 这个问题是什么原因?
        麒麟欢:楼主 麻烦问一下! xcode8 用 coreData 然后 仅仅是 用xcode 的 create NSManagerObject subclass 就会出现编译错误!linker command failed with exit code 1 .请问下 这是为什么啊?
        刘小壮:可能是你程序被kill掉了,然后在内存里的数据就丢失了。CoreData的数据,你如果不调用save方法,是存储在内存中的,调用save之后才缓存到disk。
      • 风情万种杀猪刀:您好,手动创建实体的时候,我找不到scalar properties for primitive data types的选项
        刘小壮:@动聽的心律 还真是,我下了个8.2.1版本,发现还真没有Use scalar properties for primitive data types选项了。我当时用的8.0版本还有呢,可能是苹果不想让代码里出现基础数据类型吧。
        风情万种杀猪刀:@刘小壮 Editor -> Create NSManagedObject Subclass -> 选择模型文件 -> 选择实体,只有Group与Targets这两个选项,按道理来说还有一个Use scalar properties for primitive data types 这个选项,可惜找不到这个选项,创建出来的分别是Item+CoreDataClass.h,Item+CoreDataClass.m与Item+CoreDataProperties.h,Item+CoreDataProperties.m 这四个文件,其中Item是实体的名称,我不明白Item+CoreDataClass.h,Item+CoreDataClass.m会有+CoreDataClass这个东西,与没有有什么区别?我用的是Xcode8.2
        刘小壮:这个不应该啊,创建.h和.m文件的时候,肯定会出现的。你是不是创建的时候,创建方式不对,选择普通的class文件的创建方式了。
      • a24ccb8d4e9e:楼主你好,我在按照上面做的时候,在这里:
        “可以看到上面生成了四个文件,以实体名开头的.h和.m文件,另外两个是这个实体的Category文件。”
        可是我生成的四个文件是Department+CoreDataClass.m和.h,Department+CoreDataProperties.m和.h。不是很明白哪里出问题了,并且之后的步骤就对不上号了。
        a24ccb8d4e9e:楼主,不好意思哈,我想再问一个问题。我把之前的项目删除之后又新建了一个也选中了Use Core Data,但是新项目的AppDelegate里并没有NSManagedObjectModel和NSManagedObjectContent属性,只有一个NSPersistentContainer,括号里面还没有nonatomic,想请问一下这是什么情况。
        a24ccb8d4e9e:@刘小壮 哦哦,谢谢楼主~
        刘小壮:不应该都是Category,应该是Category的两个文件和原类的两个文件,总共四个。我文章里表达的是这个意思。
      • 空壳子XJ:为毛我创建出来的托管对象文件是swift的
        刘小壮:@空壳子XJ :blush:
        空壳子XJ:@刘小壮 恩恩,刚学,不太懂,已经解决了,谢谢
        刘小壮:是因为创建的时候,选择语言的问题吧。
      • 小蜗牛吱呀之悠悠:可以在创建托管对象类文件后,手动改为对应的OC类名。使用的前提是,这个OC对象必须遵守并实现NSCoding协议,这里还需要对该对象做归档和解档的操作吗
        刘小壮:是要重写NSCoding的代理方法的。
      • Ezward_Chou:拜读文章,您的文章真心是最完整详细的,有基本理论知识,也有实际操作,我翻看了十几篇blog,有些空谈理论,有些说是干货,但是还是莫名其妙的给出代码,有些地方完全是错的.谢啦
        刘小壮:@EdwardChou 谢谢
      • fdd0543b6141:sectionName是什么属性...
        刘小壮:@xglzs 表视图section的title
      • 5a2f141238f3:创建文件
        创建实体后,就可以根据对应的实体,生成开发中使用的基于NSManagedObject类的托管对象类文件。
        还是按照上面Department和Employee的例子,先创建一个Department实体。因为Department实体有对多关系,生成托管对象类文件的关联属性不一样,可以体现出和对一关系的区别,所以使用Department实体生成文件。

        快捷键Command + N -> NSManagedObject subclass -> 选择模型文件 -> 选择实体,生成Department实体对应的托管对象类文件。

        这个在哪啊 怎么创建呢学习到这就断了
        刘小壮:@颜面 抱歉,脑袋糊涂写错了,点击后缀名为.xcdatamodeld的模型文件,选择Xcode的Editor -> Create NSManagedObject Subclass -> 选择模型文件 -> 选择实体,生成Department实体对应的托管对象类文件。
        冰封新春:我也没找到这个
        刘小壮:@颜面 这是一系列文章,你去其他文章看看能找到吗。
      • 韶华笑:请问,for循环里创建 NSManagedObject 对象保存,只成功持久话了少量(比如本来右20条,只持久化了3条),这是为什么呢?
        韶华笑:@刘小壮 谢谢,找到原因了,逻辑问题 :cold_sweat:
        刘小壮:@韶华笑 如果代码调用没问题,你是不是设置parentContext了?
        刘小壮:@韶华笑 创建一个NSManagedObject对象,调用context的save一次吗?
      • 绝不知火:感觉真心需要Demo,我感觉添加relationship我就添加不下去了。。。Inverse只显示Inverse,没有employee选择。。。 :sob:
        catcherdream:@绝不知火 同这部 就做不下去了
        刘小壮:@绝不知火 看来需求很强烈,这星期我就把所有博客里都配上Demo,说到做到。
        绝不知火:会了- -|||
      • 儒此优雅:博主 有没有代码参考啊
        60343a0ad510:@刘小壮 确实有必要的,还能攒一波star。😂
        刘小壮:@儒此优雅 我在考虑,我是不是要把我博客里,所有文章都出一份Demo,感觉好多人都需要。
        刘小壮:@儒此优雅 博客里好多要代码的,其实这一系列文章中的示例代码,都粘出来就是一个可运行的Demo

      本文标题:认识CoreData-基础使用

      本文链接:https://www.haomeiwen.com/subject/ppobjttx.html