afnetworking 学习记录

学习了大神文章,做此记录
ios 第三方库 afnetworking 解析;

afnetworking 现在是我们最常用,并且用的最多的,ios 网络框架,现在我们自己的项目已经全部使用afnetworking ,afnetworking 现在已经更新到3.0 了,需要的朋友这里下载:https://github.com/AFNetworking/AFNetworking
记录下,平时遇到的一些问题,

循环引用问题

__weak __typeof(self)weakSelf = self;
self.backgroundTaskIdentifier = [application beginBackgroundTaskWithExpirationHandler:^{
    __strong __typeof(weakSelf)strongSelf = weakSelf;
}];

我们经常在block 外面实现一个weakself的饮用,防止循环引用,但是为了保证在block 内部执行的weakself保证在没有执行完,不能自动释放,直到结束,所有有创建一个strongSelf,这样就放着了,没有执行完成自动释放,

队列下载问题
把一个任务加入到队列方法

dispatch_group_async(group, queue, ^{
    block();
});

等同于

dispatch_async(queue, ^{
    dispatch_group_enter(group);
    block()
    dispatch_group_leave(group);
});

但是当把一个异步任务加入到队列中,是有问题的

dispatch_group_async(group, queue, ^{
    [self performBlock:^(){
        block();
    }];
    //未执行到block() group任务就已经完成了
});

这样写才能保证异步任务执行完成才结束

dispatch_group_enter(group);
[self performBlock:^(){
    block();
    dispatch_group_leave(group);
}];

多线程编程NSOperationQueue 和NSRunLoop

NSOperationQueue是一个苹果提供的多线程操作的api,同类型的还有GCDNSOperationQueue是基于GCD 实现OC版本的api,而GCD 是由C语言实现的,NSOperationQueue对于并发操作队列上面,比起GCD有更多的优势,主要方面有

  • 可以更容易的添加任务的依赖关系;
  • 可以很方便的取消一个NSOperation的执行;
  • 提供了任务的状态:isExecuteing, isFinishedNSOperationQueue 和GCD 的使用主要还是需要看业务场景,来使用,NSOperationQueue 简单实用如下

一 、创建一个NSOperationQueue对象队列,默认是同步的,

NSOperationQueue *mainQueue = [NSOperationQueue mainQueue]; 

一 、创建一个NSOperationQueue对象队列,默认是同步的,

[queue addOperation:operation];  

三、添加多个任务到队列

//operations是个数组
[queue addOperations:operations waitUntilFinished:NO];  

四、 当某个NSOperation对象依赖于其它NSOperation对象的完成时,就可以通过addDependency方法添加一个或者多个依赖的对象,只有所有依赖的对象都已经完成操作,当前NSOperation对象才会开始执行操作。另外,通过removeDependency方法来删除依赖对象。

[operation2 addDependency:operation1];  

依赖关系不局限于相同queue中的NSOperation对象,NSOperation对象会管理自己的依赖, 因此完全可以在不同的queue之间的NSOperation对象创建依赖关系

这里写图片描述

唯一的限制是不能创建环形依赖,比如A依赖B,B依赖A,这是错误的

当不设置依赖关系的时候,是安装添加顺序执行的,设置依赖关系后,会有影响


NSOperationQueue *queue = [[NSOperationQueue alloc] init];

NSBlockOperation *operation1 = [NSBlockOperation blockOperationWithBlock:^(){
    NSLog(@"执行第1次操作,线程:%@", [NSThread currentThread]);
}];

NSBlockOperation *operation2 = [NSBlockOperation blockOperationWithBlock:^(){
    NSLog(@"执行第2次操作,线程:%@", [NSThread currentThread]);
}];
// operation1依赖于operation2,先执行2,在执行1
[operation1 addDependency:operation2];

[queue addOperation:operation1];
[queue addOperation:operation2];

执行结果,operation2 任务放到operation1的前面

