技術一
iOS 開発の結構
画面
UI
UIWebview
[[UIApplication sharedApplication] openURL:]
调用谷歌地图(Google Maps)
NSString *searchQuery = @"1 Infinite Loop, Cupertino, CA 95014";
searchQuery = [addressText stringByAddingPercentEscapesUsingEncoding: NSUTF8StringEncoding];
NSString *urlString=[NSString stringWithFormat:@"http://maps.google.com/maps?q=%@", searchQuery];
[[UIApplication sharedApplication] openURL:[NSURL URLWithString:urlText]];
调用邮件客户端(Apple Mail)
[[UIApplication sharedApplication] openURL:[NSURL URLWithString:@"mailto://admin@eyecm.com"]];
拨号(Phone Number)
[[UIApplication sharedApplication] openURL:[NSURL URLWithString:@"tel://10086"]];
调用短信(SMS)
[[UIApplication sharedApplication] openURL:[NSURL URLWithString:@"sms:10086"]];
调用浏览器(Safari Browser)
NSURL *url= [NSURL URLWithString:@"http://eyecm.com"];
[[UIApplication sharedApplication] openURL:url];
调用应用商店(AppStore)
NSURL *appStoreUrl= [NSURL URLWithString:@"http://phobos.apple.com/WebObjects/MZStore.woa/wa/viewSoftware?id=291586600&mt=8"];
[[UIApplication sharedApplication] openURL:appStoreUrl];
调用appstore中程序的评论
NSString *str = [NSString stringWithFormat:
@"itms-apps://ax.itunes.apple.com/WebObjects/MZStore.woa/wa/viewContentsUserReviews?type=Purple+Software&id=%d",
m_appleID ];
[[UIApplication sharedApplication] openURL:[NSURL URLWithString:str]];
// 启动系统风火轮
[UIApplication sharedApplication].networkActivityIndicatorVisible = YES;
// 隐藏系统风火轮
[UIApplication sharedApplication].networkActivityIndicatorVisible = NO;
画面の切り替え
UINavgationController
UIBarButtonItem* next = [[[UIBarButtonItem alloc] initWithTitle:@"閉じる"
style:UIBarButtonItemStyleBordered
target:modalViewController
action:@selector(touchedCloseModalViewControler)] autorelease];
modalViewController.navigationItem.leftBarButtonItem = next;
UINavigationController *navigationController = [[[UINavigationController alloc] initWithRootViewController:modalViewController] autorelease];
[self presentViewController:navigationController animated:YES completion:nil];
[self.navigationController pushViewController:detail animated:YES];
[self.navigationController popViewControllerAnimated:YES];
[self presentViewController:navigationController animated:YES completion:nil];
Network
NSURLSession
配置
image.xcassets
Prefix.pch
#ifdef __OBJC__
#endif
Info.plist
//获取当前版本号
NSDictionary*infoDic = [[NSBundlemainBundle]infoDictionary];
NSLog(@"%@",infoDic);
NSString *currentAppVersion = infoDic[@"CFBundleShortVersionString"];
// contentView.plist読み込み準備
NSString *path = [[NSBundle mainBundle] pathForResource:@"contentView" ofType:@"plist"];
NSDictionary *dict = [NSDictionary dictionaryWithContentsOfFile:path];
NSString *executableFile = [[[NSBundle mainBundle] infoDictionary] objectForKey:(NSString *)kCFBundleExecutableKey];
//获取项目名称
NSString *version = [[[NSBundle mainBundle] infoDictionary] objectForKey:(NSString *)kCFBundleVersionKey];
//获取项目版本号 NSDictionary *infoDictionary = [[NSBundle mainBundle] infoDictionary]; CFShow(infoDictionary); // app名称
NSString *app_Name = [infoDictionary objectForKey:@"CFBundleDisplayName"];
// app版本
NSString *app_Version = [infoDictionary objectForKey:@"CFBundleShortVersionString"];
// app build版本
NSString *app_build = [infoDictionary objectForKey:@"CFBundleVersion"];
performSelectorOnMainThread的一些细节
#pragma mark 加载图片
-(void)loadImage{
//请求数据
NSData *data= [self requestData];
/*将数据显示到UI控件,注意只能在主线程中更新UI,
另外performSelectorOnMainThread方法是NSObject的分类方法,每个NSObject对象都有此方法,
它调用的selector方法是当前调用控件的方法,例如使用UIImageView调用的时候selector就是UIImageView的方法
Object:代表调用方法的参数,不过只能传递一个参数(如果有多个参数请使用对象进行封装)
waitUntilDone:是否线程任务完成执行
*/
[self performSelectorOnMainThread:@selector(updateImage:) withObject:data waitUntilDone:YES];
}
NSThread->1.0创建线程
第一种:通过NSThread的对象方法(alloc / init - start)
//线程一启动,就会在线程thread中执行self的run方法
NSThread*thread = [[NSThreadalloc]initWithTarget:<#(nonnull id)#>
selector:<#(nonnull SEL)#>
object:<#(nullable id)#> ];
//开启线程,通过start方法,就会将我们创建出来的当前线程加入到`可调度线程池`,供CPU调度
//[thread start];执行后,会在另外一个线程执行longOperation:方法
[thread start];
第二种:通过NSThread的类方法(detachNewThreadSelector)
// detachNewThreadSelector类方法不需要启动,会自动创建线程并执行@selector方法
//它会自动给我们做两件事: 1.创建线程对象2.添加到`可调度线程池`
//通过NSThread的类和NSObject的分类方法直接加入到可调度线程池里面去,等待CPU调度
[NSThreaddetachNewThreadSelector:<#(nonnullSEL)#>
toTarget:<#(nonnullid)#>
withObject:<#(nullableid)#> ];
第三种:通过NSObject的方法
//此方法在后台线程中执行(即是:在子线程中执行)
// performSelectorInBackground是NSObject的分类方法,会自动在后台线程执行@selector方法
//没有thread字眼,隐式创建并启动线程
//所有NSObject都可以使用此方法,在其他线程执行方法
//通过NSThread的类和NSObject的分类方法直接加入到可调度线程池里面去,等待CPU调度
// PerformSelectorInBackground可以让方便地在后台线程执行任意NSObject对象的方法
[selfperformSelectorInBackground:<#(nonnull SEL) #>
withObject:<#(nullable id) #> per];
方式2和方式3的优缺点 :
优点:简单快捷
缺点:无法对线程进行更详细的设置
NSThread->2.0线程属性
线程相关方法:
+ (NSThread*)mainThread;//获得主线程
- (BOOL)isMainThread;//是否为主线程
+ (BOOL)isMainThread;//是否为主线程
NSThread*main = [NSThreadmainThread];// + (NSThread *)mainThread;获得主线程
[NSThreadisMainThread];// + (BOOL)isMainThread;类方法判断,该方法是否为主线程
[main isMainThread];// - (BOOL)isMainThread;对象方法判断,该对象是否为主线程
NSThread*current = [NSThreadcurrentThread];//获得当前线程
//返回当前方法所在线程的优先级
+ (double)threadPriority;
举例:[NSThreadthreadPriority];
//设置线程的优先级
+ (BOOL)setThreadPriority:(double)p;
举例:self.thread1.threadPriority=1.0;
- (double)threadPriority;//返回当前方法所在线程的优先级
- (BOOL)setThreadPriority:(double)p;//设置线程的优先级
- (void)setName:(NSString*)n;//set方法
- (NSString*)name;//get方法
举例:
thread.name=@"线程A";
NSThread->5.0线程间通讯和常用方法
线程间通信常用方法:
//在主线程执行操作(从子线程回到主线程)(推荐)
- (void)performSelectorOnMainThread:(SEL)aSelector
withObject:(id)arg
waitUntilDone:(BOOL)wait;
- (void)performSelector:(SEL)aSelector
onThread:(NSThread*)thr
withObject:(id)arg
waitUntilDone:(BOOL)wait;
GCD->1.0 GCD简介和使用
GCD 的优势:
GCD是苹果公司为多核的并行运算提出的解决方案
GCD会自动利用更多的CPU内核(比如双核、四核)
GCD会自动管理线程的生命周期(创建线程、调度任务、销毁线程)
程序员只需要告诉GCD想要执行什么任务,不需要编写任何线程管理代码
任务的执行:同步
//queue:队列block:任务
dispatch_sync(dispatch_queue_tqueue,dispatch_block_tblock);
任务的执行:异步
//queue:队列block:任务
dispatch_async(dispatch_queue_tqueue,dispatch_block_tblock);
//在前面的任务执行结束后它才执行,而且它后面的任务等它执行完成之后才会执行
dispatch_barrier_async(dispatch_queue_tqueue,dispatch_block_tblock);
【区别】同步 & 异步:同步和异步决定了要不要开启新的线程
-同步:只能在当前线程中执行任务,不具备开启新线程的能力
-异步:可以在新的线程中执行任务,具备开启新线程的能力
【区别】并发 & 串行:并发和串行决定了任务的执行方式
-并发:允许多个任务并发(同时)执行
-串行:一个任务执行完毕后,再执行下一个任务
GCD的使用 - 队列
并发队列
-可以让多个任务并发(同时)执行(自动开启多个线程同时执行任务)
-并发功能只有在异步(dispatch_async)函数下才有效
串行队列
-让任务一个接着一个地执行(一个任务执行完毕后,再执行下一个任务)
release
-凡是函数名种带有create\copy\new\retain等字眼,都应该在不需要使用这个数据的时候进行release
- GCD的数据类型在ARC环境下不需要再做release
dispatch_release(queue);//非ARC需要释放手动创建的队列
- CF(Core Foundation)的数据类型在ARC环境下还是需要再做release
创建队列 - 并发队列
方式1 - 手动创建并发队列
dispatch_queue_create(
constchar *label,//队列名称
dispatch_queue_attr_tattr//队列的类型
);
// 1.创建并发队列DISPATCH_QUEUE_CONCURRENT (并发)
dispatch_queue_t queue = dispatch_queue_create(“TD", DISPATCH_QUEUE_CONCURRENT);
// 2.非ARC需要释放手动创建的队列
dispatch_release(queue);
方式2 - 获取全局并发队列:(GCD默认已经提供了全局的并发队列,供整个应用使用,可以无需手动创建
#define DISPATCH_QUEUE_PRIORITY_HIGH2//高
#define DISPATCH_QUEUE_PRIORITY_DEFAULT0//默认(中)
#define DISPATCH_QUEUE_PRIORITY_LOW (-2)//低
#define DISPATCH_QUEUE_PRIORITY_BACKGROUND INT16_MIN//后台
dispatch_get_global_queue(
longidentifier,//队列的优先级
unsignedlong flags//此参数暂时无用,用0即可
);
//获取全局并发队列
dispatch_queue_tqueue= dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT,0);
#pragma mark -同步函数+并发队列:不会开启新的线程,在当前线程执行任务(主线程),顺序执行,并发队列失去了并发的功能
- (void)syncConcurrent {
NSLog(@"同步并发----- begin");
// 1.获得全局的并发队列
dispatch_queue_tqueue =
dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT,0);
// 2.将任务加入队列
dispatch_sync(queue, ^{
NSLog(@"1-----%@", [NSThreadcurrentThread]);
});
dispatch_sync(queue, ^{
NSLog(@"2-----%@", [NSThreadcurrentThread]);
});
dispatch_sync(queue, ^{
NSLog(@"3-----%@", [NSThreadcurrentThread]);
});
NSLog(@"同步并发----- end");
}
#pragma mark -写法2
- (void)concurrentSync {
// 1.创建并发队列
dispatch_queue_tconCurrentQueue =
dispatch_queue_create("TD", DISPATCH_QUEUE_CONCURRENT);
// 2.创建任务
void(^task1)() = ^() {
NSLog(@"---task1---%@", [NSThreadcurrentThread]);
};
void(^task2)() = ^() {
NSLog(@"---task2---%@", [NSThreadcurrentThread]);
};
void(^task3)() = ^() {
NSLog(@"---task3---%@", [NSThreadcurrentThread]);
};
// 3.将同步任务添加到并发队列中
dispatch_sync(conCurrentQueue, task1);
dispatch_sync(conCurrentQueue, task2);
dispatch_sync(conCurrentQueue, task3);
}
#pragma mark -异步函数+并发队列:可以同时开启多条线程,在当前线程执行任务(主线程),无序执行(按照任务添加到队列中的顺序被调度),线程条数具体由`可调度线程池/底层线程池`来决定
- (void)asyncConcurrent {
NSLog(@"异步并发----- begin");
// 1.获得全局的并发队列
dispatch_queue_tqueue =
dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT,0);
// 2.将任务加入队列
dispatch_sync(queue, ^{
NSLog(@"1-----%@", [NSThreadcurrentThread]);
});
dispatch_sync(queue, ^{
NSLog(@"2-----%@", [NSThreadcurrentThread]);
});
dispatch_sync(queue, ^{
NSLog(@"3-----%@", [NSThreadcurrentThread]);
});
NSLog(@"异步并发----- begin");
}
#pragma mark -写法2
- (void)concurrentAsync {
// 1.创建并发队列
dispatch_queue_tconCurrentQueue =
dispatch_queue_create("TD", DISPATCH_QUEUE_CONCURRENT);
// 2.创建任务
void(^task1)() = ^() {
NSLog(@"---task1---%@", [NSThreadcurrentThread]);
};
void(^task2)() = ^() {
NSLog(@"---task2---%@", [NSThreadcurrentThread]);
};
void(^task3)() = ^() {
NSLog(@"---task3---%@", [NSThreadcurrentThread]);
};
// 3.将异步任务添加到并发队列中
dispatch_async(conCurrentQueue, task1);
dispatch_async(conCurrentQueue, task2);
dispatch_async(conCurrentQueue, task3);
}
创建队列-队列
//1.创建串行队列
//方式1:DISPATCH_QUEUE_SERIAL (串行)
dispatch_queue_t queue = dispatch_queue_create(“TD", DISPATCH_QUEUE_SERIAL);
//方式2:传NULL
dispatch_queue_t queue = dispatch_queue_create(“TD",NULL);
// 2.非ARC需要释放手动创建的队列
dispatch_release(queue);
串行队列,异步任务,在多线程中,是斯坦福大学最推荐的一种多线程方式
优点:将任务放在其他线程中工作,每个任务顺序执行,便于调试
缺点:并发能力不强,最多只能使用一条线程!
同步函数 + 并发队列:不会开启新的线程,在当前线程执行任务(主线程),顺序执行,并发队列失去了并发的功能
#异步函数 + 并发队列:可以同时开启多条线程,在当前线程执行任务(主线程),无序执行(按照任务添加到队列中的顺序被调度),线程条数具体由可调度线程池/底层线程池来决定
(1)如果异步任务前面有同步任务:就会先执行同步任务同步任务是按顺序执行的任务等他执行完了才会执行并行中的异步任务(可以做到阻塞 控制任务的执行顺序)
(2)如果异步任务后面有同步任务:两个任务会并行(同时)执行
同步函数 + 串行队列:不会开启新的线程,在当前线程执行任务(主线程),任务是串行的(顺序执行)
异步函数 + 串行队列:会开启新的线程,在子线程执行任务,任务是串行的(顺序执行),只开一条线程
串行队列中的任务都是按顺序执行:
异步函数 + 主队列:不会开启新的线程,在当前线程执行任务(主线程),任务是串行的(顺序执行),只开一条线程(适合处理 UI 或者是 UI事件)
同步函数 + 主队列:不会开启新的线程,会出现"死等",可能导致主线程卡死
【区别】同步 & 异步:同步和异步决定了要不要开启新的线程
-同步:只能在当前线程中执行任务,不具备开启新线程的能力
-异步:可以在新的线程中执行任务,具备开启新线程的能力
【区别】并发 & 串行:并发和串行决定了任务的执行方式
-并发:允许多个任务并发(同时)执行
-串行:一个任务执行完毕后,再执行下一个任务
同步函数:无论是什么队列都不会开启线程
(1)并发队列:不会开线程
(2)串行队列:不会开线程
异步函数:具备开启线程的能力(但不一定会开线程 ),开启几条线程由队列决定
(1)并发队列:能开启N条线程
(2)串行队列:开启1条线程
Touch ID定制Demo
LAContext *myContext = [[LAContext alloc] init];
NSError *authError = nil;
NSString *myLocalizedReasonString = <#String explaining why app needs authentication#>;
if ([myContext canEvaluatePolicy:LAPolicyDeviceOwnerAuthenticationWithBiometrics error:&authError]) {
[myContext evaluatePolicy:LAPolicyDeviceOwnerAuthenticationWithBiometrics
localizedReason:myLocalizedReasonString
reply:^(BOOL success, NSError *error) {
if (success) {
// User authenticated successfully, take appropriate action
dispatch_async(dispatch_get_main_queue(), ^{
// write all your code here
});
} else {
// User did not authenticate successfully, look at error and take appropriate action
switch (error.code) {
case LAErrorAuthenticationFailed:
NSLog(@"Authentication Failed");
break;
case LAErrorUserCancel:
NSLog(@"User pressed Cancel button");
break;
case LAErrorUserFallback:
NSLog(@"User pressed \"Enter Password\"");
break;
default:
NSLog(@"Touch ID is not configured");
break;
}
NSLog(@"Authentication Fails");
}
}];
} else {
// Could not evaluate policy; look at authError and present an appropriate message to user
}
iOS ---沙盒
Documents目录
保存应用程序运行时生成的需要持久化的数据,Itunes会自动备份该目录
Library目录
存储程序的默认设置和其他状态信息,Itunes会自动备份该目录
tmp目录
保存应用运行时所需要的临时数据,使用完毕后在将相应的文件从该目录删除。
获取沙盒路径
NSString *path = NSHomeDirectory();
获取沙盒下文件目录的路径
有三种获取方法:
1.拼接字符串:
NSString *Documents = [path stringByAppendingString:@"/Documents"];
2.拼接路径
NSString *Documents2 = [path stringByAppendingPathComponent:@"Documents"];
3.系统提供的获取沙盒下目录路径的方法
NSString *Documents3 = [NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, NO) firstObject];
//NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, NO) 方法返回值为只有一个元素的数组,所以去第一个元素
对于其他两个文件路径的获取方式都一样,系统提供的方法有些区别
NSString *library3 = [NSSearchPathForDirectoriesInDomains(NSLibraryDirectory, NSUserDomainMask, YES) firstObject];
//对于Library文件获取路径方法,只是参数有些不一样
//获取library下的caches
NSString *cachesStr = [NSSearchPathForDirectoriesInDomains(NSCachesDirectory, NSUserDomainMask, YES) firstObject];
获取tmp文件夹路径(系统提供的方法)
NSString *tmpStr = NSTemporaryDirectory();
获取程序包路径
NSString *appPath = [NSBundle mainBundle].resourcePath;
获取程序包内种的一个图片资源(apple.png)路径
NSString *imagePath = [[NSBundle mainBundle] pathForResource:@"apple" ofType:@"png"];
简单对象的读写(I/O)操作
简单对象:字符串,数组,字典,data
字符串存储
//获取存储的目录
NSString *str = [NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES) firstObject];
//在沙盒文件夹下的Documents内创建.txt文件
NSString *newPath = [str stringByAppendingPathComponent:@"text.txt"];
//存入内容
NSString *name = @"蓝欧科技, nihao";
[name writeToFile:newPath atomically:YES encoding:NSUTF8StringEncoding error:nil];
字符串读取
NSString *path = [NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES) firstObject];
NSString *newPath = [path stringByAppendingString:@"/text.txt"];
NSString *string = [NSString stringWithContentsOfFile:newPath encoding:NSUTF8StringEncoding error:nil];
NSLog(@"%@", string);
数组存储
[nameArr writeToFile:newPath atomically:YES];
数组读取
NSArray *array = [NSArray arrayWithContentsOfFile:newPath];
字典存储
[dict writeToFile:newPath atomically:YES];
字典读取
NSDictionary *dict = [NSDictionary dictionaryWithContentsOfFile:newPath];
NSData类型存储
NSData *data = [str dataUsingEncoding:NSUTF8StringEncoding];
//写入文件
[data writeToFile:dataPath atomically:YES];
NSData读取
//获取路径
NSData *data = [NSData dataWithContentsOfFile:dataPath];
NSString *str = [[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding];
图片存储//将图片转成NSData类型在存储
NSString *path = [NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES) firstObject];
//在沙盒问价下面创建一个文件
NSString *newPath = [path stringByAppendingPathComponent:@"image.png"];
//需要存储的图片
NSData *imageData = UIImageJPEGRepresentation([UIImage imageNamed:@"tu.jpg"], 1.0);
//创建文件并存储
[imageData writeToFile:newPath atomically:YES];
图片读取
//获取沙盒地址
NSString *path = [NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES) firstObject];
//获取图片data地址
NSString *newPath = [path stringByAppendingPathComponent:@"image.png"];
//获取data
NSData *data = [NSData dataWithContentsOfFile:newPath];
//获取图片
UIImage *image = [UIImage imageWithData:data];
//添加imageView
UIImageView *imageView = [[UIImageView alloc] initWithFrame:CGRectMake(100, 200, 150, 150)];
imageView.image = image;
[self.view addSubview:imageView];
通过文件管理器来操作对象
创建对象
NSString *string = @"hello nihao";
//根据字符串创建data
NSData *data = [string dataUsingEncoding:NSUTF8StringEncoding];
创建文件管理器
NSFileManager *fileManager = [[NSFileManager alloc] init];
存储对象//向目标文件写入信息
BOOL save = [fileManager createFileAtPath:[[self documentPath] stringByAppendingPathComponent:@"studenttt.plist"] contents:data attributes:nil];
//注:[self documentPath]是获取Documents路径
//获取Documents路径
- (NSString *)documentPath {
return [NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES) firstObject];
}
读取对象
NSData *mData = [fileManager contentsAtPath:[[self documentPath] stringByAppendingPathComponent:@"studenttt.plist"]];
NSString *mStr = [[NSString alloc] initWithData:mData encoding:NSUTF8StringEncoding];
复杂对象的读写---归档和反归档
1.首先,复杂对象所属的类要遵循协议
//Person.h
@interface Person : NSObject
@property (nonatomic, strong) NSString *name;
@property (nonatomic, strong) NSString *gender;
@property (nonatomic, assign) NSInteger age;
@end
2.其次,实现协议中的两个方法
-(void)encodeWithCoder:(NSCoder *)aCoder;
-(instancetype)initWithCoder:(NSCoder *)aDecoder
//Person.m
@implementation Person
//当对象进行归档操作的时候,会自动调用这个方法
- (void)encodeWithCoder:(NSCoder *)aCoder{
[aCoder encodeObject:self.name forKey:@"name"];
[aCoder encodeObject:self.gender forKey:@"gender"];
[aCoder encodeInteger:self.age forKey:@"age"];
}
//当对象进行反归档的时候会调用
- (instancetype)initWithCoder:(NSCoder *)aDecoder{
if (self = [super init]) {
self.name = [aDecoder decodeObjectForKey:@"name"];
self.age = [aDecoder decodeIntegerForKey:@"age"];
self.gender = [aDecoder decodeObjectForKey:@"gender"];
}
return self;
}
@end
归档 ——NSKeyedArchiver
Person *person = [[Person alloc] init];
person.name = @"小明";
person.age = 23;
person.gender = @"男";
//进行归档操作
//1.创建一个NSmutableData,用来存储归档后的数据
NSMutableData *mData = [[NSMutableData alloc] init];
//2.创建归档器
NSKeyedArchiver *archiver = [[NSKeyedArchiver alloc] initForWritingWithMutableData:mData];
//进行归档
[archiver encodeObject:person forKey:@"person"];
//归档结束
[archiver finishEncoding];
//存到沙盒的文件夹中
NSString *path = [NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES) firstObject];
NSString *newPath = [path stringByAppendingPathComponent:@"guidang.plist"];
//存储data
[mData writeToFile:newPath atomically:YES];
反归档 ——NSKeyedUnarchiver
//获取路径
NSString * newPath = [[self documentPath] stringByAppendingPathComponent:@"guidang.plist"];
//取出数据
NSData *data = [NSData dataWithContentsOfFile:newPath];
//创建反归档器
NSKeyedUnarchiver *unArchiver = [[NSKeyedUnarchiver alloc] initForReadingWithData:data];
Person *person = [unArchiver decodeObjectForKey:@"person"];
//反归档结束
[unArchiver finishDecoding];
NSLog(@"name : %@ age : %ld gender : %@", person.name, person.age, person.gender);
iOS语言本地化/国际化
应用名称本地化/国际化
InfoPlist.strings
CFBundleDisplayName = "国际化App名称";
代码中字符串的本地化
Localizable.stirings
// NSLocalizedString(key, comment) 本质
// NSlocalizeString 第一个参数是内容,根据第一个参数去对应语言的文件中取对应的字符串,第二个参数将会转化为字符串文件里的注释,可以传nil,也可以传空字符串@""。
#define NSLocalizedString(key, comment) [[NSBundle mainBundle] localizedStringForKey:(key) value:@"" table:nil]
Targets:
Build Settings
Search Paths
Header Search Paths
NSNotification
NSNotificationCenter *center = [NSNotificationCenter defaultCenter];
[center addObserver:self selector:@selector(notifyUrlScheme:) name:kNotifyUrlScheme object:nil];
NSNotification *notice = [NSNotification notificationWithName:kNotifyUrlScheme object:self userInfo:dic];
[[NSNotificationCenter defaultCenter] postNotification:notice];
NSNotificationCenter *center = [NSNotificationCenter defaultCenter];
[center removeObserver:self name:kNotifyUrlScheme object:nil];
Notification
Local Notification
remote notifications
- (void)application:(UIApplication *)application didRegisterForRemoteNotificationsWithDeviceToken:(NSData *)deviceToken {
}
- (void)application:(UIApplication *)application didReceiveRemoteNotification:(NSDictionary *)userinfo {
}
atomic和nonatomic用来决定编译器生成的getter和setter是否为原子操作。
assign
对基础数据类型 (NSInteger,CGFloat)和C数据类型(int, float, double, char)等等。
此标记说明设置器直接进行赋值,这也是默认值。
retain
对其他NSObject和其子类对参数进行release旧值,再retain新值
copy
对NSString 它指出,在赋值时使用传入值的一份拷贝。
也就是说,retain 是指针拷贝,copy 是内容拷贝
weak 和 strong 属性只有在你打开ARC时才会被要求使用,这时你是不能使用retain release autorelease 操作的,因为ARC会自动为你做好这些操作,但是你需要在对象属性上使用weak 和strong,其中strong就相当于retain属性,而weak相当于assign。
ARC的重点:
它是Compile-time的技术,并不是在runtime用thread去处理
开发者不能再直接使用retain,release,autorelease,改由compiler帮你写这些代码。
所有的property不再使用retain,assign而改用strong,weak取代。
NSAutoreleasePool也要改用特有的@autoreleasepool{}来取代
ARC的工作原理:
在你编译程序时,将内存操作的代码(retain,release或autorelease)自动添加到需要的位置。即底层上使用和Manual Reference Counting(手工引用计数)一样的内存管理机制,但由于是自动帮你在编译时添加内存操作的代码,从而简化了编程的工作。
第31条 在dealloc方法中只释放引用并解除监听
要点
• 在dealloc方法里,应该做的事情就是释放指向其他对象的引用,并取消原来订阅的“键值观测”(KVO)或NSNotificationCenter等通知,不要做其他事情。
• 如果对象持有文件描述符等系统资源,那么应该专门写一个方法来释放此种资源。这样的类要和其使用者约定:用完资源后必须调用close方法。
• 执行异步任务的方法不应该在dealloc里调用;只能在正常状态下执行的那些方法也不应在dealloc里调用,因为此时对象已处于正在回收的状态了。
第36条 不要使用retainCount
要点
• 对象的保留计数看似有用,实则不然,因为任何给定时间点上的“绝对保留计数”(absolute retain count对象生命期的全貌。
引入ARC之后,retainCount方法就正式废止了,在ARC‰º调用该方法会导致编译器报错。
Other Framework
1.ZXingObjC---二次元コード
- (IBAction)updatePressed:(id)sender {
[self.textView resignFirstResponder];
NSString *data = self.textView.text;
if (data == 0) return;
ZXMultiFormatWriter *writer = [[ZXMultiFormatWriter alloc] init];
ZXBitMatrix *result = [writer encode:data
format:kBarcodeFormatQRCode
width:self.imageView.frame.size.width
height:self.imageView.frame.size.width
error:nil];
if (result) {
ZXImage *image = [ZXImage imageWithMatrix:result];
self.imageView.image = [UIImage imageWithCGImage:image.cgimage];
} else {
self.imageView.image = nil;
}
}
Object-C
使用synchronized方法实现:
staticidobj = nil;
+(instancetype)shareInstance
{
@synchronized(self) {
if(!obj) {
obj = [[SingletonObj alloc] init];
}
}
returnobj;
}
使用dispatch_once方法实现:
static id obj = nil;
+(instancetype)shareInstance
{
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
obj = [[SingletonObj alloc] init];
});
return obj;
}
数据类型
ObjC支持的数据类型包括:基本类型、构造类型和指针类型。其中,基本类型包括:整型、字符型、浮点型和枚举型;构造类型包括:数组类型、结构体类型和共用体类型;而指针类型是ObjC中最重要的类型。
整型
short int,int,long int,long long,unsigned
int 与 NSInteger, bool 与 BOOL, float 与 CGFloat
NSValue
另外NSNumber支持和NSString一样的@符号简写
NSArray
字符型
char,
NSString
http://blogios.stack3.net/archives/309
検索
NSString#rangeOfStringメソッドを使います。戻り値はNSRange構造体です。
NSRange#location 見つかった位置のインデックス。見つからなかった時はNSNotFound
NSRange#length 見つかった文字列の長さ
NSRange range;
range = [@"string" rangeOfString:@"ring"];
// --> range.location = 2; range.length = 4;
range = [@"string" rangeOfString:@"abc"];
// --> range.location = NSNotFound; range.length = 0;
大文字小文字区別なし。
range = [@"string" rangeOfString:@"NG" options:NSCaseInsensitiveSearch];
// --> range.location = 4; range.length = 2;
後ろから検索。
range = [@"abcabcabc" rangeOfString:@"abc" options:NSBackwardsSearch];
// --> range.location = 6; range.length = 3;
複数のオプションをORで指定することもできます。以下は、大文字小文字区別なし、かつ、後ろから検索。
range = [@"abcabcabc" rangeOfString:@"ABC" options:NSCaseInsensitiveSearch|NSBackwardsSearch];
// --> range.location = 6; range.length = 3;
optionsに0を指定すると条件指定なし。単なるrangeOfStringと同じです。
range = [@"abcabcabc" rangeOfString:@"ABC" options:0];
// --> range.location = NSNotFound; range.length = 0;
正規表現による検索。
range = [@" abc" rangeOfString:@"\w+" options:NSRegularExpressionSearch];
// --> range.location = 2; range.length = 3;
範囲を指定して検索。
range = [@"abcabc" rangeOfString:@"abc" options:0 range:NSMakeRange(3, 3)];
// --> range.location = 3; range.length = 3;
range = [@"abcabc" rangeOfString:@"ABC" options:NSCaseInsensitiveSearch range:NSMakeRange(3, 3)];
// --> range.location = 3; range.length = 3;
置換
置換はメソッド名が長いです・・・
string = [@"string" stringByReplacingOccurrencesOfString:@"ring" withString:@"and"];
// --> @"stand"
また、見つかった文字は全部置換されることに注意が必要です。
string = [@"stringring" stringByReplacingOccurrencesOfString:@"ring" withString:@"and"];
// --> @"standand"
最初に見つかった文字だけ置換したい場合。
string = @"stringring";
NSRange range = [string rangeOfString:@"ring"];
if (range.location != NSNotFound) {
string = [string stringByReplacingCharactersInRange:range withString:@"and"];
// --> @"standring"
}
文字列の連結
NSString *string = @"abc";
string = [string stringByAppendingString:@"def"];
// string --> @"abcdef"
もちろん変数に代入にしなくても直接呼び出せます。
string = [@"abc" stringByAppendingString:@"def"];
STAssertEqualObjects(string, @"abcdef", nil);
NSMutableStringを使う場合
NSMutableString *mutableString = [NSMutableString stringWithString:@"abc"];
// こういう書き方もできる
// mutableString = [@"abc" mutableCopy];
[mutableString appendString:@"def"];
// string --> @"abcdef"
数値などを文字列に埋めたい時は、フォーマットを使う。
string = @"abc";
string = [string stringByAppendingFormat:@"%d", 100];
// string --> @"abc100"
mutableString = [NSMutableString stringWithString:@"abc"];
[mutableString appendFormat:@"%d", 100];
// string --> @"abc100"
あまり使わないと思いますが、こういうのもあります。
string = @"abc";
string = [string stringByPaddingToLength:6 withString:@"x" startingAtIndex:0];
// string --> @"abcxxx"
文字列は第1引数Lengthの長さになります。
文字列がLengthより短い場合は末尾を第2引数のStringで埋めます。
文字列が第2引数の文字列のどの位置から連結するかを第3引数で決定します。
第3引数がわかりづらいとおもうのですが、たとえばこういうことです。
string = @"abc";
string = [string stringByPaddingToLength:9 withString:@"efg" startingAtIndex:2];
// string -> @"abcgefgef"
文字列の長さを9文字にして、@”efg”のindex:2つまり@”g”から連結をはじめ、次は@”ef”と連結するのを繰りかえすことになります。
文字列の分解
カンマ区切りの文字列を分解。
NSString *string = @"Apple,Orange,Grape";
NSArray *array = [string componentsSeparatedByString:@","];
// array --> @[@"Apple", @"Orange", @"Grape"];
カンマ+スペース区切りの文字列を分解
string = @"Apple, Orange, Grape";
array = [string componentsSeparatedByString:@", "];
// array --> @[@"Apple", @"Orange", @"Grape"]
空白(半角スペース、全角スペース)で区切られているものを分解。こういう場面ではNSCharacterSetを使うと良い。
string = @"Apple Orange Grape";
array = [string componentsSeparatedByCharactersInSet:[NSCharacterSet whitespaceCharacterSet]];
// array --> @[@"Apple", @"Orange", @"Grape"]
指定インデックスから切り取る。
string = [@"abcdef" substringFromIndex:3];
// string --> @"def"
指定インデックスより前までを切り取る。指定インデックスは含まない。
string = [@"abcdef" substringToIndex:3];
// string --> @"abc"
指定範囲を切り取る。
string = [@"abcdef" substringWithRange:NSMakeRange(2, 2)];
// string --> @"cd"
NSMutableString好比一个字符串链表,
- \- (void)appendString:(NSString *)aString;
+ 拼接aString到最后面
- \- (void)appendFormat:(NSString *)format, ...;
+ 拼接一段格式化字符串到最后面
- (void)deleteCharactersInRange:(NSRange)range;
删除range范围内的字符串
- (void)insertString:(NSString *)aString atIndex:(NSUInteger)loc;
在loc这个位置中插入aString
NSUserDefault用法
方法很简单,一行代码就可以搞定
//保存
- (void)saveValue:(NSString *)value forKey:(NSString *)key
{
[[NSUserDefaults standardUserDefaults] setObject:value forKey:key];
}
//读取
- (void)readValueforKey:(NSString *)key
{
[[NSUserDefaults standardUserDefaults] objectForKey:key];
}
[[NSUserDefaults standardUserDefaults] setValue:version forKey:@"newfeatures"];
[[NSUserDefaults standardUserDefaults] synchronize];
NSString *newUserAgent = [NSString stringWithFormat:@"KuronekoyamatoOfficialApp/%@ %@", aplversion, userAgent];
NSDictionary *dic = [[NSDictionary alloc] initWithObjectsAndKeys:newUserAgent, @"UserAgent", nil];
[[NSUserDefaults standardUserDefaults] registerDefaults:dic];
(lldb) po dic
{
UserAgent = "KuronekoyamatoOfficialApp/2.26.0 Mozilla/5.0 (iPhone; CPU iPhone OS 9_3 like Mac OS X) AppleWebKit/601.1.46 (KHTML, like Gecko) Mobile/13E230”;
}
注意
NSUserDefault只能保存特定类型的对象:NSData, NSString, NSNumber, NSDate, NSArray, NSDictionary。如果想要存储其它类型,需要将打包成NSData类型。
当应用第一次设置某项用户偏好设置的值时,相应的值会通过指定的键加入应用域。当通过NSUserDefaults获取某项用户偏好设置的值时,NSUserDefaults会先在应用域中查找,如果找到了值,NSUserDefaults就会返回这个值。如果没有找到,NSUserDefaults就会在注册域中查找并返回默认值。
NSUserDefaults从上到下透过域的层级寻找正确的值,不同的域有不同的功能,有些域是可持久的,有些域则不行。
应用域(application domain)是最重要的域,它存储着你app通过NSUserDefaults set...forKey添加的设置。
注册域(registration domain)仅有较低的优先权,只有在应用域没有找到值时才从注册域去寻找。
全局域(global domain)则存储着系统的设置
语言域(language-specific domains)则包括地区、日期等
参数域( argument domain)有最高优先权
Protocol协议
@protocol ProtocolDelegate // 必须实现的方法
@required
- (void)error;
// 可选实现的方法
@optional
- (void)other;
- (void)other2;
- (void)other3;
@end
#import
@interface ASStudent : NSObject
{
NSString* name;//ios当中 对象都是在堆上分配的,所以都是指针
@public
unsigned int age;
@private
NSString* sid;//
}
+(void)print; // + 代表方法为 类方法; void为返回值类型, print为签名
-(void)setName:(NSString*)aName;// - 代表方法为 实例方法; void为返回值类型,setName: 为 签名 :也属于签名部分; NSString* 为形参类型, aName为形参
-(NSString*)name;
//-(void)name;//error 实例方法签名不能相同 这个和上面都是 name
-(void)name:(int)aname;// 签名部分为 name: 包含冒号部分所以不算相同
+(void)name; //true 类方法和 实例方法签名可以相同方法签名不能一样,不支持重载
//在同一个类内方法不能重载,即方法的签名不能完全一样;但是类方法和实例方法签名可以相同
//方法的签名 和 参数类型、参数名称无关
//方法签名和方法的返回值类型无关;
@end
实现:
#import "ASStudent.h"
@implementation ASStudent
-(NSString*)name{
return name;
}
-(void)setName:(NSString *)aName{
name = aName;
}
+(void)print{
NSLog(@"类方法");
}
//实例方法可以直接引用类的实例变量和其他实例方法
//类的方法都是public的,没有protected 和 private方法 ,但是如果一个方法 只是出现在类的方法里面,没有出现在类的声明里,name这个方法可以认为是私有的!
@end
.h文件中@interface指令用来标识文件的接口代码的起始位置,而@end指令标示该段的结束位置。在.m文件中,@implementation指令用来标识实现的起始位置,@end标识结束位置
@interface用于定义类的公共接口,通常,接口被称为API(application programming interface)而真正使对象能够运行的代码,位于@implementation中。
当我们要给一个Car类声明一个发动机属性的时候,如果对外公开,则代码为
文/xuyafei86(简书作者)
原文链接:http://www.jianshu.com/p/4f7dda4a5192
著作权归作者所有,转载请联系作者获得授权,并标注“简书作者”。如果不对外公开,则在.m里的代码为
关于Objective-C Runtime看我就够了
http://www.jianshu.com/p/f73ea068efd2
目通常采用工程文件设计结构的例子
1.主目录结构
-ProjectDemo
--Features//模块。包含各个模块的Model,View,Controller,Manager
--categories//类目。包含各种类的分类
--Frameworks//系统框架。包含导入的系统的框架
--Helpers//帮助类。包含网络,数据库,归档,定位等操作类的封装和实现
--Utilites//工具类,一些非对象的,而是类方法调用的类
--Vendors//第三方库。部分需要修改或者不支持cocoapod的第三方的框架引入
--Config//配置。包含宏定义文件,全局配置文件,全局常量文件,颜色配置文件
--Resources//资源。包含plist,image,html,bundle,Localizable.strings等
--AppEntry//程序入口。包含AppDelegate,main.c,info.plist
-PAHealthTests
-PAHealthUITests
-Products// 系统自动生成的.app所在文件夹
-Pods// 采用 CocoaPods 管理的第三方库。
2.模块目录结构
-- Features
---Base//MVC的基类或者通用类
----Models//数据模型
----Views//视图
----Controllers//控制器
----Manager//store层的数据管理类
---Home
----Models
----Views
----Controllers
----Manager
---UserCenter
----Models
----Views
----Controllers
----Manager
---UserEntry
----Models
----Views
----Controllers
----Manager
---Payment
----Models
----Views
----Controllers
----Manager
…