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):在新的线程中执行任务,具备开启新线程的能力
1:在主队列并发

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后会等当前正在进行的任务执行完闭后取消后面的操作
  • 挂起和恢复
    • 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;

0 0
原创粉丝点击