2016-01-28 14:30:20.118 downLoadTESt[70641:1589522] 执行第2次操作,线程:<NSThread: 0x7fa3a3d31ed0>{number = 3, name = (null)}
2016-01-28 14:30:20.120 downLoadTESt[70641:1589522] 执行第1次操作,线程:<NSThread: 0x7fa3a3d31ed0>{number = 3, name = (null)}

对于添加到queue中的operations,它们的执行顺序取决于2点:

1.首先看看NSOperation是否已经准备好:是否准备好由对象的依赖关系确定

2.然后再根据所有NSOperation的相对优先级来确定。优先级等级则是operation对象本身的一个属性。默认所有operation都拥有“普通”优先级,不过可以通过setQueuePriority:方法来提升或降低operation对象的优先级。优先级只能应用于相同queue中的operations。如果应用有多个operation queue,每个queue的优先级等级是互相独立的。因此不同queue中的低优先级操作仍然可能比高优先级操作更早执行。
注意:优先级不能替代依赖关系,优先级只是对已经准备好的 operations确定执行顺序。先满足依赖关系,然后再根据优先级从所有准备好的操作中选择优先级最高的那个执行。


五、设置队列的最大并发操作数量,意思是队列中最多同时运行几条线程,虽然NSOperationQueue类设计用于并发执行Operations,你也可以强制单个queue一次只能执行一个Operation。setMaxConcurrentOperationCount:方法可以配置queue的最大并发操作数量。设为1就表示queue每次只能执行一个操作。不过operation执行的顺序仍然依赖于其它因素,比如operation是否准备好和operation的优先级等。因此串行化的operation queue并不等同于GCD中的串行dispatch queue

// 每次只能执行一个操作  
queue.maxConcurrentOperationCount = 1; 

六、取消Operations,一旦添加到operation queue,queue就拥有了这个Operation对象并且不能被删除,唯一能做的事情是取消。你可以调用Operation对象的cancel方法取消单个操作,也可以调用operation queue的cancelAllOperations方法取消当前queue中的所有操作。

// 取消单个操作
[operation cancel];

// 取消queue中所有的操作
[queue cancelAllOperations];

**七、等待Options完成
为了最佳的性能,你应该设计你的应用尽可能地异步操作,让应用在Operation正在执行时可以去处理其它事情。如果需要在当前线程中处理operation完成后的结果,可以使用NSOperation的waitUntilFinished方法阻塞当前线程,等待operation完成。通常我们应该避免编写这样的代码,阻塞当前线程可能是一种简便的解决方案,但是它引入了更多的串行代码,限制了整个应用的并发性,同时也降低了用户体验。绝对不要在应用主线程中等待一个Operation,只能在第二或次要线程中等待。阻塞主线程将导致应用无法响应用户事件,应用也将表现为无响应。**


// 会阻塞当前线程,等到某个operation执行完毕
[operation waitUntilFinished];

**八、暂停和继续queue
如果你想临时暂停Operations的执行,可以使用queue的setSuspended:方法暂停queue。不过暂停一个queue不会导致正在执行的operation在任务中途暂停,只是简单地阻止调度新Operation执行。你可以在响应用户请求时,暂停一个queue来暂停等待中的任务。稍后根据用户的请求,可以再次调用setSuspended:方法继续queue中operation的执行**


// 暂停queue
[queue setSuspended:YES];

// 继续queue
[queue setSuspended:NO];

上面就是NSOperationQueue的简单使用,

NSRunLoop 使用

RunLoop的字面意思就是“运行回路”,听起来像是一个循环。实际它就是一个循环,它在循环监听着事件源,把消息分发给线程来执行。RunLoop并不是线程,也不是并发机制,但是它在线程中的作用至关重要,它提供了一种异步执行代码的机制;NSRunLoop只处理两种源:输入源、时间源。而输入源又可以分为:NSPort、自定义源、performSelector:OnThread:delay;

主要讲解定时源

