1、熟练掌握Object-C/C,对Object-C的内存管理机制有较好的理解。
1)手动管理内存MRC和ARC模式
2)内存管理的定义:内存管理就是确保开辟的堆空间得到正确的释放
如果堆空间没有释放,称为【内存泄露】
使用已释放的堆空间,称为【提前释放】
重复释放同一个空间,称为【重复释放】
3)传统内存管理的困境:
(1)当我们要释放一个堆,首先要确定使用这个堆的指针,都访问完毕,避免提前释放。
(2)释放指针指向的堆空间,首先要确定那些指针指向同一个堆,这些指针,
只能释放一次,避免重复释放。
(3)模块化操作,那个模块负责释放,将成为最大的难题
(4)多线程的操作,不却定那个线程最后使用完毕
解决办法:【引用计数器】
引用计数简称计数器,某一个指针指向了一个对象,
引用计数加1,减少一个指针指向对象,引用计数减1,
当引用计数为0的时候,由系统自动释放该对象占用的空间
(模拟多人网络游戏,斗地主、QQ堂)
新建立一个对象,系统会自动给该对象添加一个引用计数(retaincount)的属性
4)内存管理的黄金法则:
1,凡是用alloc,retain,new(或者以new开头的方法),
copy(或者copy开头的方法),mutableCopy
(或者muTableCopy开头的方法)创建的对象,
都需要用release或者autorelease释放。
2,谁创建谁释放(那个类创建,那个类释放,谁写alloc,谁写release)
5)如何将工程改为MRC:
1点击工程-》build settings->通过gar关键字的搜索搜出Automatic Re..Cou..
->将此选项的设置改称NO
6)alloc和release
alloc,创建对象的过程,同时会将对象引用计数0加1,变成1
retain使对象引用计数器加1
release 会使对象引用次数减一
7)析构函数
- (void)dealloc方法的注意事项
dealloc,称为析构函数,调用顺序和构造函数相反,
一般情况下会在该函数中做一些释放内存工作
是在引用计数减为0时才会被调用
8)autorelease与autoreleasepool
autoreleasepool 自动释放池,管理池子中的对象
autorelease,给对象发送autorelease消息时,会将对象存入autoreleasepool,
当系统执行完自动释放池时,自动释放池会自动清空池子里边的对象
【注】自动释放池类似于一个数组,进行延迟释放,不会马上计数器减一,
而是将当前对象放入最近的自动释放池中,当释放池释放的时候将池中每一个元素都释放一次
加方法的内存管理
+(Student*)create
{
Student *student=[[[Student alloc]init]autorelease];
// [student autorelease];
return student;
}
【注意事项】
1,对象的成员变量在构造方法中创建,应该在析构函数中释放
2,注意指针的转移,释放旧对象,保留新对象
3,从数据结构中如数组中读取出对象地址,如果需要长时间使用应当retain
1.2.4 字符串内存管理
1.2.4.1 字符串的内存管理
1,@“ ”创建出来的字符串在只读数据段,是只读的,引用计数为-1,
默认的是autorelease 对象不能给它发 送retainCount++ 或 retainCount - - 的消息
2、alloc retain copy mutablecopy new创建出的字符串均需要release
3、类方法创建出的字符串和直接赋值的字符串,不需要手动管理内存
1.2.4.2 copy和mutableCopy(只用来复制字符串)
1,copy发消息,可变和不可变字符串均转换成不可变字符串
NSMutableString *mulStr=[[NSMutableString alloc]initWithString:@"abc"];
NSString *str=@"a";
NSMutableString *mulStr2=[mulStr copy];
[mulStr2 appendString:@"aa"];
2,mutableCopy,无论是可变还是不可变字符串,都将转换可变字符串
NSMutableString *mulString=[[NSMutableString alloc]initWithString:@"abc"];
NSString *str=@"ab";
NSMutableString* mulStr= [str mutableCopy];
[mulString appendString:@"ab"];
1.2.5 数组的内存管理
结论
1、在可变数组中,通过添加和删除对象,均可以对该对象的引用计数加1和减1
2、在不可变数组中初始化时,初始化的对象的引用计数会加1,
当该数组销毁的时候(release),数组会给初始化的对象的引用计数减1
2,【自动内存管理 ARC】
【注】1,简单点说就是让编译器完成堆空间的引用计数的加减,自动释放,
程序员不再写retain 和release等方法
2,OC的自动内存管理不同于java 的垃圾回收,
而是在预处理是直接在应该保留的地方加上retain,
在应该改释放的地方加上release,是直接添加代码
3,从效率上讲,ARC优于手动内存管理
2.1.1 ARC 的局限性
1,使用ARC可能因为代码的不规范,导致内存提前释放
2,导入第三方库,或者导入旧代码,这些代码不支持ARC
2.2 使用ARC 的技巧
2.2.1常用两个关键字修饰引用
__strong(强引用) 缺省属性,其修饰的对象指针,指向哪个对象,
会对该对象retain,离开哪个对象,会对该对象release。
__weak(弱引用)其修饰的对象指针,指向任何对象都不会retain。
这样的指针指向的对象随时可能消失。
如果对象消失了,这个指针会自动变成nil。
//在iOS编程中,代理对象使用弱引用。
2、熟悉IOS程序的生命周期,熟悉Foundation框架及UIKit框架。
熟练使用inerface bulider在程序中的开发。
熟练使用MVC在应用中作分层开发。
iOS的生命周期:程序运行->入口didFinshLaunchWithOptions
->变为活动状态applicationDidBecomeActive
用户点home将程序退到后台applicationDidEnterBackground
用户点程序 程序进入前台
->即将进入前台applictionWillEnterForeground
->进入活动状态applicationDidBecomeActive
用户来电终止当前应用程序 applicationWillTerminate
iphone个代的尺寸iPhone4 320 * 480
iPhone4S 320 * 480
iPhone5 320 * 568
iPhone5S 320 * 568
iPhone6 375 * 667
iPhone6 Plus 414 * 736
MVC:
1)最上面的一层,是直接面向最终用户的"视图层"(View)。
它是提供给用户的操作界面,是程序的外壳。
2)最底下的一层,是核心的"数据层"(Model),
也就是程序需要操作的数据或信息。
3)中间的一层,就是"控制层"(Controller),
它负责根据用户从"视图层"输入的指令,选取"数据层"中的数据,
然后对其进行相应的操作,产生最终结果。
这三层是紧密联系在一起的,但又是互相独立的,每一层内部的变化不影响其他层。
每一层都对外提供接口(Interface),供上面一层调用。
这样一来,软件就可以实现模块化,修改外观或者变更数据都不用修改其他层,大大方便了维护和升级。
Xcode中的Interface Builder可以让您无需任何代码即可轻松地设计一个完整的用户界面。
您可以在设计画布上简单拖放窗口、按钮、文本框以及其他对象来创建一个能运行在
Mac、iPhone或者iPad上的用户界面。
iOS 和OS X都有一个强大的布局系统--Auto Layout,
内置在Interface Builder 中的优秀支持。
Auto Layout的理念来源于--
界面中的每个对象都可以定义一个约束来控制它对父视图和其他界面控件的反应。
比如,当展示不同的语言时,你可以优先考虑是否要一个按钮保持特定的尺寸或者调为更大尺寸的文本
Interface Builder可自动为您创建约束,以确保一些兼容性的规则。
您也可以直接控制约束来定义具体的优先级,
定义您的应用如何在不同尺寸的屏幕上运行,如何在屏幕旋转时运行,
以及如何在新语言环境中运行
3、熟悉Controller及View的生命周期。
熟练使用主流的导航模式。
熟悉tableView及collectionView的使用及复用方法。
controller的生命周期
controller 初始成功后压栈
视图出现:
1.loadView
——> 2.viewDidLoad
——> 3.viewWillAppear
——> 4.viewDidAppear
视图消失:
1.viewWillDisappear
——> 2.viewDidDisappear
——> 3.dealloc
1.在view加载过程中首先会调用loadView方法,在这个方法中主要完成一些关键view的初始化工作
2.接下来就是加载view,加载成功后,会接着调用viewDidLoad方法,
这里要记住的一点是,在loadView之前,是没有view的,也就是说,在这之前,view还没有被初始化。
完成viewDidLoad方法后,ViewController里面就成功的加载view了,
3.loadView和viewDidLoad的区别就是,loadView时view还没有生成,
viewDidLoad时,view已经生成了,loadView只会被调用一次,
而viewDidLoad可能会被调用多次(View可能会被多次加载),
当view被添加到其他view中之前,会调用viewWillAppear,之后会调用viewDidAppear。
当view从其他view中移除之前,调用viewWillDisAppear,移除之后会调用viewDidDisappear。
当view不再使用时,受到内存警告时,ViewController会将view释放并将其指向为nil。
复用的方法:采用复用队列的原理:将推出到屏幕的cell放到复用队列中,
以后需要的时候先从复用队列中寻找是否可以复用的cell,如果没有才会实例花cell
4、熟练使用IOS中的6种手势作界面交互。熟悉IOS动画,掌握基础动画在应用中的使用。
iOS 中六种手势
1.tap点击手势 2.pinchs 缩放手势 3.rotate 旋转手势
4.swipe 滑动手势 5.pan 慢滑手势 6.longPress长按手势
一、点击手势
1.UITapGestureRecognizer
1) 设置点击的次数 tap.numberOfTapsRequired = 2;
2)先响应 后面的手势对象 如果后面的手势对象失败 则会响应前面的手势对象[tap1 requireGestureRecognizerToFail:tap];
注:比如tap为双击,tap1为单击,当第一次相应完后面的双击,单击的时候就相应前面的单击
二、缩放手势
1. UIPinchGestureRecognizer
UIPinchGestureRecognizer *pinch = [[UIPinchGestureRecognizer alloc] initWithTarget:self a ction:@selector(pinch:)];
[imageView addGestureRecognizer:pinch];
-(void)pinch:(UIPinchGestureRecognizer *)p
{
imageView.transform = CGAffineTransformScale(imageView.transform, p.scale, p.scale);
//NSLog(@"%@ ---> %f",NSStringFromSelector(_cmd),p.scale);
//缩放一次之后 要把缩放比重置
p.scale = 1.0f;
}
注意:缩放的时候每一次要将缩放比例重置为1,以免缩放过快
三、旋转手势
UIRotationGestureRecognizer
UIRotationGestureRecognizer *rotate = [[UIRotationGestureRecognizer alloc] initWithTarget:self action:@selector(rotate:)];
[imageView addGestureRecognizer:rotate];
(void)rotate:(UIRotationGestureRecognizer *)r
{
imageView.transform = CGAffineTransformRotate(imageView.transform, r.rotation);
//NSLog(@"%@--->%f",NSStringFromSelector(_cmd),r.rotation);
//重置
r.rotation = 0.0f;
}
注意:旋转的时候每一次要将旋转角度重置为0.0;
四、快速滑动
UISwipeGestureRecognizer
1,方向默认向右
如果需要向左,向右一起滑动需要2个快速滑动手势,一个向左,一个向右,可以模拟相册中相片的滑动
UISwipeGestureRecognizer *swip = [[UISwipeGestureRecognizer alloc] initWithTarget:self action:@selector(swip:)];
//方向 默认 向右
swip.direction = UISwipeGestureRecognizerDirectionLeft;
[imageView addGestureRecognizer:swip];
UISwipeGestureRecognizer *swip1 = [[UISwipeGestureRecognizer alloc] initWithTarget:self action:@selector(swip:)];
//方向 默认 向右
swip1.direction = UISwipeGestureRecognizerDirectionRight;
[imageView addGestureRecognizer:swip1];
五、慢速滑动手势
UIPanGestureRecognizer
1.IPanGestureRecognizer *pan = [[UIPanGestureRecognizer alloc] initWithTarget:self action:@selector(pan:)];
[imageView addGestureRecognizer:pan];
-(void)pan:(UIPanGestureRecognizer *)p
{
switch (p.state) {
case UIGestureRecognizerStateBegan:
{
_beginPoint = [p locationInView:self.view];
_center = imageView.center;
NSLog(@"11111%@",NSStringFromCGPoint([p locationInView:self.view]));
}
break;
case UIGestureRecognizerStateChanged:
{
CGPoint point = [p locationInView:self.view];
NSInteger x_offset = point.x - _beginPoint.x;
NSInteger y_offset = point.y - _beginPoint.y;
NSLog(@"22222%@",NSStringFromCGPoint([p locationInView:self.view]));
imageView.center = CGPointMake(_center.x + x_offset, _center.y + y_offset);
}
break;
case UIGestureRecognizerStateEnded:
NSLog(@"33333%@",NSStringFromCGPoint([p locationInView:self.view]));
break;
default:
break;
}
//NSLog(@"%@",NSStringFromSelector(_cmd));
}
注:以上代码中模拟点击相片跟着鼠标一起滑动,注意观察began,change。endde中点得取值范围,将滑动点击point.x写在change中写得原因,因为如。。。。。。。第一个为起始点,最后一个结束点,倒数第2个是在change中得改变点和结束点的坐标是一样的,若写在end方法中滑动就会补连贯,很别扭啊,注意观察点就知道了啊。。。
六、长按手势
UILongPressGestureRecognizer
-(void)createLongPressGuesture
{
UILongPressGestureRecognizer *longPress = [[UILongPressGestureRecognizer alloc] initWithTarget:self action:@selector(longPress:)];
longPress.minimumPressDuration = 2.0f;
[imageView addGestureRecognizer:longPress];
}
-(void)longPress:(UILongPressGestureRecognizer *)l
{
if(l.state == UIGestureRecognizerStateBegan)
{
NSLog(@"%@",NSStringFromSelector(_cmd));
}
}
动画效果
1.
// 创建动画对象
CATransition * tran = [CATransition animation];
// 设置动画持续时间
tran.duration = 2;
// 设置动画类型
/*type kCATransitionPush kCATransitionMoveIn kCATransitionReveal kCATransitionFade*/
NSArray * type = @[kCATransitionPush,kCATransitionMoveIn,kCATransitionReveal,kCATransitionFade];
// 子类型 动画方向
/*subType kCATransitionFromLeft kCATransitionFromRight kCATransitionFromTop kCATransitionFromBottom */
NSArray * subType = @[kCATransitionFromLeft,kCATransitionFromRight,kCATransitionFromTop,kCATransitionFromBottom];
//动画的效果
NSArray * privateArr = @[@"suckEffect",@"rippleEffect",@"pageCurl",@"pageUnCurl",@"oglFlip",@"cameraIris",@"cameraIrisHollowOpen",@"cameraIrisHollowClose"];
if (b.tag < 14) {
tran.type = type[b.tag-10];
tran.subtype = kCATransitionFromLeft;
}
else if (b.tag > 13 && b.tag < 18){
tran.type = kCATransitionPush;
tran.subtype = subType[b.tag-14];
}else{
// 私有动画
tran.type = privateArr[b.tag-18];
tran.subtype = kCATransitionFromLeft;
}
// 添加动画
[self.window.layer addAnimation:tran forKey:nil];
[self.window exchangeSubviewAtIndex:0 withSubviewAtIndex:1];
2,页面跳转的动画效果
SecondViewController *sec = [[SecondViewController alloc] init];
sec.view.backgroundColor = [UIColor purpleColor]; 颜色
设置动画效果: 默认是第一种;
sec.modalTransitionStyle = UIModalTransitionStylePartialCurl;
UIModalTransitionStyleCoverVertical
UIModalTransitionStyleFlipHorizontal,
UIModalTransitionStyleCrossDissolve,
UIModalTransitionStylePartialCurl,
[self presentViewController:sec animated:YES completion:^{ //将会跳转到sec试图,是否需要动画效果;
}];表示将会跳转到sec试图中去,需要动画效果;
如果想在第二张图中创建一个按钮,点击按钮使其返回第一张试图, 可以在第二张的按键监听方法中设置dismissViewControllerAnimated方法:
[self dismissViewControllerAnimated:YES completion:^{
}];
3.移动,缩放的动画效果
[UIView beginAnimations:nil context:nil]; 设置动画效果;
[UIView setAnimationDuration:1.0f]; 时间为1秒,不设置的话默认会为0.3秒左右;
[UIView commitAnimations]; 动画结束
view.frame = CGRectMake(100, 200, 100, 100);
移动
view.transform = CGAffineTransformTranslate(view.transform, 0, 100); 在x上偏移0,y上偏移100;
缩放
view.transform = CGAffineTransformScale(view.transform, 0.3f,0.3f); 参数大于1为放大,小于1为缩小;
旋转
view.transform = CGAffineTransformRotate(view.transform, M_PI / 180 * 60); 旋转60度;
[UIView animateWithDuration:<#(NSTimeInterval)#> animations:<#^(void)animations#> completion:<#^(BOOL finished)completion#>]
第一个参数写时间,第二个参数写动画效果,第三个参数写第二的动画;
效果是运行完第一个动画之后会自动运行第二个动画;
双击一下第二第三个参数会自动显示空白的block,将动画代码写在里面;
如果想要添加第三第四个动画,可以在completion:<#^(BOOL finished)completion#>]后面继续添加;
5、熟练使用单例、代理、观察者、策略、工厂等设计模式。
单例模式:就是一个类只能有一个是例子的时候,只有一个实例。
单例模式确保某一个类只有一个实例,而且自行实例化并向整个系统提供这个实例。
这个类称为单例类。
1.单例模式的要点:
显然单例模式的要点有三个:一是某个类只能有一个实例;
二是它必须自行创建这个实例;
三是它必须自行向整个系统提供这个实例。
2.单例模式的优点:
1.实例控制:Singleton 会阻止其他对象实例化其自己的 Singleton 对象的副本,
从而确保所有对象都访问唯一实例。
2.灵活性:因为类控制了实例化过程,所以类可以更加灵活修改实例化过程
iOS中的单例模式
在objective-c中要实现一个单例类,至少需要做以下四个步骤:
1、为单例对象实现一个静态实例,并初始化,然后设置成nil,
2、实现一个实例构造方法检查上面声明的静态实例是否为nil,如果是则新建并返回一个本类的实例,
3、重写allocWithZone方法,用来保证其他人直接使用alloc和init试图获得一个新实力的时候不产生一个新实例,
4、适当实现allocWitheZone,copyWithZone,release和autorelease。
系统中的单利类:UIApplication(应用程序实例)
NSNotificationCenter(消息中心):
NSFileManager(文件管理):
NSUserDefaults(应用程序设置):
NSURLCache(请求缓存):
NSHTTPCookieStorage(应用程序cookies池):
代理模式:(一)代理模式
应用场景:当一个类的某些功能需要由别的类来实现,
但是又不确定具体会是哪个类实现。
优势:解耦合
敏捷原则:开放-封闭原则
实例:tableview的 数据源delegate,通过和protocol的配合,完成委托诉求。
列表row个数delegate
自定义的delegate
(二)观察者模式
应用场景:一般为model层对,controller和view进行的通知方式,
不关心谁去接收,只负责发布信息。
优势:解耦合
敏捷原则:接口隔离原则,开放-封闭原则
实例:Notification通知中心,
注册通知中心,
任何位置可以发送消息,
注册观察者的对象可以接收。
kvo,键值对改变通知的观察者,平时基本没用过。
(三)MVC模式
应用场景:是一中非常古老的设计模式,
通过数据模型,控制器逻辑,视图展示将应用程序进行逻辑划分。
优势:使系统,层次清晰,职责分明,易于维护
敏捷原则:对扩展开放-对修改封闭
实例:model-即数据模型,
view-视图展示,
controller-进行UI展现和数据交互的逻辑控制。
(四)单例模式
应用场景:确保程序运行期某个类,只有一份实例,用于进行资源共享控制。
优势:使用简单,延时求值,易于跨模块
敏捷原则:单一职责原则
实例:[UIApplication sharedApplication]。
注意事项:确保使用者只能通过 getInstance方法才能获得,单例类的唯一实例。
java,C++中使其没有公有构造函数,私有化并覆盖其构造函数。
object c中,重写allocWithZone方法,保证即使用户用 alloc方法直接创建单例类的实例,
返回的也只是此单例类的唯一静态变量。
(五)策略模式
应用场景:定义算法族,封装起来,使他们之间可以相互替换。
优势:使算法的变化独立于使用算法的用户
敏捷原则:接口隔离原则;多用组合,少用继承;针对接口编程,而非实现。
实例:排序算法,NSArray的sortedArrayUsingSelector;
经典的鸭子会叫,会飞案例。
注意事项:1,剥离类中易于变化的行为,通过组合的方式嵌入抽象基类
2,变化的行为抽象基类为,所有可变变化的父类
3,用户类的最终实例,通过注入行为实例的方式,设定易变行为
防止了继承行为方式,导致无关行为污染子类。
完成了策略封装和可替换性。
具体的使用:1.抽象出一个基类,
把不同的验证写成子类,
这样在所有地方就可以调用同一个接口,
大大降低使用者的复杂度。
1 #import <Foundation/Foundation.h>
- static NSString* const InputValidationErrorDomain = @"InputValidationErrorDomain";
- @interface InputValidator : NSObject
- -(BOOL) validateInput:(UITextField*)input error:(NSError**)error;
- 可以看到我们仅仅提供了一个实例方法把要验证的对象传进去,然后传一个填充error的指针。
- 返回验证结果为BOOL型。
(六)工厂模式
世界上就是由一个工厂类,根据传入的参数,动态地决定创建出哪一个产品类的实例。
应用场景:工厂方式创建类的实例,多与proxy模式配合,创建可替换代理类。
优势:易于替换,面向抽象编程,application只与抽象工厂和易变类的共性抽象类发生调用关系。
敏捷原则:DIP依赖倒置原则
实例:项目部署环境中依赖多个不同类型的数据库时,需要使用工厂配合proxy完成易用性替换
注意事项:项目初期,软件结构和需求都没有稳定下来时,不建议使用此模式,因为其劣势也很明显,
增 加了代码的复杂度,增加了调用层次,增加了内存负担。
所以要注意防止模式的滥用。
优点:简单工厂模式的优点是客户端可以直接消费产品,而不必关心具体产品的实现,
消除了客户端直接创建产品对象的责任,实现了对责任的分割。
缺点:是工厂类集中了所有产品的创建逻辑,一旦不能正常工作,整个系统都会受到影响,
而且当产品类多结构复杂的时候,把所有创建工作放进一个工厂中来,回事后期程序的扩展较为困难。
通过优缺点的分析,我们可以再如下场景中使用简单工厂模式:
(1)工厂类负责创建的对象较少时;
(2)客户端只知道传入工厂类的参数,对于如何创建对象的逻辑不必关心时。
简单工厂模式示例代码下载地址,
1、简述
首先需要说明一下,简单工厂模式不属于23种GOF设计模式之一。
它也称作静态工作方法模式,是工厂方法模式的特殊实现(也就是说工厂模式包含简单工厂模式)。
这里对简单工厂模式进行介绍,是为后面的工厂方法和抽象工厂模式做一个引子。
2、定义
“专门定义一个类来负责创建其他类的实例,被创建的实例通常具有共同的父类。”
世界上就是由一个工厂类,根据传入的参数,动态地决定创建出哪一个产品类的实例。
一个新手,极有可能按照自己的初步思维逻辑,判断用户输入的运算符,然后将两个数字进行运算,
当然还会加上必要的除数不为0的判断,那么点击运算Button,对应的事件可以如下面这样编写,
恩,这样写肯定能够实现功能。但是如果进行更多的运算,例如增加开平方、乘方运算,增加100种运算,
那么是不是要增加100个else if判断语句呢?
如果这样去做每次都要去修改这部分代码,这样有悖于可扩展性原则。
所以我们需要引入简单工厂模式,把运算给抽象出来,并且加入运算工厂用于接收用户的操作。
注释:这里我们把运算这个动作给抽象出来,当做一个对象,可能很多人觉得有点迷糊。
我们知道,面向对象编程是不同于面向过程编程的,通常将一个事物给抽象成一个类,类具有属性和方法;
那么我们也可以把一个动作进行抽象,例如此处的运算Operation,它具有两个属性
(前一个操作数和后一个操作数),它具有的方法就是获取运算的结果。
所以深入理解面向对象编程,还有很多的路要走。
这样的话ViewController中的代码看起来就简洁明了,而且易于扩展。
那么我们根据ViewController中的代码,分析一下使用思路,把操作类类比成一个容器,它有输入端
(操作符号、第一个操作数、第二个操作数)和输出端(运算结果),如下图所示,
所以上面的代码将ViewController的TextField中的输入内容拿过来创建操作对象,并且把操作运算的逻辑放在了Operation及其子类中实现,然后将结果返回给ViewController,这样减少了ViewController的逻辑代码。
通过简单工厂模式的重构,我们就是闲了低耦合度的代码结构,做到了对外扩展开放,对修改关闭。
如果再增加任何的操作方法,只需要继承操作方法父类,新建一个操作子类,并且在简单工厂类里面多添加一个else if的判断即可。
五、优缺点
优点:简单工厂模式的优点是客户端可以直接消费产品,而不必关心具体产品的实现,
消除了客户端直接创建产品对象的责任,实现了对责任的分割。
缺点:是工厂类几种了所有产品的创建逻辑,一旦不能正常工作,整个系统都会受到影响,
而且当产品类多结构复杂的时候,把所有创建工作放进一个工厂中来,回事后期程序的扩展较为困难。
通过优缺点的分析,我们可以再如下场景中使用简单工厂模式:
(1)工厂类负责创建的对象较少时;
(2)客户端只知道传入工厂类的参数,对于如何创建对象的逻辑不必关心时。
MVC是一个框架模式 Model View Controller 模型--视图--控制器 各自处理自己的任务
M是指数据模型 V是指用户界面 C是指控制器
使用MVC的目的是将M和V的实现代码分离 C存在的目的则是确保M和V的同步
耦合性低 重用性高 生命周期成本低
最典型的MVC就是JSP + servlet + javabean的模式
6、熟练使用Http协议在开发中作网络请求,熟练掌握JSON/XML的解析方式,熟练使用相关第三方的网络库,如AFNetWorking、SDWebImage并理解其运行原理。AFNetworking2.0
1.看看AFURLConnectionOperation,AFURLConnectionOperation继承自NSOperation,
是一个封装好的任务单元,在这里构建了NSURLConnection,
作为NSURLConnection的delegate处理请求回调,做好状态切换,线程管理,
可以说是AFNetworking最核心的类
2.为保证线程安全,所有单例都用dispatch_once生成,保证只执行一次,用GCD的单例实现
3.常看到一个block要使用self,会处理成在外部声明一个weak变量指向self,在block里又声明一个strong变量指向 weakSelf:
1 2 3 4 | __weak __typeof(self)weakSelf = self; self.backgroundTaskIdentifier = [application beginBackgroundTaskWithExpirationHandler:^{ __strong __typeof(weakSelf)strongSelf = weakSelf; }]; |
weakSelf是为了block不持有self,避免循环引用,而再声明一个strongSelf是因为一旦进入block执行,
就不允许self在这个执行过程中释放。block执行完后这个strongSelf会自动释放,没有循环引用问题。
4.先来看看NSURLConnection发送请求时的线程情况,NSURLConnection是被设计成异步发送的,
调用了start方法后,NSURLConnection会新建一些线程用底层的CFSocket去发送和接收请求,
在发送和接收的一些事件发生后通知原来线程的Runloop去回调事件,
NSURLConnection的同步方法sendSynchronousRequest方法也是基于异步的,
同样要在其他线程去处理请求的发送和接收,只是同步方法会手动block住线程,发送状态的通知也不是通过 RunLoop进行。
5.若要在主线程使用NSURLConnection异步接口,需要手动把RunloopMode设为NSRunLoopCommonModes。
这个mode意思是无论当前Runloop处于什么状态,都执行这个任务。
1 2 3 | NSURLConnection *connection = [[NSURLConnection alloc] initWithRequest:request delegate:self startImmediately:NO]; [connection scheduleInRunLoop:[NSRunLoop currentRunLoop] forMode:NSRunLoopCommonModes]; [connection start]; |
原因:当在主线程调用[[NSURLConnection alloc] initWithRequest:request delegate:self startImmediately:YES]时,请求发出,侦听任务会加入到主线程的Runloop下,RunloopMode会默认为NSDefaultRunLoopMode。这表明只有当前线程的Runloop处于NSDefaultRunLoopMode时,这个任务才会被执行。但当用户滚动tableview或scrollview时,主线程的Runloop是处于NSEventTrackingRunLoopMode模式下的,不会执行NSDefaultRunLoopMode的任务,所以会出现一个问题,请求发出后,如果用户一直在操作UI上下滑动屏幕,那在滑动结束前是不会执行回调函数的,只有在滑动结束,RunloopMode切回NSDefaultRunLoopMode,才会执行回调函数。
4.AFnetworking在子线程中调用异步接口,实际上多个NSURLConnection会共用一个NSURLConnectionLoader线程
5.继承NSOperation有个很麻烦的东西要处理,就是改变状态时需要发KVO通知,否则这个类加入NSOperationQueue不可用了。NSOperationQueue是用KVO方式侦听NSOperation状态的改变,以判断这个任务当前是否已完成,完成的任务需要在队列中除去并释放
6.AFURLConnectionOperation有一把递归锁,在所有会访问/修改成员变量的对外接口都加了锁,因为这些对外的接口用户是可以在任意线程调用的,对于访问和修改成员变量的接口,必须用锁保证线程安全。
SDWebImage 图⽚片缓存原理?
1.沙盒的liabrary 文件夹下创建”image”文件夹
2.保存显⽰示过的图⽚
把图⽚片本地路径保存到数据库中.
3.再次请求url时,它会去沙盒中”image⽂文件夹”找到对应的图⽚片显⽰示
好处:节约流量,节约资源,⽤用户体验好,节省同样文件第⼆次下载.
内管管理的⻩黄⾦金法则?
需要引⼊入⾃自动计数器这个概念. 凡是⽤用alloc,copy,retain,new创建的对象,计数器都需要+1,
release会使引⽤用计数器减1. 当引⽤用计数器减为0的时候会调⽤用dealloc⽅方法,销毁内存.
block原理?
block是匿名函数,它是通过闭包传值,它有三种类型,堆block,栈block, 全局block.
在arc 上是堆block在mrc(⼿手动)是栈block 引⽤用外部变量的时候是全局Block.
⼀一般⽤用于传 值,和回调函数.
苹果⾥里⾯面有多少种缓存机制? 沙盒,plist,userDefult ,数据库
怎么清除?
数据库 清除表对应的字段.
沙盒 清除⽂文件
userDefult ⽤用kVC把⾥里⾯面的值置为空
plist 找到沙盒⾥面的⽂件,读取修改再写⼊入归档 归⼀些⼆进制出来,保存⼆二进制⽂文件
8、熟悉OC中的Run Loop机制。
理解Run Loop的启动、运行、结束的过程。
熟悉IOS开发中的Run Loop模式以及注册到相应模式下的观察者。
1.Runloop是事件接收和分发机制的一个实现。
Runloop提供了一种异步执行代码的机制,不能并行执行任务。
2.在主队列中,Main RunLoop直接配合任务的执行,负责处理UI事件、定时器以及其他内核相关事件。
RunLoop的主要目的:保证程序执行的线程不会被系统终止。
3.自己创建的线程: RunLoop默认不开启,需要我们手动开启
主线程,自带RunLoop,默认开启
其他非自己创建的子线程,里面都加了[[NSRunLoop currentRunLoop] runMode:NSDefaultRunLoopMode beforeDate:[NSDate distantFuture]]方法;
滚动试图和异步加载共存
[connection scheduleInRunLoop:[NSRunLoop currentRunLoop] forMode:NSRunLoopCommonModes]; 默认的事 NSDefaultRunLoopMode,需要共同存在必须改为NSRunLoopCommonModes
9.熟悉OC的运行时机制及反射机制。
熟悉元类、类、对象、方法、属性、成员的元数据结构,熟悉这些基本数据结构在运行时的使用,
熟悉运行时的消息处理、动态绑定、消息转发机制。
了解运行时的类型编码及方法编码。
运行机制:runtime机制
1.基于C语言的一个封装 让C语言具备面向对象的能力(只有程序在运行时才能确定对象调用哪个方法)
开源 OC编译时很多方法都会转化为Runtime对应的方法执行,
而Objective-C runtime就是通过“id objc_msgSend(id theReceiver, SEL theSelector, ...)
”这个函数来调用方法的。其中theReceiver是调用对象,theSelector则是消息名,
省略号就是C语言的不定参数了。
这里的消息名是SEL类型,它被定义为struct objc_selector *。
不过文档中并没有透露objc_selector是什么东西,
但提供了@selector指令来生成:SEL selector = @selector(message);
2. 相关应用
- NSCoding(归档和解档, 利用runtime遍历模型对象的所有属性)
- 字典 –> 模型 (利用runtime遍历模型对象的所有属性,
- 根据属性名从字典中取出对应的值, 设置到模型的属性上)
- KVO(利用runtime动态产生一个类)
- 用于封装框架(想怎么改就怎么改)
- 这就是我们runtime机制的只要运用方向
3.相关函数
- objc_msgSend : 给对象发送消息
- class_copyMethodList : 遍历某个类所有的方法
- class_copyIvarList : 遍历某个类所有的成员变量
- class_…..
3.runtime实现的机制是什么,怎么用,一般用于干嘛.
你还能记得你所使用的相关的头文件或者某些方法的名称吗?
运行时机制,runtime库里面包含了跟类、成员变量、方法相关的API,
比如获取类里面的所有成员变量,为类动态添加成员变量,动态改变类的方法实现,
为类动态添加新的方法等 需要导入<objc/message.h><objc/runtime.h>
1.是什么
1> runtime是一套比较底层的纯C语言API, 属于1个C语言库, 包含了很多底层的C语言API
2> 平时编写的OC代码, 在程序运行过程中, 其实最终都是转成了runtime的C语言代码,
runtime算是OC的幕后工作者
3> 举例:
OC :
[[MJPerson alloc] init]
runtime :
objc_msgSend(objc_msgSend("MJPerson" , "alloc"), "init")
2.用过么? 怎么用?
1> runtime是属于OC的底层, 可以进行一些非常底层的操作(用OC是无法现实的, 不好实现)
* 在程序运行过程中, 动态创建一个类(比如KVO的底层实现)
* 在程序运行过程中, 动态地为某个类添加属性\方法, 修改属性值\方法
* 遍历一个类的所有成员变量(属性)\所有方法
3.相关的头文件和函数
1> 头文件
* <objc/runtime.h>
* <objc/message.h>
2> 相关应用
* NSCoding(归档和解档, 利用runtime遍历模型对象的所有属性)
* 字典 --> 模型 (利用runtime遍历模型对象的所有属性, 根据属性名从字典中取出对应的值, 设置到模型的属性上)
* KVO(利用runtime动态产生一个类)
* 用于封装框架(想怎么改就怎么改)
3> 相关函数
* objc_msgSend : 给对象发送消息
* class_copyMethodList : 遍历某个类所有的方法
* class_copyIvarList : 遍历某个类所有的成员变量
* class_.....
4.必备常识
1> Ivar : 成员变量
2> Method : 成员方法
/------------------------华丽的分割线--------------------------/
3.Foundation对象与Core Foundation对象有什么区别
1> Foundation对象是OC的,Core Foundation对象是C对象
2> 数据类型之间的转换
ARC:__bridge_retained、__bridge_transfer
非ARC: __bridge
反射机制:1. 获取类名 :
返回:NString [NSString stringWithUTF8String:object_getClassName(model)]
反射机制函数:object_getClassName()
2.NSClassFromString方法来使用字符串获得类
动态绑定
—在运行时确定要调用的方法
动态绑定将调用方法的确定也推迟到运行时。
在编译时,方法的调用并不和代码绑定在一起,只有在消实发送出来之后,才确定被调用的代码。
通过动态类型和动态绑 定技术,您的代码每次执行都可以得到不同的结果。
运行时因子负责确定消息的接收者和被调用的方法。
运行时的消息分发机制为动态绑定提供支持。
当您向一个动 态类型确定了的对象发送消息时,运行环境系统会通过接收者的isa指针定位对象的类,
并以此为起点确定被调用的方法,方法和消息是动态绑定的。
而且,您不 必在Objective-C 代码中做任何工作,就可以自动获取动态绑定的好处。您在每次发送消息时,
特别是当消息的接收者是动态类型已经确定的对象时,动态绑定就会例行而透明地发生。
消息转发:我们通常使用@selector来消息转发,selector就是一种类型的SEL,代表你要发送的消息(方法),跟字符串有点像,也可以互转 NSSelectorFromString() / NSSelectorFromString(),也可以理解为类似函数指针的东西,是让Objective-C动态调用方法的玩意,时oc的动态绑定技术,可以通过字符串访问函数指针,其实就是消息响应函数--选一个消息响应的函数地址给你的action,@selector(function_name) 即取得一个function的id
如何实现消息转发:
1.实现方法签名
首先,我在ZWRootViewController.h文件中定义了一个print方法,但是并没有实现这个方法,我想把控制器的实例所接到的消息由ZWobj类来处理,在ZWobj中我声明并实现了print方法
要实现消息的转发第一步必须要先构造方法签名,在ZWRootViewController里面重载- ( NSMethodSignature *)methodSignatureForSelector:( SEL )aSelector方法,该方法会返回一个方法签名供ZWRootViewController实例来找到ZWobj中print方法(在运行时动态绑定,oc特性之一呵呵)
- (NSMethodSignature *)methodSignatureForSelector:(SEL)aSelector
{
NSMethodSignature *signature = [super methodSignatureForSelector:aSelector];
if (!signature)
{
//首先判断ZWobj的实例是否有能力回应此selector
if ([ZWobj instancesRespondToSelector:aSelector]) {
//获取ZWobj的selector的方法签名对象
signature=[ZWobj instanceMethodSignatureForSelector:aSelector];
}
}
return signature;
}
进行消息转发
- (void)forwardInvocation:(NSInvocation *)invocation
{
NSLog(@"invocation==[%@]",invocation);
if ([ZWobj instancesRespondToSelector:[invocation selector]])
{
//创建要移交消息响应权的实例obj
ZWobj *obj = [ZWobj new];
//激活invocation中的消息,但是消息的响应者是ZWobj,而不是默认的self。
[invocation invokeWithTarget:obj];
}
}
11、熟悉socket通信及TCP/UDP协议。了解XMPP即时通讯协议。
socket: Socket的英文原义是“孔”或“插座”。作为BSD UNIX的进程通信机制。通常也称作“套接字”,
用于描述IP地址和端口,是一个通信链的句柄。
Socket是面向C/S(客户端/服务端)模型而设计的,针对客户端和服务端提供不同的Socket。
客户端有客户端的Socket, 服务端有服务端的Socket;
Socket的常用类型有:流式Socket(基于TCP协议)和 数据报式Socket(基于UDP协议);
Socket:实现TCP UDP协议的接口 长连接
HTTP:基于TCP实现 面向应用层面的超文本传输协议 短连接
TCP与UDP区别
面向连接 面向非连接
TCP---传输控制协议,提供的是面向连接、可靠的字节流服务。
当客户和服务器彼此交换数据前,必须先在双方之间建立一个TCP连接,之后才能传输数据。
TCP提供超时重发,丢弃重复数据,检验数据,流量控制等功能,保证数据能从一端传到另一端。
UDP---用户数据报协议,是一个简单的面向数据报的运输层协议。
UDP不提供可靠性,它只是把应用程序传给IP层的数据报发送出去,但是并不能保证它们能到达目的地。
由于UDP在传输数据报前不用在客户和服务器之间建立一个连接,且没有超时重发等机制,故而传输速度很快
TCP(Transmission Control Protocol,传输控制协议)是基于连接的协议,也就是说,在正式收发数据前,必须和对方建立可靠的连接。
一个TCP连接必须要经过三次“对话”才能建立起来,我们来看看这三次对话的简单过程:
1.主机A向主机B发出连接请求数据包;
2.主机B向主机A发送同意连接和要求同步(同步就是两台主机一个在发送,一个在接收,协调工作)的数据包;
3.主机A再发出一个数据包确认主机B的要求同步:“我现在就发,你接着吧!”,这是第三次对话。
三次“对话”的目的是使数据包的发送和接收同步,经过三次“对话”之后,主机A才向主机B正式发送数据。
UDP(User Data Protocol,用户数据报协议)是与TCP相对应的协议。
它是面向非连接的协议,它不与对方建立连接,而是直接就把数据包发送过去!
UDP适用于一次只传送少量数据、对可靠性要求不高的应用环境。
tcp协议 和 udp协议的差别
是否连接 面向连接 面向非连接
传输可靠性 可靠 不可靠
应用场合 传输大量数据 少量数据
速度 慢 快
3.TCP三次握手
1.客户端向服务端发起连接请求数据包
2.服务端返回客户端 同意连接 要求同步数据包
3.客户端向服务端发起同步请求数据包
连接建立 数据传输…
socket连接和http连接的区别
网页都是http协议传输到浏览器, 而http是基于socket之上的。
socket是一套完成tcp,udp协议的接口。
HTTP协议:简单对象访问协议,对应于应用层 ,HTTP协议是基于TCP连接的
tcp协议: 对应于传输层
ip协议: 对应于网络层 TCP/IP是传输层协议,主要解决数据如何在网络中传输;
而HTTP是应用层协议,主要解决如何包装数据。
Socket是对TCP/IP协议的封装,Socket本身并不是协议,而是一个调用接口(API),通过Socket,
我们才能使用TCP/IP协议。
http连接:http连接就是所谓的短连接,即客户端向服务器端发送一次请求,服务器端响应后连接即会断掉;
socket连接:socket连接就是所谓的长连接,理论上客户端和服务器端一旦建立起连接将不会主动断掉
TCP(Transmission Control Protocol,传输控制协议)是基于连接的协议,也就是说,
在正式收发数据前,必须和对方建立可靠的连接。
一个TCP连接必须要经过三次“对话”才能建立起来,我们来看看这三次对话的简单过程:
1.主机A向主机B发出连接请求数据包;
2.主机B向主机A发送同意连接和要求同步
(同步就是两台主机一个在发送,一个在接收,协调工作)的数据包;
3.主机A再发出一个数据包确认主机B的要求同步:“我现在就发,你接着吧!”,这是第三次对话。
三次“对话”的目的是使数据包的发送和接收同步,经过三次“对话”之后,主机A才向主机B正式发送数据。
UDP(User Data Protocol,用户数据报协议)是与TCP相对应的协议。
它是面向非连接的协议,它不与对方建立连接,而是直接就把数据包发送过去!
UDP适用于一次只传送少量数据、对可靠性要求不高的应用环境。
tcp协议 和 udp协议的差别
是否连接 面向连接 面向非连接
传输可靠性 可靠 不可靠
应用场合 传输少量数据 大量数据
速度 慢 快
XMPP(可扩展消息处理现场协议)是基于可扩展标记语言(XML)的协议,
它用于即时消息(IM)以及在线现场探测。
它在促进服务器之间的准即时操作。
这个协议可能最终允许因特网用户向因特网上的其他任何人发送即时消息,即使其操作系统和浏览器不同。
XMPP的前身是Jabber,一个开源形式组织产生的网络即时通信协议。
XMPP目前被IETF国际标准组织完成了标准化工作。
标准化的核心结果分为两部分;
1.XMPP: XMPP是一种基于XML的协议,它继承了在XML环境中灵活的发展性。
因此,基于XMPP的应用具有超强的可扩展性
2.XMPP优点:开放—XMPP协议是自由、开放、公开的,并且易于了解,
标准,证实可用,分布式,安全,可扩展,弹性佳,多样性
缺点:数据负载太重,没有二进制数据
XMPPJID XMPP抽象出一个在互联网上唯一的对象实体,用 JID 来表达,基于JID建立一个TCP长连接。
3.步骤:
1.与XMPP服务器建立连接
[_xmppSteam setMyJID:@“用户名”]; 给XMPP流设置账号
[_xmppSteam connectWithTimeout:-1 error:&error]
2.连接成功后注册
[_xmppSteam registerWithPassword:@“密码” error:nil]
3.连接成功后登陆
[_xmppSteam authenticateWithPassword:@“密码” error:nil]
4.上线
XMPPPresence * presence = [XMPPPresence presence];
[_xmppSteam sendElement:presence];
5.下线
XMPPPresence * presence = [XMPPPresence presenceWithType:@"unavailable"];
[_xmppSteam sendElement:presence];
6.添加好友
[_xmppRoster subscribePresenceToUser:jid];
7.删除好友
[_xmppRoster removeUser:jid]
8.获取好友
<iq type="get"
from="maozhi@127.0.0.1”
to=“127.0.0.1”
id=“1000”>
<query xmlns="jabber:iq:roster"/>
<iq />
type:向服务器端请求信息
from:消息来源
to 服务器域名
id 标记请求 ID
<query xmlns="jabber:iq:roster"/> 客户端需要查询 roster
NSXMLElement *query = [NSXMLElement elementWithName:@"query" xmlns:@"jabber:iq:roster"];
NSXMLElement *iq = [NSXMLElement elementWithName:@"iq"];
[iq addAttributeWithName:@"from" stringValue:[NSString stringWithFormat:@"%@",_xmppSteam.myJID]];
[iq addAttributeWithName:@"to" stringValue:myJID.domain];
[iq addAttributeWithName:@"id" stringValue:@"10000"];
[iq addAttributeWithName:@"type" stringValue:@"get"];
[iq addChild:query];
[_xmppSteam sendElement:iq];
9.好友列表XML解析
NSXMLElement * query = iq.childElement;
for (NSXMLElement * oneChild in query.children) {
NSString * jid = [oneChild attributeStringValueForName:@"jid"];
}
10.发送消息
<message type="chat" to="xiaoming@example.com">
<body>Hello World!<body />
<message />
NSXMLElement *body = [NSXMLElement elementWithName:@"body"];
[body setStringValue:msg];
NSXMLElement *message = [NSXMLElement elementWithName:@"message"];
[message addAttributeWithName:@"type" stringValue:@"chat"];
[message addAttributeWithName:@"to" stringValue:user];
[message addChild:body];
[_xmppSteam sendElement:message];
11.接受消息解析
[[message elementsForName:@"body"][0] stringValue];
◆10、熟悉IOS中的Block机制。
理解Block的闭包性原理,
熟悉Block的存储结构,
能比较深入的理解Block的对象特性,
熟悉Block的回调及参数特性。
block是匿名函数,它是通过闭包传值,它有三种类型,堆block,栈block, 全局block.
在arc 上是堆block在mrc(⼿手动)是栈block 引⽤用外部变量的时候是全局Block.
⼀一般⽤用于传 值,和回调函数
12、熟练使用常用的数据持久化方式。
如基于属性列表plist的文件的数据存储,
归档与反归档的数据存储及数据使用,
基于sqlite数据库的基本创建及增删改差等操作,
基于对象关系映射技术(ORM)的Core Data使用。
◆13、熟悉第三方登录、分享、推送、统计在应用中的集成。
熟悉支付宝支付、微信支付在开发中的集成与应用。
支付宝流程
1.做过,首先下载支付宝的sdk,根据文档集成。
2.然后让后台给我私钥,还有服务器的通知地址,订单号等,app拼接字符串
3.发送给支付宝SDK,SDK再发送请求到支付宝服务器,
支付宝服务器处理支付后,返回结果给我们的服务器和app里面集成的sdk,
sdk再回调到我的app上,我这边再判断是否成功,最后提示给用户。
4.以前后台也不会做支付宝,然后让我做。
我说,私钥必须要在服务端生成的,后台听我说完,最后自己搞定给私钥我。
支付宝流程:1.使用支付宝付款
2.调用支付接口
3.支付请求
4.支付完成(移动快捷服务端)
5.异步发送支付通知(外部商品服务端)
6.返回支付结果
7.接口返回支付结果
8.显示交易结果
13、熟悉第三方登录、分享、推送、统计在应用中的集成。
熟悉支付宝支付、微信支付在开发中的集成与应用
14.推送
1.iphone向APNS索取DeviceToken
2.APNS返回iphone对应的Token
3.iphone将TOken上传至应用程序对应的服务器
4.应用程序对应的服务器将Token和需要推送的信息上传至APNS
5.APNS讲消息PUSH至iphone
客户端要做的
1.索要token
2.上传Token
3.推送证书由客户端开发人员提供
4.接受并且定制显示推送消息的UI界面
◆15、熟练Cocoapod库版本控制的使用, 熟练使用SVN、GitHub作库版本控制
◆16、熟悉APP上架的基本流程
1.创建发布正式
2。创建证书发布的类型App Store and Ad Hoc
3.选择证书的配置文件
4.选择appid
5.下载证书
6.项目里面选择相应的发布证书
7. iOS device 才会出现archive
7.点击archive,项目自己打包
8.点击submit的时候提示我们iturns上面没有该应用的信息
9.进入iTunes里面为我准备上架的应用填写信息
10.跳转到 https://itunesconnect.apple.com/WebObjects/iTunesConnect.woa 创建应用
11.创建成功后,在xcode上面去提交应用
12. 地图
一. MapKit
1> 使用须知
* MapKit框架中所有数据类型的前缀都是MK
* MapKit有一个比较重要的UI控件 :MKMapView,专门用于地图显示
2> 使用步骤:
// 1.全局变量地图显示
@property (weak, nonatomic) IBOutlet MKMapView *mapView;
// 2.设置地图的类型
/** 可以通过设置MKMapView的mapViewType设置地图类型
* MKMapTypeStandard :普通地图(左图)
* MKMapTypeSatellite :卫星云图 (中图)
* MKMapTypeHybrid :普通地图覆盖于卫星云图之上(右图)*/
self.mapView.mapType = MKMapTypeStandard;
// 3.设置跟踪模式 (MKUserTrackingModeFollow == 跟踪)
/** 跟踪显示用户的当前位置
* MKUserTrackingModeNone :不跟踪用户的位置
* MKUserTrackingModeFollow :跟踪并在地图上显示用户的当前位置
* MKUserTrackingModeFollowWithHeading :跟踪并在地图上显示用户的当前位置,地图会跟随用户的前进方向进行旋转 */
self.mapView.userTrackingMode = MKUserTrackingModeFollow;
// 4.设置代理 (监控地图的相关行为:比如显示的区域发生了改变)
self.mapView.delegate = self;
3> MKMapViewDelegate的常用代理代理方法
* MKMapView可以设置一个代理对象,用来监听地图的相关行为
* 常见的代理方法有
// 调用非常频繁,不断监测用户的当前位置。
每次调用,都会把用户的最新位置(userLocation参数)传进来
- (void)mapView:(MKMapView *)mapView didUpdateUserLocation:(MKUserLocation *)userLocation;
// 地图的显示区域已经发生改变的时候调用
- (void)mapView:(MKMapView *)mapView regionDidChangeAnimated:(BOOL)animated;
// 地图的显示区域即将发生改变的时候调用
- (void)mapView:(MKMapView *)mapView regionWillChangeAnimated:(BOOL)animated;
4> MKUserLocation
* 大头针模型数据,对大头针位置的一个封装 (这里的userLocation描述的是显示用户位置的蓝色大头针)
@property (nonatomic, copy) NSString *title;
// 显示在大头针上的标题
@property (nonatomic, copy) NSString *subtitle;
// 显示在大头针上的子标题
@property (readonly, nonatomic) CLLocation *location;
// 地理位置信息(大头针钉在什么地方?)
5> 设置地图的显示
* 通过MKMapView的下列方法,可以设置地图显示的位置和区域
@property (nonatomic) CLLocationCoordinate2D centerCoordinate;
// 设置地图的中心点位置
- (void)setCenterCoordinate:(CLLocationCoordinate2D)coordinate animated:(BOOL)animated;
@property (nonatomic) MKCoordinateRegion region;
// 设置地图的显示区域
- (void)setRegion:(MKCoordinateRegion)region animated:(BOOL)animated;
6> 总结
* CLLocation : 封装位置信息 (经纬度、海拔)
* CLPlacemark : 封装地标信息 (位置信息location、地名name、国家country)
* MKUserlocation : 封装地图上大头针位置信息 (位置信息location、标题title、子标题subtitle)
* CLLocationDegrees : 度数 (经度、纬度)
* CLLocationCoodinate2D : 地理坐标 (经度CLLocationDegrees longitude、纬度CLLocationDegrees latitude)
* MKCoordinateSpan : 跨度 (经度跨度CLLocationDegrees longitudeDelta、纬度跨度CLLocationDegrees latitudeDelta)
* MKCoordinateRegion : 区域 (中心位置CLLocationCoodinate2D center、区域跨度MKCoordinateSpan span)
二. 大头针
1> 基本操作
- (void)addAnnotation:(id <MKAnnotation>)annotation;
// 添加一个大头针
- (void)addAnnotations:(NSArray *)annotations;
// 添加多个大头针
- (void)removeAnnotation:(id <MKAnnotation>)annotation;
// 移除一个大头针
- (void)removeAnnotations:(NSArray *)annotations;
// 移除多个大头针
* (id <MKAnnotation>)annotation参数是什么东西?
大头针模型对象:用来封装大头针的数据,比如大头针的位置、标题、子标题等数据
2> 大头针模型
#import <Foundation/Foundation.h>
#import <MapKit/MapKit.h>
@interface HFXAnnotation : NSObject <MKAnnotation>
@property (nonatomic, assign) CLLocationCoordinate2D coordinate;
// 坐标位置
@property (nonatomic, copy) NSString *title;
// 标题
@property (nonatomic, copy) NSString *subtitle;
// 子标题
@end
3> 添加大头针
// 1.全局变量地图显示
@property (weak, nonatomic) IBOutlet MKMapView *mapView;
// 2.创建大头针模型
HFXAnnotation *anno = [[HFXAnnotation alloc] init];
anno.title = @"传智播客iOS学院";
anno.subtitle = @"全部课程15折,会员20折,老学员30折";
anno.coordinate = CLLocationCoordinate2DMake(40, 116);
// 3.添加大头针
[self.mapView addAnnotation:anno];
4> 自定义大头针
// 1.设置MKMapView的代理
[self.mapView addAnnotation:anno];
// 2.实现下面的代理方法,返回大头针控件
// 根据传进来的(id <MKAnnotation>)annotation参数创建并返回对应的大头针控件
// 代理方法的使用注意一下2点:
1.如果返回nil,显示出来的大头针就采取系统的默认样式
2.标识用户位置的蓝色发光圆点,它也是一个大头针,当显示这个大头针时,也会调用代理方法
// 因此,需要在代理方法中分清楚(id <MKAnnotation>)annotation参数代表自定义的大头针还是蓝色发光圆点
- (MKAnnotationView *)mapView:(MKMapView *)mapView viewForAnnotation:(id<MKAnnotation>)annotation {
// 1) 判断annotation的类型
if (![annotation isKindOfClass:[HFXAnnotation class]]) return nil;
// 2) 先从缓存池中取出可以循环利用的大头针控件
static NSString *ID = @"tuangou";
MKAnnotationView *annoView = [mapView dequeueReusableAnnotationViewWithIdentifier:ID];
// 3) 缓存池中没有可以循环利用的大头针
if (annoView == nil) {
annoView = [[MKAnnotationView alloc] initWithAnnotation:annotation reuseIdentifier:ID]; // 传人循环利用标识来创建大头针控件
annoView.pinColor = MKPinAnnotationColorPurple; // 设置大头针的颜色
annoView.animatesDrop = YES; // 从天而降
annoView.canShowCallout = YES; // 显示标题和子标题
annoView.calloutOffset = CGPointMake(10, 10); // 设置偏差
annoView.rightCalloutAccessoryView = [UIButton buttonWithType:UIButtonTypeContactAdd]; // 添加右控件
annoView.leftCalloutAccessoryView = [UIButton buttonWithType:UIButtonTypeInfoDark]; // 添加左控件
[annoView addSubview:[UIButton buttonWithType:UIButtonTypeContactAdd]]; // 往大头针里面添加一个按钮 (测试)
}
// 4) 传递模型 (更新大头针数据,覆盖掉之前的旧数据)
annoView.annotation = annotation;
// 5) 设置图片
annoView.image = [UIImage imageNamed:annotation.icon];
return annoView;
}
5> MKAnnotationView:地图上的大头针控件是MKAnnotationView
* 常见属性:
@property (nonatomic, strong) id <MKAnnotation> annotation;
// 大头针模型
@property (nonatomic, strong) UIImage *image;
// 显示的图片
@property (nonatomic) BOOL canShowCallout;
// 是否显示标注
@property (nonatomic) CGPoint calloutOffset;
// 标注的偏移量
@property (strong, nonatomic) UIView *rightCalloutAccessoryView;
// 标注右边显示什么控件
@property (strong, nonatomic) UIView *leftCalloutAccessoryView;
// 标注左边显示什么控件
6> MKPinAnnotationView:MKPinAnnotationView是MKAnnotationView的子类
* MKPinAnnotationView比MKAnnotationView多了2个属性
@property (nonatomic) MKPinAnnotationColor pinColor;
// 大头针颜色
@property (nonatomic) BOOL animatesDrop;
// 大头针第一次显示时是否从天而降
7> 高度自定义大头针上面的视图控件
// 当点击大头针的时候
- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event {
// 需要懒加载
UIButton *redView = [[UIButton alloc] init];
[redView addTarget:self action:@selector(redClick) forControlEvents:UIControlEventTouchUpInside];
redView.backgroundColor = [UIColor redColor];
redView.width = 20;
redView.height = 20;
redView.y = - redView.height;
redView.x = (self.width - redView.width) * 0.5;
[self addSubview:redView];
self.redView = redView;
}
// 不管用户点击手机的哪里都会调用这个方法,返回一个响应事件的View
- (UIView *)hitTest:(CGPoint)point withEvent:(UIEvent *)event {
if (CGRectContainsPoint(self.redView.frame, point)) {
return self.redView;
}
return [super hitTest:point withEvent:event];
}
一. CLLocationManager
1> 框架:
* Map Kit :用于地图展示
* Core Location :用于地理定位
2> 使用须知
* CoreLocation框架中所有数据类型的前缀都是CL
* CoreLocation中使用CLLocationManager对象来做用户定位
3> 使用步骤:
// 1.必须使用全局变量
@property (nonatomic, strong) CLLocationManager *locMgr;
// 2.懒加载定位的管理对象
- (CLLocationManager *)locMgr {
if (!_locMgr) {
self.locMgr = [[CLLocationManager alloc] init];
// 1.创建位置管理者 (定位用户位置)
self.locMgr.delegate = self;
// 2.设置代理
}
return _locMgr;
}
// 3.判断系统的版本
if ( [[[UIDevice currentDevice] systemVersion] doubleValue] > 8.0 ) {
// 设置定位权限 仅ios8有意义
[self.locMgr requestWhenInUseAuthorization]; // 前台定位
// [locationManager requestAlwaysAuthorization]; // 前后台同时定位
}
// 4.开始用户定位
if ( [CLLocationManager locationServicesEnabled] ) {
// 开始定位用户的位置
[self.locMgr startUpdatingLocation];
// self.locMgr.distanceFilter = kCLDistanceFilterNone;
// 每隔多少米定位一次
// self.locMgr.desiredAccuracy = kCLLocationAccuracyHundredMeters;
// 定位精确度(越精确就越耗电)
} else { // 不能定位用户的位置
// 1.告诉用户检查网络状况
// 2.提醒用户打开定位开关
}
4> 常用的属性、方法
// 开始用户定位
- (void)startUpdatingLocation;
// 停止用户定位
- (void) stopUpdatingLocation;
// 当调用了startUpdatingLocation方法后,就开始不断地定位用户的位置,中途会频繁地调用代理的下面方法
- (void)locationManager:(CLLocationManager *)manager didUpdateLocations:(NSArray *)locations;
//locations参数里面装着CLLocation对象
* CLLocationCoordinate2D是一个用来表示经纬度的结构体,定义如下
typedef struct {
CLLocationDegrees latitude; // 纬度
CLLocationDegrees longitude; // 经度
} CLLocationCoordinate2D; // 一般用CLLocationCoordinate2DMake函数来创建CLLocationCoordinate2D
* CLLocation用来表示某个位置的地理信息,比如经纬度、海拔等等
@property(readonly, nonatomic) CLLocationCoordinate2D coordinate;
// 经纬度
@property(readonly, nonatomic) CLLocationDistance altitude;
// 海拔
@property(readonly, nonatomic) CLLocationDirection course;
// 路线,航向(取值范围是0.0° ~ 359.9°,0.0°代表真北方向)
@property(readonly, nonatomic) CLLocationSpeed speed;
// 行走速度(单位是m/s)
* 计算两个位置之间的距离
- (CLLocationDistance)distanceFromLocation:(const CLLocation *)location方法可以
5> 用户授权
* 从iOS 6开始,苹果在保护用户隐私方面做了很大的加强,以下操作都必须经过用户批准授权
1.要想获得用户的位置
2.想访问用户的通讯录、日历、相机、相册等等
* 当想访问用户的隐私信息时,系统会自动弹出一个对话框让用户授权
* 开发者可以在Info.plist中设置NSLocationUsageDescription说明定位的目的(Privacy - Location Usage Description)
* 从iOS 8以后使用到定位就要在Info.plist中设置NSLocationWhenInUseUsageDescription与NSLocationAlwaysUsageDescription才能得到授权的弹框.
二. CLGeocoder
1> 简介
* 使用CLGeocoder可以完成"地理编码"和"反地理编码"
* 地理编码:根据给定的地名,获得具体的位置信息(比如经纬度、地址的全称等)
* 反地理编码:根据给定的经纬度,获得具体的位置信息
2> 使用步骤:
// 1.使用全局变量
@property (nonatomic, strong) CLGeocoder *geocoder;
// 2.懒加载编码的管理对象
- (CLGeocoder *)geocoder {
if (!_geocoder) self.geocoder = [[CLGeocoder alloc] init];
return _geocoder;
}
// 3.进行编码
- (void)geocodeAddressString:(NSString *)addressString completionHandler:(CLGeocodeCompletionHandler)completionHandler; // 地理编码方法
- (void)reverseGeocodeLocation:(CLLocation *)location completionHandler:(CLGeocodeCompletionHandler)completionHandler; // 反地理编码方法
typedef void (^CLGeocodeCompletionHandler)(NSArray *placemarks, NSError *error); // 当地理\反地理编码完成时,就会调用CLGeocodeCompletionHandler
/** 这个block传递2个参数
* error :当编码出错时(比如编码不出具体的信息)有值
* placemarks :里面装着CLPlacemark对象 */
3> CLPlacemark对象
* CLPlacemark的字面意思是地标,封装详细的地址位置信息
@property (nonatomic, readonly) CLLocation *location; // 地理位置
@property (nonatomic, readonly) CLRegion *region; // 区域
@property (nonatomic, readonly) NSDictionary *addressDictionary; // 详细的地址信息
@property (nonatomic, readonly) NSString *name; // 地址名称
@property (nonatomic, readonly) NSString *locality; // 城市
一. 画线
1> 基本步骤:
// 1.初始化方向请求
MKDirectionsRequest *request = [[MKDirectionsRequest alloc] init];
// 方向请求
CLPlacemark *sourceCLPm; CLPlacemark *destinationCLPm;
// 通过下面的地理编码得到
self.sourceMKPm = [[MKPlacemark alloc] initWithPlacemark:sourceCLPm];
// 设置起点 MKPlacemark
request.source = [[MKMapItem alloc] initWithPlacemark:self.sourceMKPm];
// 设置起点 MKMapItem
self.destinationMKPm = [[MKPlacemark alloc] initWithPlacemark:destinationCLPm];
// 设置终点 MKPlacemark
request.destination = [[MKMapItem alloc] initWithPlacemark:self.destinationMKPm];
// 设置终点 MKMapItem
// 2.根据请求创建方向
MKDirections *directions = [[MKDirections alloc] initWithRequest:request];
// 3.执行请求
[directions calculateDirectionsWithCompletionHandler:^(MKDirectionsResponse *response, NSError *error) {
if (error) return;
for ( MKRoute *route in response.routes ) {
// 遮盖 overlay, 添加路线遮盖 (传递路线的遮盖模型数据)
[self.mapView addOverlay:route.polyline];
}
}];
2> 地理编码:
// 1.起点的地理编码
[self.geocoder geocodeAddressString:sourceAddress completionHandler:^(NSArray *placemarks, NSError *error) {
CLPlacemark *sourceCLPm = [placemarks firstObject];
// 判断有没有值
if (sourceCLPm == nil) return;
// 添加起点大头针
HFXAnnotation *sourceAnno = [[HFXAnnotation alloc] init];
sourceAnno.coordinate = sourceCLPm.location.coordinate;
sourceAnno.title = sourceAddress;
sourceAnno.subtitle = sourceCLPm.name;
[self.mapView addAnnotation:sourceAnno];
// 2.终点的地理编码
[self.geocoder geocodeAddressString:destinationAddress completionHandler:^(NSArray *placemarks, NSError *error) {
CLPlacemark *destinationCLPm = [placemarks firstObject];
if (destinationCLPm == nil) return;
// 添加终点大头针
HFXAnnotation *destinationAnno = [[HFXAnnotation alloc] init];
destinationAnno.coordinate = destinationCLPm.location.coordinate;
destinationAnno.title = destinationAddress;
destinationAnno.subtitle = destinationCLPm.name;
[self.mapView addAnnotation:destinationAnno];
// 3.画线操作
[self drawLineWithSourceCLPm:sourceCLPm destinationCLPm:destinationCLPm];
}];
}];
3> 遵守协议 MKMapViewDelegate
// 返回一层遮盖overlay
- (MKOverlayRenderer *)mapView:(MKMapView *)mapView rendererForOverlay:(id<MKOverlay>)overlay {
MKPolylineRenderer *redender = [[MKPolylineRenderer alloc] initWithOverlay:overlay];
redender.lineWidth = 5;
redender.strokeColor = [UIColor blueColor];
return redender;
}
二. 导航
1> 导航概要
* 导航功能
1.打开苹果官方的地图应用
2.在我们应用集成百度地图
* 百度里面数据众包:1.导航 2.POI搜索 (搜索周边酒店、餐馆)
2> 苹果官方的地图应用
* 开启导航
if (self.sourceMKPm == nil || self.destinationMKPm == nil) return; // 保险判断
// 1.设置起点和终点
MKMapItem *sourceItem = [[MKMapItem alloc] initWithPlacemark:self.sourceMKPm]; // 起点
MKMapItem *destinationItem = [[MKMapItem alloc] initWithPlacemark:self.destinationMKPm]; // 终点
// 2.创建存放起点和终点的数组
NSArray *items = @[sourceItem,destinationItem];
// 3.设置导航的基本参数信息
NSMutableDictionary *options = [NSMutableDictionary dictionary];
options[MKLaunchOptionsDirectionsModeKey] = MKLaunchOptionsDirectionsModeDriving; // 导航模式:驾驶导航
options[MKLaunchOptionsShowsTrafficKey] = @YES; // 是否要显示路况
// 4.打开苹果官方的导航应用
[MKMapItem openMapsWithItems:items launchOptions:options];
三. 集成百度地图
1> 申请密钥
* 参考:http://developer.baidu.com/map/sdkiosdev-1.htm
* 申请:http://lbsyun.baidu.com/apiconsole/key
2> 下载SDK
* http://developer.baidu.com/map/static/doc/BaiduMap_iOSSDK_v2.1.0_All.zip
3> 合并静态库文件
* cd /..../BaiduMap_iOSSDK_v2.1.0_All/BaiduMap_iOSSDK_v2.1.0_Lib/libs
* lipo -create Release-iphoneos/libbaidumapapi.a Release-iphonesimulator/libbaidumapapi.a -output libbaidumapapi.a
4> 添加头文件和资源
* BaiduMap_iOSSDK_v2.1.0_Lib/inc
* BaiduMap_iOSSDK_v2.1.0_Lib/mapapi.bundle
5> 添加依赖框架
CoreLocation.framework
QuartzCore.framework
OpenGLES.framework
SystemConfiguration.framework
CoreGraphics.framework
Security.framework(2.1.0开始需要)
6> 导入主头文件
#import "BMapKit.h" (至少有一个.mm文件)
四. 百度地图
1> 常用的基本使用
// 地图引擎管理类
self.mgr = [[BMKMapManager alloc] init];
[self.mgr start:@"VGqGP1w1kpHGGBDzTmp2vcLO" generalDelegate:self];
// 添加地图控件
BMKMapView *mapView = [[BMKMapView alloc] init];
mapView.frame = self.view.bounds;
[self.view addSubview:mapView];
// 搜索
self.search = [[BMKPoiSearch alloc] init];
self.search.delegate = self;
[self.search poiSearchInCity:(BMKCitySearchOption *)];
2> 代理方法
#pragma mark - BMKGeneralDelegate
- (void)onGetNetworkState:(int)iError
{
if (0 == iError) {
NSLog(@"联网成功");
} else {
NSLog(@"onGetNetworkState %d",iError);
}
}
- (void)onGetPermissionState:(int)iError {
if (0 == iError) {
NSLog(@"授权成功");
} else {
NSLog(@"onGetPermissionState %d",iError);
}
}
#pragma mark - BMKPoiSearchDelegate
- (void)onGetPoiResult:(BMKPoiSearch *)searcher result:(BMKPoiResult *)poiResult errorCode:(BMKSearchErrorCode)errorCode {
for ( BMKPoiInfo *info in poiResult.poiInfoList ) {
}
}
13.百度地图
百度地图API 最新版本是2.4.1,需要关注,不支持64位
注:静态库中采用ObjectC++实现,因此需要您保证您工程中至少有一个.mm后缀的源文件
1> 没有64位架构的支持
libbaidumapapi.a, missing required architecture x86_64
.a文件缺少64位的架构
解决办法:将Architectures修改位:$(ARCHS_STANDARD_32_BIT)
2> 如果在导入第三方框架时,发现提示"std::"提示错误,说明框架使用了C++
解决办法,随便把项目中的一个文件,扩展名.mm
.m c语言&OC混编
.mm c++语言&OC混编
.c 纯C语言
.cpp 纯C++
3> 百度地图api的特点,代理方法,通常以onXXX,表示发生了什么事件时。。。
4> 关于error的数字
0 表示正确
其他数字,表示错误代码
5> 自2.0.0起,BMKMapView新增viewWillAppear、viewWillDisappear方法来控制BMKMapView的生命周期,并且在一个时刻只能有一个BMKMapView接受回调消息
因此在使用BMKMapView的viewController中需要在viewWillAppear、viewWillDisappear方法中调用BMKMapView的对应的方法,并处理delegate,代码如下
6> POI检索:周边检索、区域检索和城市内检索
苹果原生地图框架不支持的功能
应用程序互相跳转
1. 应用场景
1) 使用第三方用户登录,需要用户授权,还需要"返回到调用的程序,同时返回授权的用户名"
2) 应用程序推广,网易彩票,设置-推荐应用-有很多应用程序图标
-如果本机已经安装过,会直接跳转到另外一个应用程序
-软件的广告,推广结果,后续会有一些列的金钱上的结算
3) 支付宝,第三方支付,淘宝,电话费充值。。。
2. 要打开本机上的其他应用程序,需要设置schemes,自定义的协议头,可以打开其他的应用程序
跳转的代码如下:
- (IBAction)openWangyi:(id)sender
{
// 跳转到其他应用程序
// schemes: 网易的scheme wangyi
NSURL *url = [NSURL URLWithString:@"wangyi://view?newsid=201410130001"];
// 判断本机是否安装了目标程序
if ([[UIApplication sharedApplication] canOpenURL:url]) {
[[UIApplication sharedApplication] openURL:url];
} else {
NSLog(@"没有安装,可以再给定下载地址,前往");
}
}
新浪微博的授权界面说明:
1> 在新浪微博中,本身不能直接跳转到该界面
2> 用其他应用程序打开时,如果scheme时weibo://oaauth,直接进入此界面
3> 如果直接点击,返回,返回调用放应用程序
4> 如果点击表格行中的用户名,直接返回用户信息给调用应用程序
4. 如果要返回调用的应用程序,需要知道调用我们的应用程序的scheme
/** 只要是由其他应用程序打开的,就会调用此方法 */
/** URL 就是其他应用程序,打开当前程序使用的URL */
- (BOOL)application:(UIApplication *)application handleOpenURL:(NSURL *)url
/**
openURL 是打开当前应用程序的url
sourceApplication 是当开当前应用程序的源程序的BundleId
提示:一旦重写了新方法,旧方法就不再被执行
但是:很多第三方框架,都建议两个方法全都写
*/
- (BOOL)application:(UIApplication *)application openURL:(NSURL *)url sourceApplication:(NSString *)sourceApplication annotation:(id)annotation
24. 社交分享
远程通知-由服务器、开发商向用户推送消息
社交分享-App的用户主动分享他们的经验和心得 - 口碑营销
========================================================
SSO - 目前在国内使用比较多,如果本机安装了某个应用程序,会直接进入该应用程序获得授权。
URL Schemes填"sina."+你的友盟AppKey.实现下面两个系统回调:
========================================================
如果使用腾讯接口,就不支持64位了。
强烈推荐大家使用的框架:友盟的统计分析
提示:目前使用友盟的社交分享无法上线!
关注官方网站:http://bbs.umeng.com/forum-social-1.html