如下问题的答案是以前写的一篇文章 《iOS面试旗开得胜之问题篇》 如今把问题的答案整理了一份出来给你们。但愿对你们有所帮助。若是整理的答案有问题,请联系我。shavekevin@gmail.com
在堆上开辟一块空间,用指针a指向,而后将指针a赋值(assign)给指针b,等因而a和b同时指向这块堆空间,当a不使用这块堆空间的时候,是否要释放这块堆空间?答案是确定要的,可是这件堆空间被释放后,b就成了野指针。java
如何避免这样的问题? 这就引出了引用计数器,当a指针这块堆空间的时候,引用计数器+1,当b也指向的时候,引用计数器变成了2,当a再也不指向这块堆空间时,release-1,引用计数器为1,当b也不指向这块堆空间时,release-1,引用计数器为0,调用dealloc函数,空间被释放ios
总结:当数据类型为int,float原生类型时,可使用assign。若是是上面那种状况(对象)就是用retain。git
当属性是 NSString、NSArray、NSDictionary时,既能够用strong 修饰,也能够用copy修饰。当用strong修饰的NSString 指向一个NSMutableString时,若是在不知情的状况下这个NSMutableString的别的引用修改了值,就会出现:一个不可变的字符串却被改变了的状况, 使用copy就不会出现这种状况。github
使用场景:在ARC下,若是使用XIB 或者SB 来建立控件,就使用 weak。纯代码建立控件时,用strong修饰,若是想用weak 修饰,就须要先建立控件,而后赋值给用weak修饰的对象。面试
查找了一些资料,发现主要缘由是,controller须要拥有它本身的view(这个view是因此子控件的父view),所以viewcontroller对view就必须是强引用(strong reference),得用strong修饰view。对于lable,它的父view是view,view须要拥有label,可是controller是不须要拥有label的。若是用strong修饰,在view销毁的状况下,label还仍然占有内存,由于controller还对它强引用;若是用wak修饰,在view销毁的时label的内存也同时被销毁,避免了僵尸指针出现。sql
用引用计数回答就是:由于Controller并不直接“拥有”控件,控件由它的父view“拥有”。使用weak关键字能够不增长控件引用计数,确保控件与父view有相同的生命周期。控件在被addSubview后,至关于控件引用计数+1;父view销毁后,全部的子view引用计数-1,则能够确保父view销毁时子view当即销毁。weak的控件在removeFromSuperview后也会当即销毁,而strong的控件不会,由于Controller还保有控件强引用。数据库
总结概括为:当控件的父view销毁时,若是你还想继续拥有这个控件,就用srtong;若是想保证控件和父view拥有相同的生命周期,就用weak。固然在大多数状况下用两个都是能够的。设计模式
使用weak的时候须要特别注意的是:先将控件添加到superview上以后再赋值给self,避免控件被过早释放。数组
Objective-C使用引用计数来管理内存,对象有个计数器,用以表示当前有多少个事物想令此对象继续存活下去。缓存
retain 递增保留计数
release 递减保留计数
对象被建立出来,对象的保留计数至少为1,若想令某对象继续存活,则调用retain方法。要是不想令其继续存活,就调用release或autorelease。当保留计数归零时,对象就回收了(deallocated),系统会将其占用的内存标记为“可重用”。(拖对象保留计数为1 的时候 调用release或autorelease,不会让计数为0,会直接释放,由于这样能够省一步操做)
注意:对象建立出来以后,并非说对象此时的保留计数一定是1.在alloc或initWith方法的实现代码中,也许还有其余对象也保留了此对象,因此,其保留计数可能会大于1.毫不应该说保留计数必定是某个值,只能说你执行的操做是递增了引用计数或递减了引用计数。
autoreleasepool(自动释放池)
调用autorelease时,对象的引用计数不会立刻递减,而是先对象放进自动释放池,一般是在下一次“事件循环”时递减。
由于自动释放池中的释放操做要等到下一次事件循环时才会执行,因此NSLog语句在使用str对象前不须要手工保留。可是,假如要持有此对象的话(好比将其设置给实例变量),那就须要保留。
使用ARC时,引用计数实际仍是要执行的,只不过保留与释放操做如今是由ARC自动添加。
这种方式和java相似,在你的程序的执行过程当中。始终有一个高人在背后准确地帮你收拾垃圾,你不用考虑它何时开始工做,怎样工做。你只须要明白,我申请了一段内存空间,当我再也不使用从而这段内存成为垃圾的时候,我就完全的把它忘记掉,反正那个高人会帮我收拾垃圾。遗憾的是,那个高人须要消耗必定的资源。
ARC在调用这些方法时(retain、release、autorelease、dealloc),并不经过普通的Objective-C消息派发机制,而是直接调用其底层C语言版本。这样作性能更好,由于保留及释放操做须要频繁执行,因此直接调用底层函数能节省不少CPU周期。比方说,ARC会调用与retain等价的底层函数objc_retain。这也是不能覆写retain、release、autorelease的缘由。
若是经过其余方法获取一个对象,则能够假设这个对象引用计数为1,而且被设置为autorelease,不须要对该对象进行清理,若是确实须要retain这个对象,则须要使用完毕后release。
若是retain了某个对象,须要release或autorelease该对象,保持retain方法和release方法使用次数相等。
使用new、alloc、copy关键字生成的对象和retain了的对象须要手动释放。设置为autorelease的对象不须要手动释放,会直接进入自动释放池。
MVC全名是Model View Controller,是模型(model)-视图(view)-控制器(controller)的缩写,一种软件设计典范,用一种业务逻辑、数据、界面显示分离的方法组织代码,将业务逻辑汇集到一个部件里面,在改进和个性化定制界面及用户交互的同时,不须要从新编写业务逻辑。MVC被独特的发展起来用于映射传统的输入、处理和输出功能在一个逻辑的图形化用户界面的结构中。
Model(模型)是应用程序中用于处理应用程序数据逻辑的部分。
一般模型对象负责在数据库中存取数据。
View(视图)是应用程序中处理数据显示的部分。
一般视图是依据模型数据建立的。
Controller(控制器)是应用程序中处理用户交互的部分。
一般控制器负责从视图读取数据,控制用户输入,并向模型发送数据。
你还熟悉什么设计模式?
解决什么场景:当一个类的某些功能须要被别人来实现,可是既不明确是些什么功能,又不明确谁来实现这些功能的时候,委托代理模式就能够派上用场。
在cocoa框架中的Delegate模式中,委托人每每是框架中的对象(视图中的控件、表视图神马的),代理人每每是视图控制器对象。
自定义一个delegate模式
@interface A:UIView id transparendValueDelegate; @property(nomatic, retain) id transparendValueDelegate; @end @implementation A @synthesize transparendValueDelegate -(void)Call { NSString* value = @"你好"; [transparendValueDelegate transparendValue: value]; } @end @interface B:UIView NSString* value; @end @implementation B -(void)transparendValue:(NSString*)fromValue { value = fromValue; NSLog(@"%@ ,我是B",value); } @end
使用时:
A* a = [[A alloc] init]; B* b = [[B alloc] init]; a. transparendValueDelegate = b;//设置A代理委托对象为B [a Call];
这样就会输出:
**你好,我是B** 委托模式关键就在于一个“**被”**字。这个B是很被动的,随时就会被你A Call一下。
观察者模式本质上时一种发布-订阅模型,用以消除具备不一样行为的对象之间的耦合,经过这一模式,不一样对象能够协同工做,同时它们也能够被复用于其余地方Observe
r从Subject
订阅通知,ConcreteObserver
实现重现ObServer
并将其重载其update
方法。一旦SubJect的实例须要通知Observer
任何新的变动,Subject
会发送update
消息来通知存储在其内部类中所注册的Observer
、在ConcreteObserver
的update
方法的实际实现中,Subject
的内部状态可被取得并进行后续处理。
通知
在Cocoa Touch框架中NSNotificationCenter
和NSNotification
对象实现了一对多的模型。经过NSNotificationCenter
可让对象之间进行通信,即使这些对象之间并不认识。
KVO
KVO是Cocoa
提供的一种称为键值观察的机制,对象能够经过它获得其余对象特定属性的变动通知。而这个机制是基于NSKeyValueObserving
非正式些,Cocoa
经过这个协议为全部遵循协议的对象提供了一种自动化的属性监听的功能。
虽然通知
和KVO
均可以对观察者进行实现,可是他们之间仍是略有不一样的,由上面的例子咱们能够看出通知是由一个中心对象为全部观察者提供变动通知,主要是广义上关注程序事件,而KVO
则是被观察的对象直接想观察者发送通知,主要是绑定于特定对象属性的值。
单例设计模式确保对于一个给定的类只有一个实例存在,这个实例有一个全局惟一的访问点。它一般采用懒加载的方式在第一次用到实例的时候再去建立它。
注意:苹果大量使用了此模式。例如:[NSUserDefaults standardUserDefaults], [UIApplication sharedApplication], [UIScreen mainScreen], [NSFileManager defaultManager],全部的这些方法都返回一个单例对象。
有一些状况下,只有一个实例显得很是合理。举例来讲,你不须要有多个Logger的实例,除非你想去写多个日志文件。或者一个全局的配置处理类:实现线程安全的方式访问共享实例是容易的,好比一个配置文件,有好多个类同时修改这个文件。
正式的解释是:在基类中定义建立对象的一个接口,让子类决定实例化哪一个类。工厂方法让一个类的实例化延迟到子类中进行。工厂方法要解决的问题是对象的建立时机,它提供了一种扩展的策略,很好地符合了开放封闭原则。工厂方法也叫作虚构造器(Virtual Constructor)。
经过工厂方法建立工厂对象,而后在工厂类中定义建立基类的子类对象的方法并经过外部传入的条件判断去建立哪个子类对象,不过因为OC是运行时语言,因此工厂类虽然提供了建立子类对象的方法,可是在编译时期并不能肯定对象类型,编译时期建立的子类对象类型是基类类型,真正的类型在运行时期由子类来肯定,也即此时肯定为子类类型。
优势
极大地优化了代码,若是须要100个子类对象,不用再一直调用alloc方法去建立,而是直接经过其工厂类的一句代码便可实现,提升了对代码的复用性。同时,也能够将大量的操做放到工厂类中去处理,业务类中只负责去调用建立须要的对象便可。
缺点
由于它的实现条件之一必须存在继承关系,因此模式中工厂类集中了全部的建立逻辑,造成一个庞大的全能类,当全部的类不是继承自同一个父类的时候扩展比较困难。
浅拷贝就是对内存地址的复制,让目标对象指针和源对象指向同一片内存空间。
浅拷贝只是对对象的简单拷贝,让几个对象共用一片内存,当内存销毁的时候,指向这片内存的几个指针须要从新定义才可使用,要否则会成为野指针。
在 iOS 里面, 使用retain 关键字进行引用计数,就是一种更加保险的浅拷贝。他既让几个指针共用同一片内存空间,又能够在release 因为计数的存在,不会轻易的销毁内存,达到更加简单使用的目的。
深拷贝是指拷贝对象的具体内容,而内存地址是自主分配的,拷贝结束以后,两个对象虽然存的值是相同的,可是内存地址不同,两个对象也互不影响,互不干涉。
copy 与 retain 的区别:
copy 是建立一个新对象,retain 是建立一个指针,引用对象计数加一。 copy属性标识两个对象内容相同,新的对象retain count为1, 与旧有对象引用计数无关,旧有对象没有变化。copy减小对象对上下文的依赖。
iOS提供了copy和mutableCopy方法,顾名思义,copy就是复制了一个imutable的对象,而mutableCopy就是复制了一个mutable的对象。如下将举几个例子来讲明。
这里指的是NSString, NSNumber等等一类的对象。
NSString *string = @”dddd"; NSString *stringCopy = [string copy]; NSMutableString *stringDCopy = [string mutableCopy]; [stringMCopy appendString:@``"!!"``];
查看内存能够发现,string和stringCopy指向的是同一块内存区域(weak reference),引用计数没有发生改变。而stringMCopy则是咱们所说的真正意义上的复制,系统为其分配了新内存,是两个独立的字符串内容是同样的。
copy构造
- (id)copyWithZone:(NSZone *)zone{ MyObj *copy = [[[self class] allocWithZone :zone] init]; copy->name = [_name copy]; copy->imutableStr = [_imutableStr copy]; copy->age = age; return copy; }
mutableCopy构造
- (id)mutableCopyWithZone:(NSZone *)zone{ MyObj *copy = NSCopyObject(self, 0, zone); copy->name = [_name mutableCopy]; copy->age = age; return copy; }
KVC
KVC,便是指 NSKeyValueCoding,一个非正式的 Protocol,提供一种机制来间接访问对象的属性。KVO 就是基于 KVC 实现的关键技术之一。
一个对象拥有某些属性。好比说,一个 Person 对象有一个 name 和一个 address 属性。以 KVC 说法,Person 对象分别有一个 value 对应他的 name 和 address 的 key。 key 只是一个字符串,它对应的值能够是任意类型的对象。从最基础的层次上看,KVC 有两个方法:一个是设置 key 的值,另外一个是获取 key 的值。
说白了就是经过指定的key得到想要的值value。而不是经过调用Setter、Getter方法访问。
(1). key的值必须正确,若是拼写错误,会出现异常
(2). 当key的值是没有定义的,valueForUndefinedKey:这个方法会被调用,若是你本身写了这个方法,key的值出错就会调用到这里来
(3). 由于类key反复嵌套,因此有个keyPath的概念,keyPath就是用.号来把一个一个key连接起来,这样就能够根据这个路径访问下去
(4). NSArray/NSSet等都支持KVC
底层原理
1.判断有没有指定key的set方法,若是有set方法,就会调用set方法,给该属性赋值
2.若是没有set方法,判断有没有跟key值相同且带有下划线的成员属性(_key).若是有,直接给该成员属性进行赋值
3.若是没有成员属性_key,判断有没有跟key相同名称的属性.若是有,直接给该属性进行赋值
4.若是都没有,就会调用 valueforUndefinedKey 和setValue:forUndefinedKey:方法。
一、赋值:setValue:forkey。
二、字典转模型:KVC,使用setValuesForKeysWithDictionary:方法,该方法默认根据字典中每一个键值对,调用setValue:forKey方法
缺点:字典中的键值对必须与模型中的键值对彻底对应,不然程序会崩溃
三、取值:valueForKey
KVO
Key-Value Observing (KVO) 创建在 KVC 之上,它可以观察一个对象的 KVC key path 值的变化。说白了就是你关心的一个值改变了,你就会获得通知。你就能够在你想处理的地方处理这个值。
一、为对象添加一个观察者(监听器)
二、设置监听事件
三、取消监听
底层实现原理
第一次被观察
时,系统就会在运行期动态
地建立该类的一个派生类
,在这个派生类中重写基类中任何被观察属性的setter 方法。派生类在被重写的setter方法内实现真正的通知机制
NSKVONotifying_Person
willChangeValueForKey:
和 didChangevlueForKey:
;在一个被观察属性发生改变以前, willChangeValueForKey:
必定会被调用,这就 会记录旧的值。而当改变发生后,didChangeValueForKey:
会被调用,继而 observeValueForKey:ofObject:change:context:
也会被调用。主要分为三大步
- 第一步:寻找该属性有没有setsetter方法?有,就直接赋值 - 第二步:寻找有没有该属性的成员属性?有,就直接赋值 - 第三步:寻找有没有该属性带下划线的成员属性?有,就直接赋值
通知
须要有一个通知中心:NSNotificationCenter
,自定义通知的话须要给一个名字,而后监听。
协议
经过setDelegate来设置代理对象,最典型的例子是经常使用的 TableView.
沙盒
Documents:
保存用户产生的数据,iTunes同步设备的时候会备份该目录。用户产生的数据就是指用户在使用当前app
的时候保存的一些数据,好比保存app
中的图片、保存下载的文件等。Library:
这个目录下有2个文件夹,一个是Caches
、一个是Preferences
,Caches
主要保存缓存数据,好比SDWebImage
把缓存的图片就存放到该目录下。当程序退出后,改目录保存的文件一直存在。Preferences
在Xcode6
以前保存的是偏好设置,好比NSUserDefaults
保存的文件。可是Xcode6
以上就保存到/Users/用户名/Library/ Developer/CoreSimulator/Devices/模拟器UDID/data/Library/Preferences/
文件夹下。tmp:
保存程序中的临时数据,当程序退出后系统会自动删除tmp
中全部的文件。NSUserDefaults
NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults]; //保存值(key值同名的时候会覆盖的) [defaults setObject:@"用户名" forKey:kUsernameKey]; //当即保存 [defaults synchronize]; //取值 NSString *username = [defaults objectForKey:kUsernameKey];
//保存NSInteger [defaults setInteger:(NSInteger) forKey:(nonnull NSString *)]; //保存BOOL [defaults setBool:(BOOL) forKey:(nonnull NSString *)]; //保存NSURL [defaults setURL:(nullable NSURL *) forKey:(nonnull NSString *)]; //保存float [defaults setFloat:(float) forKey:(nonnull NSString *)]; //保存double [defaults setDouble:(double) forKey:(nonnull NSString *)];
[defaults integerForKey:(nonnull NSString *)]; [defaults boolForKey:(nonnull NSString *)]; [defaults URLForKey:(nonnull NSString *)]; [defaults floatForKey:(nonnull NSString *)]; [defaults doubleForKey:(nonnull NSString *)];
[defaults removeObjectForKey:(nonnull NSString *)];
归档(序列化)
准守NSCoding协议必需要实现两个require
方法
(void)encodeWithCoder:(NSCoder *)aCoder //归档会触发
- (nullable instancetype)initWithCoder:(NSCoder *)aDecoder //解归档会触发
Coding 类具体实现:
@interface Coding : NSObject<NSCoding> @property (nonatomic, copy) NSString *name; @property (nonatomic, assign) NSInteger age;
#import "Coding.h" #import <objc/runtime.h> @implementation Coding /** * 根据类动画获取类的全部属性,不要忘记导入#import <objc/runtime.h> * * @param cls <#cls description#> * * @return <#return value description#> */ - (NSArray *)perperiesWithClass:(Class)cls { NSMutableArray *perperies = [NSMutableArray array]; unsigned int outCount; //动态获取属性 objc_property_t *properties = class_copyPropertyList(cls, &outCount); //遍历person类的全部属性 for (int i = 0; i < outCount; i++) { objc_property_t property = properties[i]; const char *name = property_getName(property); NSString *s = [[NSString alloc] initWithUTF8String:name]; [perperies addObject:s]; } return perperies; } /** * 归档会触发 * * @param aCoder <#aCoder description#> */ - (void)encodeWithCoder:(NSCoder *)aCoder { for (NSString *perperty in [self perperiesWithClass:[self class]]) { [aCoder encodeObject:perperty forKey:perperty]; } } /** * 解归档会触发 * * @param aDecoder <#aDecoder description#> * * @return <#return value description#> */ - (nullable instancetype)initWithCoder:(NSCoder *)aDecoder { if (self = [super init]) { for (NSString *perperty in [self perperiesWithClass:[self class]]) { [self setValue:[aDecoder decodeObjectForKey:perperty] forKey:perperty];; } } return self; } @end
Coding *coding1 = [[Coding alloc] init]; coding1.name = @"小明"; coding1.age = 12; Coding *coding2 = [[Coding alloc] init]; coding1.name = @"小王"; coding1.age = 20; NSArray *array = @[coding1, coding2]; //保存对象转化为二进制数据(必定是可变对象) NSMutableData *data = [NSMutableData data]; //1.初始化 NSKeyedArchiver *archivier = [[NSKeyedArchiver alloc] initForWritingWithMutableData:data]; //2.归档 [archivier encodeObject:array forKey:@"key"]; //3.完成归档 [archivier finishEncoding]; //4.保存 [[NSUserDefaults standardUserDefaults] setObject:data forKey:@"data"];
//1.获取保存的数据 NSData *data = [[NSUserDefaults standardUserDefaults] objectForKey:@"data"]; //2.初始化解归档对象 NSKeyedUnarchiver *unarchiver = [[NSKeyedUnarchiver alloc] initForReadingWithData:data]; //3.解归档 NSArray *persons = [unarchiver decodeObjectForKey:@"key"]; //4.完成解归档 [unarchiver finishDecoding];
plist文件保存
plist
保存,plist
自己就是XML文件,名字后缀为.plist
。plist
主要保存的数据类型为NSString
、NSNumber
、NSData
、NSArray
、NSDictionary
。//把字典写入到plist文件,好比文件path为:~/Documents/data.plist [dictionary writeToFile:path atomically:YES]; //把数组写入到plist文件中 [array writeToFile:path atomically:YES];
NSDictionary *dictionary = [NSDictionary dictionaryWithContentsOfURL:[NSURL fileURLWithPath:(nonnull NSString *)]]; NSDictionary *dictionary = [NSDictionary dictionaryWithContentsOfFile:[[NSBundle mainBundle] pathForResource:(nullable NSString *) ofType:(nullable NSString *)]];
NSArray *array = [NSArray arrayWithContentsOfURL:[NSURL fileURLWithPath:(nonnull NSString *)]]; NSArray *array = [NSArray arrayWithContentsOfFile:[[NSBundle mainBundle] pathForResource:(nullable NSString *) ofType:(nullable NSString *)]];
数据库
sqlite3
, 使用sqlite3
须要配置库文件libsqlite3.tbd
或者导入libsqlite3.0.tbd
,这两个库导入任何一个均可以sqlite
使用步骤:
sqlite3
对象而且打开数据库。
具体实现:
数据库路径
//返回数据库路径,保存到Cache目录下 -(NSString *)databasePath { NSString *path = NSSearchPathForDirectoriesInDomains(NSCachesDirectory, NSUserDomainMask, YES)[0]; return [path stringByAppendingPathComponent:@"contacts.db"]; }
sqlite3
对象而且打开数据库,若是数据库打开成功,就建立表。//数据库对象 sqlite3 *contactDB; const char *path = [[self databasePath] UTF8String]; if (sqlite3_open(path, &contactDB) == SQLITE_OK) { char *errMsg; const char *sql_stmt = "CREATE TABLE IF NOT EXISTS CONTACTS(ID INTEGER PRIMARY KEY AUTOINCREMENT, NAME TEXT, ADDRESS TEXT,PHONE TEXT)"; //执行语句 if (sqlite3_exec(contactDB, sql_stmt, NULL, NULL, &errMsg) != SQLITE_OK) { //建立表失败 } } else { //打开数据库失败 } sqlite3_close(contactDB);
代码解释:
sqlite3_open:
打开指定路径的数据库,若是数据库不存在,就会建立一个新的数据库。SQLITE_OK
是一个常量,表示打开数据库成功。contactDB
就是数据库对象。sqlite3_exec
就是执行sql语句方法。sqlite3_close
关闭数据库,通常暂时不用数据库的时候手动关闭,防止资源浪费。//是一个抽象类型,是一个句柄,在使用过程当中通常以它的指针进行操做 sqlite3_stmt *statement; //数据库路径 const char *path = [[self databasePath] UTF8String]; //使用的时候打开数据库 if (sqlite3_open(path, &contactDB) == SQLITE_OK) { NSString *insertSQL = [NSString stringWithFormat:@"INSERT INTO CONTACTS (name,address,phone) VALUES(\"%@\",\"%@\",\"%@\")",name.text,address.text,phone.text]; const char *insert_stmt = [insertSQL UTF8String]; // 这个函数将sql文本转换成一个准备语句(prepared statement)对象,同时返回这个对象的指针。这个接口须要一个数据库链接指针以及一个要准备的包含SQL语句的文本。它实际上并不执行这个SQL语句,它仅仅为执行准备这个sql语句 sqlite3_prepare_v2(contactDB, insert_stmt, -1, &statement, NULL); //执行这个sql if (sqlite3_step(statement) == SQLITE_DONE) { //TODO:已存储到数据库; } else { //TODO:保存失败 } //销毁statement对象 sqlite3_finalize(statement); //关闭数据库 sqlite3_close(contactDB); }
//数据库路径 const char *path = [[self databasePath] UTF8String]; //查询结果集对象句柄 sqlite3_stmt *statement; //打开数据库 if (sqlite3_open(path, &contactDB) == SQLITE_OK) { //查询的sql语句 NSString *querySQL = [NSString stringWithFormat:@"SELECT address,phone from contacts where name=\"%@\"",name.text]; const char *query_stmt = [querySQL UTF8String]; //执行查询sql语句 if (sqlite3_prepare_v2(contactDB, query_stmt, -1, &statement, NULL) == SQLITE_OK) { //遍历每条数据 if (sqlite3_step(statement) == SQLITE_ROW) { //获取每条数据的字段。 NSString *addressField = [[NSString alloc] initWithUTF8String:(const char *)sqlite3_column_text(statement, 0)]; address.text = addressField; NSString *phoneField = [[NSString alloc] initWithUTF8String:(const char *)sqlite3_column_text(statement, 1 )]; phone.text = phoneField; //TODO:已查到结果 } else { //TODO:未查到结果 } sqlite3_finalize(statement); } sqlite3_close(contactDB); }
CoreData
CoreData
提供了一种“对象-关系映射”的功能,能将OC对象转化成数据,保存Sqlite中。CoreData
的好处就是可以合理管理内存,避免sql语句的麻烦(不用写sql语句)。CoreData
构成
NSManagedObjectContext:
被管理的数据上下文,主要做用:插入、查询、删除。NSManagedObjectModel:
数据库全部的表结构和数据结构,包含各个实体的定义的信息。主要做用就是添加实体、实体属性,创建属性之间的关系。NSPersistentStoreCoordinator
持久化存储助理对象,至关于数据库的链接器。主要做用就是设置存储的名字、位置、存储方式。NSFetchRequest
至关于select
语句。查询封装对象。NSEntityDescription
实体结构对象,至关于表格结构。xxx.xcdatamodeld
文件,编译后为xxx.momd
的文件。- (NSManagedObjectContext *)context { AppDelegate *app = [UIApplication sharedApplication].delegate; return app.managedObjectContext; }
//建立Person对象 /* insertNewObjectForEntityForName:就是建立的实体名字。 inManagedObjectContext:上下文,appDelegate里面已经建立完成。 */ Person *person = [NSEntityDescription insertNewObjectForEntityForName:@"Person" inManagedObjectContext:[self context]]; //赋值 [person setValue:@"小王" forKey:@"name"]; [person setValue:@(35) forKey:@"age"]; //保存 if (![[self context] save:nil]) { //TODO:保存失败 }
//建立查询对象 NSFetchRequest *request = [NSFetchRequest fetchRequestWithEntityName:@"Person"]; #if 0 //条件查询 //NSPredicate *predicate = [NSPredicate predicateWithFormat:@"age<=35"]; //查询名字带有王的 NSPredicate *predicate = [NSPredicate predicateWithFormat:@"name like[cd]'*王*'"]; //设置查询条件 request.predicate = predicate; #endif //排序 NSSortDescriptor *sort = [NSSortDescriptor sortDescriptorWithKey:@"age" ascending:NO]; //设置排序条件 request.sortDescriptors = @[sort]; //执行查询 NSArray *objectArray = [[self context] executeFetchRequest:request error:nil]; //遍历查询结果 for (Person *p in objectArray) { NSLog(@"%@ - %@",[p valueForKey:@"name"],[p valueForKey:@"age"]); }
//先查询要修改的对象 NSFetchRequest *request = [NSFetchRequest fetchRequestWithEntityName:@"Person"]; //设置查询条件 NSPredicate *predicate = [NSPredicate predicateWithFormat:@"name='小王' and age = 35"]; request.predicate = predicate; //执行查询 NSArray *objectArray = [[self context] executeFetchRequest:request error:nil]; //遍历要修改的对象 for (Person *p in objectArray) { //修改(修改内存数据,没有同步数据库) [p setValue:@(45) forKey:@"age"]; } //同步数据库 [[self context] save:nil];
//查询要删除的数据 NSFetchRequest *request = [NSFetchRequest fetchRequestWithEntityName:@"Person"]; //设置查询条件 NSPredicate *predicate = [NSPredicate predicateWithFormat:@"name='小王'"]; request.predicate = predicate; //执行查询 NSArray *objectArray = [[self context] executeFetchRequest:request error:nil]; //遍历删除 for (Person *p in objectArray) { //删除内存中的数据 [[self context] deleteObject:p]; } //同步数据库 [[self context] save:nil];
KeyChain
NSDictionary
的存储方式同样。NSUserDefaults
来讲,KeyChain保存更为安全,并且KeyChain里面保存的数据不会由于app删除而丢失。基本使用
为了使用方便,咱们使用github上封装好的类KeychainItemWrapper
和SFHFKeychainUtils
KeychainItemWrapper
是苹果封装的类,封装了操做KeyChain的基本操做,下载地址:https://github.com/baptistefe... // 初始化一个保存用户账号的KeychainItemWrapper KeychainItemWrapper *wrapper = [[KeychainItemWrapper alloc] initWithIdentifier:@"Your Apple ID" accessGroup:@"YOUR_APP_ID.com.yourcompany.AppIdentifier"]; //保存账号 [wrapper setObject:@"<账号>" forKey:(id)kSecAttrAccount]; //保存密码 [wrapper setObject:@"<账号密码>" forKey:(id)kSecValueData]; //从keychain里取出账号密码 NSString *password = [wrapper objectForKey:(id)kSecValueData]; //清空设置 [wrapper resetKeychainItem];
上面代码的setObject: forKey:
里参数forKey
的值应该是Security.framework
里头文件SecItem.h
里定义好的key
。
SFHFKeychainUtils
是另一个第三方库,这个类比KeychainItemWrapper
要简单不少,提供了更简单的方法保存密码到KeyChain,下载地址:https://github.com/ldandersen/scifihifi-iphone/tree/master/security。 这个库是mrc,导入后可能会由于mrc会报错。SFHFKeychainUtils
就3个方法://获取密码密码 +(NSString *) getPasswordForUsername: (NSString *) username andServiceName: (NSString *) serviceName error: (NSError **) error; //存储密码 +(BOOL) storeUsername: (NSString *) username andPassword: (NSString *) password forServiceName: (NSString *) serviceName updateExisting: (BOOL) updateExisting error: (NSError **) error; //删除密码 +(BOOL) deleteItemForUsername: (NSString *) username andServiceName: (NSString *) serviceName error: (NSError **) error;
参数说明
username:
由于KeyChain保存也是以键值对存在,因此这个能够看做key,根据key取value.forServiceName :
这个就是组的名字,能够理解为KeyChain保存是分组保存。通常要惟一哦,命名可使用YOUR_APP_ID.com.yourcompany.AppIdentifier。username
、serviceName
参数同样,那么这两个app会共用KeyChain里面的数据,也就是能够共享密码。//建立一个uuid NSString *uuidString = [self uuidString]; //31C75924-1D2E-4AF0-9C67-96D6929B1BD3 [SFHFKeychainUtils storeUsername:kKeyChainKey andPassword:uuidString forServiceName:kKeyChainGroupKey updateExisting:NO error:nil]; -(NSString *)uuidString { //建立一个uuid CFUUIDRef uuidRef = CFUUIDCreate(kCFAllocatorDefault); CFStringRef stringRef = CFUUIDCreateString(kCFAllocatorDefault, uuidRef); NSString *uuidString = (__bridge NSString *)(stringRef); CFRelease(uuidRef); return uuidString; }