IOS学习总结--Android转IOS的福音
来源:互联网 发布:mac有没有助手 编辑:程序博客网 时间:2024/05/17 22:46
一. IBOutlet和IBAction
1,IBOutlet 其实就是个符号 标记给interface builder看的,就是在初始化的时候,
被IBOutlet修饰的变量是跟.xib文件UI控件一一链接的,而且这个变量的修饰最好用weak类型的
2,IBAction 跟IBOutlet功能很类似,只是他是修饰方法的,它修饰的方法,是跟.xib中的UI控件相应的TouchEvent对应的
3 ,在interface builder中编辑.xib时,其实很简单,就是按照布局要求,进行相应的拖拽,然后操作各种inspector进行设置
UI控件的属性值,比如size inspector设置大小 attitude inspector设置各种属性
IOS一次TouchEvent的示意图如下:
二. UIViewController
在IOS中UIViewController就相当于Android的Acticity是MVC框架中的C,就是控制器,View角色只负责显示视图,view的这部分就是我们在nib或者storyboard设计的UI了。Model也就是我们的数据模型,例如从Core data中加载的实体类等等。这整个架构分工清晰,降低了代码的耦合度。今天我们要学习的角色就是Controller。
UIViewController与UIWindow、UIView的关系如下 图所示:
UIViewController加载视图
UIViewController有两种加载方式,第一种是通过手动加载xib文件来加载视图,第二种是直接通过代码来创建View Controller中的师徒来加载。直接看示例吧。
方式一 :xib加载
在创建一个xib文件,将File's Owner设置为对应的UIViewController类型,然后关联File's Owner的view与xib中的root view(在创建UIViewController时自动创建了xib的话不需要这一步,只有分开创建时才需要手动建立关联,否则会报错。),然后通过如下代码即可创建:
// MainViewController *vc=[[MainViewController alloc]initWithNibName:@"MainViewController" bundle:nil];
// self.window.rootViewController = vc;
MainViewController *vc=[[MainViewController alloc] init];
self.window.rootViewController = vc;
方式二:代码添加View
思路就是将View添加到UIViewController的root view中,在UIViewController启动时默认会从与其关联的xib或者storyboard中加载视图,如果没有找到则root view为nil。我们可以覆写loadView方法,通过代码的形式向里面添加view。代码如下 :
// 加载视图时绘制
-(void) loadView
{
UILabel* label = [[UILabel alloc] initWithFrame:[UIScreen mainScreen].applicationFrame] ;
label.text = @"Draw The Text";
label.backgroundColor = [UIColor redColor] ;
self.view = label;
}
UIViewController的生命周期
ViewController生命周期会经历初始化、加载视图、销毁视图、生命结束等过程。
1)init方法
初始化ViewController本身。
2)loadView方法
当view需要被展示而它却是nil时,viewController会调用该方法,如果代码构建View的话需要重写此方法。
3)viewDidLoad方法
执行完loadView后继续执行viewDidLoad,loadView时还没有view,而viewDidLoad时view已经创建好了。
4)viewDidUnload方法
当系统内存吃紧的时候会调用该方法。
5)dealloc
释放其他资源或内存。
细则:
一、 大体流程:
(loadView/nib)文件来加载view到内存-->viewDidLoad函数进一步初始化这些view-->内存不足时, 调用viewDidUnload函数释放views-->当需要使用view时又回到第一步
loadView:
永远不要主导调用这个函数。
viewController 会在view的property被请求并且当前view值为nil时调用这个函数。如果你手动创建view, 你应该重载这个函数,切不要在重载的时候调用[super loadView]。
viewDidload:
这个函数的作用主要是让你可以进一步的初始化你的views。
viewDidLoad通常负责的是view及其子view被加载进内存之后的数据初始化的工作,即视图的数据部分的初始化
viewDidUnLoad:
这个函数时viewDidLoad的对立函数。在程序内存欠缺时,这个函数被controller带哦用,来释放他的view以及view相关的对象。由于controller通常保存着view以及相关的object的引用,所以你必须使用这个函数来放弃这些对象的所有权以便内存回收,但不要释放那些难以重建的数据
viewWillAppear:
视图即将可见时调用,默认情况下不执行任何操作。
viewDidAppear:
视图已完全过渡到屏幕上时调用
viewWillDisappear:
视图被驳回时调用,覆盖或以其他方式隐藏,默认情况下不执行任何操作
viewDidDisappear:
视图被驳回后调用,覆盖或以其他方式隐藏。默认情况下不执行任何操作
didReceiveMemoryWarning:
当程序内存过度时,系统会调用该方法
二、Controller和View的生命周期
这里指的View是指Controller的View。它作为Controler的属性,生命周期在Controller的生命周期内。就是说你的Controller不能在view释放前就释放了。
当你alloc并init了一个ViewController时,这个ViewController应该是还没有创建view的。ViewController的view是使用了lazyInit方式创建,就是说你调用的view属性的getter:[self view]。在getter里会先判断view是否创建,如果没有创建,那么会调用loadView来创建view。loadView完成时会继续调用viewDidLoad。loadView和viewDidLoad的一个区别就是:loadView时还没有view。而viewDidLoad时view以及创建好了。
当view被添加其他view中之前时,会调用viewWillAppear,而之后会调用viewDidAppear。
当view从其他view中移出之前时,会调用viewWillDisAppear,而之后会调用viewDidDisappear。
当view不在使用,而且是disappeared,受到内存警告时,那么viewController会将view释放并将其指向nil。
三、代码组织(如何设计良好的viewcontroller)
ViewController生命周期中有那么多函数,一个重要问题就是什么代码该写在什么地方。
1、init里不要出现创建view的代码。良好的设计,在init里应该只有相关数据的初始化,而且这些数据都是比较关键的数据。init里不要掉self.view,否则会导致viewcontroller创建view。(因为view是lazyinit的)。
2、loadView中只初始化view,一般用于创建比较关键的view如tableViewController的tabView,UINavigationController的navgationBar,不可掉用view的getter(在掉super loadView前),最好也不要初始化一些非关键的view。如果你是从nib文件中创建的viewController在这里一定要首先调用super的loadView方法,但建议不要重载这个方法。
3、viewDidLoad 这时候view已经有了,最适合创建一些附加的view和控件了。
4、viewWillAppear 这个一般在view被添加到superview之前,切换动画之前调用。在这里可以进行一些显示前的处理。比如键盘弹出,一些特殊的过程动画(比如状态条和navigationbar颜色)。
5、viewDidAppear 一般用于显示后,在切换动画后,如果有需要的操作,可以在这里加入相关代码。
6、viewDidUnload 这时候viewController的view已经是nil了。由于这一般发生在内存警告时,所以在这里你应该将那些不在显示的view释放了。比如你在viewcontroller的view上加了一个label,而且这个label是viewcontroller的属性,那么你要把这个属性设置成nil,以免占用不必要的内存,而这个label在viewDidLoad时会重新创建。
7、接下来看看ViewController中的view是如何被卸载的:
当系统发出内存警告时,会调用didReceiveMemoeryWarning方法,如果当前有能被释放的view,系统会调用viewWillUnload方法来释放view,完成后调用viewDidUnload方法,至此,view就被卸载了。此时原本指向view的变量要被置为nil,具体操作是在viewDidUnload方法中调用self.myButton = nil;
小结一下:loadView和viewDidLoad的区别就是,loadView时view还没有生成,viewDidLoad时,view已经生成了,loadView只会被调用一次,而viewDidLoad可能会被调用多次(View可能会被多次加载),当view被添加到其他view中之前,会调用viewWillAppear,之后会调用viewDidAppear。当view从其他view中移除之前,调用viewWillDisAppear,移除之后会调用viewDidDisappear。当view不再使用时,受到内存警告时,ViewController会将view释放并将其指向为nil。
ViewController的生命周期中各方法执行流程如下:init—>loadView—>viewDidLoad—>viewWillApper—>viewDidApper—>viewWillDisapper—>viewDidDisapper—>viewWillUnload->viewDidUnload—>dealloc
三:线程
1:根类自带NSObject:[NSObject performSelectorInBackGround:@selector() withObject;nil]
2:NSThread:[NSThread detachNewThreadSelector:@Selector() toTarget:self wihtObject:nil]
SThread* myThread = [[NSThread alloc] initWithTarget:self selector:@selector(doSomething:) object:nil];
[myThread start];
这两种方式的区别在于:
前一种调用就会立即创建一个线程并执行selector方法;第二种方式尽管alloc了一个新Thread,但需要手动调用start方法来启动线程。这点与Java创建线程的方式相似。
第一种方式,与上述做法1使用NSObject的类方法performSelectorInBackground:withObject:是一样的;第二种方式的可以在start真正创建线程之前对其进行设置,比如设 置线程的优先级。
3:GCD全称Grand Central Dispatch,我们通俗的翻译叫牛逼的中心调度。
通过 GCD,开发者不用再直接跟线程打交道了,只需要向队列中添加代码块即可,GCD 在后端管理着一个线程池。GCD 不仅决定着你的代码块将在哪个线程被执行,它 还根据可用的系统资源对这些线程进行管理。这样可以将开发者从线程管理的工作中解放出来,通过集中的管理线程,来缓解大量线程被创建的问题。
GCD 带来的另一个重要改变是,作为开发者可以将工作考虑为一个队列,而不是一堆线程,这种并行的抽象模型更容易掌握和使用。
首先,系统提供给你一个叫做 主队列(main queue) 的特殊队列。和其它串行队列一样,这个队列中的任务一次只能执行一个。然而,它能保证所有的任务都在主线程执行,而主线程是唯一可用于更新 UI 的线程。这个队列就是用于发生消息给 UIView 或发送通知的。
系统同时提供给你好几个并发队列。它们叫做 全局调度队列(Global Dispatch Queues) 。目前的四个全局队列有着不同的优先级:background、low、default 以及 high。要知道,Apple 的 API 也会使用这些队列,所以你添加的任何任务都不会是这些队列中唯一的任务。
最后,你也可以创建自己的串行队列或并发队列。这就是说,至少有五个队列任你处置:主队列、四个全局调度队列,再加上任何你自己创建的队列。
GCD术语
- 串行(Serial):让任务一个接着一个地执行(一个任务执行完毕后,再执行下一个任务)
- 并发(Concurrent):可以让多个任务并发(同时)执行(自动开启多个线程同时执行任务)并发功能只有在异步(dispatch_async)函数下才有效。
- 同步(Synchronous):在当前线程中执行任务,不具备开启新线程的能力
- 异步(Asynchronous):在新的线程中执行任务,具备开启新线程的能力
dispatch_queue_t mainQueue = dispatch_get_main_queue();
dispatch_async(mainQueue, ^{
NSLog(@"1");
});
dispatch_async(mainQueue, ^{
NSLog(@"2");
});
dispatch_async(mainQueue, ^{
NSLog(@"3");
});
dispatch_async(mainQueue, ^{
NSLog(@"4");
});
2.认识全局队列,体验并发队列的运行,运行结果随机打印:2,3,1,4,随机执行。
dispatch_queue_t defaultQueue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
dispatch_async(defaultQueue, ^{
NSLog(@"1");
});
dispatch_async(defaultQueue, ^{
NSLog(@"2");
});
dispatch_async(defaultQueue, ^{
NSLog(@"3");
});
dispatch_async(defaultQueue, ^{
NSLog(@"4");
});
3,自己创建队列
dispatch_queue_t concurrentQueue = dispatch_queue_create("com.bjsxt.concurrentQueue", DISPATCH_QUEUE_CONCURRENT);
dispatch_async(concurrentQueue, ^{
NSLog(@"4");
dispatch_sync(concurrentQueue, ^{
[NSThread sleepForTimeInterval:3];
NSLog(@"5");
});
NSLog(@"6");
});
4,单例中运行
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
NSLog(@"onceToken");
});
5.延迟加载dispatch_after
double delayInSeconds = 2.0;
dispatch_queue_t mainQueue = dispatch_get_main_queue();
dispatch_time_t popTime = dispatch_time(DISPATCH_TIME_NOW,delayInSeconds * NSEC_PER_SEC);
dispatch_after(popTime, mainQueue, ^{
NSLog(@"延时执行的2秒");
});
6.调度组dispatcch_group_t
dispatch_group_t group = dispatch_group_create();
dispatch_queue_t defaultQueue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
dispatch_group_async(group, defaultQueue, ^{
[NSThread sleepForTimeInterval:3];
NSLog(@"1");
});
dispatch_group_async(group, defaultQueue, ^{
[NSThread sleepForTimeInterval:2];
NSLog(@"2");
});
dispatch_group_notify(group, defaultQueue, ^{
NSLog(@"3");
});
如果想在dispatch_queue中所有的任务执行完成后在做某种操作,在串行队列中,可以把该操作放到最后一个任务执行完成后继续,但是在并行队列中怎么做呢。这就有dispatch_group 成组操作。
1和2谁先输出不一定 但是3肯定是最后输出这就是dispatch_group的作用
//等价于
// dispatch_group_enter(group);
// dispatch_async(defaultQueue, ^{
//
// NSLog(@"1");
// dispatch_group_leave(group);
// });
7,dispatch_barrier_async
在前面的任务执行结束后它才执行,而且它后面的任务等它执行完成之后才会执行.
dispatch_queue_t queue = dispatch_queue_create("com.bjsxt.barrierExecute", DISPATCH_QUEUE_CONCURRENT);
dispatch_async(queue, ^{
[NSThread sleepForTimeInterval:2];
NSLog(@"1");
});
dispatch_async(queue, ^{
[NSThread sleepForTimeInterval:4];
NSLog(@"2");
});
dispatch_barrier_async(queue, ^{
NSLog(@"3");
[NSThread sleepForTimeInterval:4];
});
dispatch_async(queue, ^{
[NSThread sleepForTimeInterval:1];
NSLog(@"4");
});
8.执行某个代码dispatch_apply
dispatch_queue_t defaultQueue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
dispatch_apply(5, defaultQueue, ^(size_t i) {
NSLog(@"%lu",i);
});
4:NSOperation和NSOprationQueue
其实使用的时候主要是使用NSOperation的两个子类NSInvocationOperation和NSBlockOperation
NSInvocationOperation * invocationOperation=[[NSInvocationOperation aloc] initWithtarget:self selector:@selector()]
[invocationOperation start]
NSBlockOperation *blockOperation=[NSBlockOperation blockOperationWithBlock:^{
NSLog(@"thread--%@",[NSThread currentThread]);
}]
[blockOperation addExcutorBlock:^{
NSLog(@"thread--%@",[NSThread currentThread]);
}]
[blockOperation start]
由打印的结果发现: task0的结果和前面的结论一样,是执行在主线程中的(因为是在主线程中调用start方法),但task1和task2都是在自己的新线程中执行.也就是说:当NSBlockOperation封装的操作数大于1的时候,就会执行异步操作.
自定义NSOperation
自定义NSOperation的方法也很简单,我们需要做到下面几个步骤:
1.子类化NSOperation
2.在.m文件里面实现-(void)main方法
3.初始化该操作的时候直接调用alloc及init即可
4.同样可以通过start方法让你自定义的任务跑在当前线程中
关键代码:
自定义NSOperation的实现文件:
#import "AROperation.h"
@implementation AROperation
- (void)main {
NSLog(@"this is my custom operation ---- %@", [NSThread currentThread]);
}
@end
使用(在任何需要启用该任务的地方):
// 该操作被执行时就会执行op内部定义的任务
AROperation *op = [[AROperation alloc] init];
[op start];
怎样使用NSOperationQueue
NSOperation的start方法默认是同步执行任务,这样的使用并不多见,只有将NSOperation与NSOperationQueue进行结合,才会发挥出这种多线程技术的最大功效.当NSOperation被添加到NSOperationQueue中后,就会全自动地执行异步操作.
NSOperationQueue的种类:
- 自带主队列
[NSOperationQueue mainQueue]
: 添加到主队列中的任务都会在主线程中执行 - 自己创建队列(非主队列)
NSOperationQueue *queue = [[NSOperationQueue alloc] init];
: 这种队列同时包含串行、并发的功能,添加到非主队列的任务会自动放到子线程中执行
- 自带主队列
向NSOperationQueue中添加操作:
- 直接添加
[queue addOperation:op1];
- 使用block添加,block的内容会被包装成operation对象添加到队列
[queue addOperationWithBlock:^{}];
操作一但被到添加到队列中,就会自动异步执行.
- 直接添加
设置NSOperationQueue的最大并发数
NSOperationQueue可以通过以下方法设置最大并发数,setMaxConcurrentOperationCount:
,值得注意的是:当并发数为1就变成了串行执行任务
NSOperationQueue的暂停恢复和取消
- 取消
- NSOperation有一个
cancel
方法可以取消单个操作 - NSOperationQueue的
cancelAllOperations
相当于队列中的每个operation调用了cancel方法,会取消队列里面全部的操作. - 但是,不能取消正在进行中的任务,队列调用了cancelAllOperations后会等当前正在进行的任务执行完闭后取消后面的操作
- NSOperation有一个
- 挂起和恢复
isSuspended
: 判断是否挂起setSuspended
: YES表示挂起,NO表示恢复- 和取消功能类似,我们同样不能挂起正在运行中的操作,队列会等当前操作结束后将后面的操作暂停(挂起)
因此, 我们在自定义NSOperation的时候需要注意,最好可以经常通过判断isCancelled方法检测操作是否被取消,以响应外部可能进行的取消操作.如:
添加依赖和监听通过设置操作间的依赖,可以确定这些操作的执行顺序
如:
[op3 addDependency:op1];
[op3 addDependency:op2];
表示op3会在op1和op2都执行完毕后才执行
添加依赖的时候要注意防止添加循环依赖,此外我们还可以在不同队列的operation之间添加依赖
监听
op.completeBlock可以监听一个操作执行完毕的时刻,这个block里面可以添加一些我们需要执行的操作
这个block里面的操作仍然是在子线程执行,但不一定和被监听的操作在同一个线程
线程间通信
有时我们在子线程中执行完一些操作的时候,需要回到主线程做一些事情(如进行UI操作),因此需要从当前线程回到主线程,以下载并显示图片为例,方法如下:
NSOperationQueue *queue = [[NSOperationQueue alloc] init];
// 子线程下载图片
[queue addOperationWithBlock:^{
NSURL *url = [NSURL URLWithString:@"http://img.pconline.com.cn/images/photoblog/9/9/8/1/9981681/200910/11/1255259355826.jpg"];
NSData *data = [NSData dataWithContentsOfURL:url];
UIImage *image = [[UIImage alloc] initWithData:data];
// 回到主线程进行显示
[[NSOperationQueue mainQueue] addOperationWithBlock:^{
self.imageView.image = image;
}];
}];
四,UITableView相当于Android的ListView数据列表
用UITableView的时候,难免会涉及到两个协议一个类,UITableViewDataSource, UITableViewDelegate和UITableViewCell
1, UITableViewDataSource相当于Android的Adapter,主要负责数据处理,比如一共多少类别的Item type-》sections表示还有就是每个section有多少条数据
其实这些都被封装到NSIndexPath中有section--哪一个itemType和row---那一行;
2, UITableViewDeletegate相当于OnItemClickLisner;
3,UITableViewCell相当于Android里面的ItemView;
- IOS学习总结--Android转IOS的福音
- Swift语言的诞生是IOS 开发者的福音
- swift开源了,iOS开发者的福音到了
- [iOS总结]delegate的学习
- ios学习总结的非常好
- Android Json 的福音
- iOS学习笔记---简单的学习总结
- 【IOS 开发学习总结-OC-61】IOS 的自动旋转
- 【IOS 开发学习总结-OC-62】IOS 应用的生命周期
- 【IOS 开发学习总结-OC-61】IOS 的自动旋转
- iOS学习动画总结
- iOS学习开发总结
- iOS AutoLayout学习总结
- IOS学习资源总结
- iOS学习内容总结
- iOS AutoLayout学习总结
- iOS开发学习总结
- iOS学习总结
- 日常总结
- nginx的日志切割
- 写了个shell脚本自由切换openjdk7和openjdk8
- ARM 移植 PPPD
- d3d坐标系与绘制基本图元
- IOS学习总结--Android转IOS的福音
- 秒杀抢购思路以及高并发下数据安全
- Git如何切换远程仓库地址
- 绑定服务的用法
- HDU 1166 敌兵布阵 【线段树入门题,单点更新,结构题数组】
- 用root直接登入ubuntu 14_04
- servlet生命周期
- Unity开发HTC vive 五、拾取和触碰
- jpa或bernate使用原生sql进行关联查询org.hibernate.hql.internal.ast.QuerySyntaxException: Path expected for join