定时源就是NSTimer了,定时源在预设的时间点同步地传递消息。因为Timer是基于RunLoop的,也就决定了它不是实时的。

Run Loop Modes

RunLoop对于上述四种事件源的监视,可以通过设置模式来决定监视哪些源。 RunLoop只会处理与当前模式相关联的源,未与当前模式关联的源则处于暂停状态。

modenameDescription
DefaultNSDefaultRunLoopMode (Cocoa) kCFRunLoopDefaultMode (Core Foundation)缺省情况下,将包含所有操作,并且大多数情况下都会使用此模式
ConnectionNSConnectionReplyMode (Cocoa)此模式用于处理NSConnection的回调事件
ModalNSModalPanelRunLoopMode (Cocoa)模态模式,此模式下,RunLoop只对处理模态相关事件
Event TrackingNSEventTrackingRunLoopMode (Cocoa)此模式下用于处理窗口事件,鼠标事件等
Common ModesNSRunLoopCommonModes (Cocoa) kCFRunLoopCommonModes (Core Foundation)此模式用于配置”组模式”,一个输入源与此模式关联,则输入源与组中的所有模式相关联。

常见问题一:为什么TableView滑动时,Timer暂停了?

因为当tableview 在滑动的时候,runLoop 正在以NSEventTrackingRunLoopMode 模式运行,因此只会处理和NSEventTrackingRunLoopMode绑定的事件源,比如触摸,滚动,而timer 是绑定的在NSDefaultRunLoopMode,因此停掉了,如果需要timer 也运行,就需要,组合模式,NSRunLoopCommonModes,这样就不会停掉了

//设置timer的模式
[[NSRunLoop currentRunLoop] addTimer:timer forMode:NSRunLoopCommonModes];

后台线程的RunLoop没有启动的情况下的现象就是:“代码执行完,线程就结束被回收了”。就像我们简单的程序执行完就退出了。 所以如果我们希望在代码执行完成后还要保留线程等待一些异步的事件时,比如NSURLConnection和NSTimer, 就需要手动启动后台线程的RunLoop。
启动RunLoop,我们需要设定RunLoop的模式,我们可以设置 NSDefaultRunLoopMode。 那默认就是监听所有时间源:

//NSURLConnection   
[_connection scheduleInRunLoop:[NSRunLoop currentRunLoop] forMode:NSRunLoopCommonModes];
[[NSRunLoop currentRunLoop] runMode:NSRunLoopCommonModes beforeDate:[NSDate distantFuture]];

//Timer
[[NSRunLoop currentRunLoop] addTimer:_timer forMode:NSRunLoopCommonModes];
[[NSRunLoop currentRunLoop] runMode:NSRunLoopCommonModes beforeDate:[NSDate distantFuture]];

了解几个名词

互斥锁
互斥访问的意思就是同一时刻,只允许一个线程访问某个特定资源。为了保证这一点,每个希望访问共享资源的线程,首先需要获得一个共享资源的互斥锁。 对资源加锁会引发一定的性能代价。

原子性
从语言层面来说,在 Objective-C 中将属性以 atomic 的形式来声明,就能支持互斥锁了。事实上在默认情况下,属性就是 atomic 的。将一个属性声明为 atomic 表示每次访问该属性都会进行隐式的加锁和解锁操作。虽然最把稳的做法就是将所有的属性都声明为 atomic,但是加解锁这也会付出一定的代价。

死锁
互斥锁解决了竞态条件的问题,但很不幸同时这也引入了一些其他问题,其中一个就是死锁。当多个线程在相互等待着对方的结束时,就会发生死锁,这时程序可能会被卡住。

参考文章

http://blog.cnbluebox.com/blog/2014/07/01/cocoashen-ru-xue-xi-nsoperationqueuehe-nsoperationyuan-li-he-shi-yong/

http://blog.cnbang.net/tech/2320/

http://blog.csdn.net/q199109106q/article/details/8566222

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值