2017-03第二周

> 不积跬步,无以至千里;不积小流,无以成江海。——荀子

[TOC]

2017-03-06

一、苹果核 - 菜鸟不要怕,看一眼,你就会用GCD,带你装逼带你飞

1.Serial Diapatch Queue 串行队列

//第一个参数是队列名字。第二个参数是队列类型,如果设置为NULL,创建出来的是串行队列

dispatch_queue_t serialDispatchQueue = dispatch_queue_create("com.test.queue", DISPATCH_QUEUE_SERIAL);

2.Concurrent Diapatch Queue 并发队列

//与串行队列刚好相反,他不会存在任务间的相互依赖。

dispatch_queue_t concurrentDispatchQueue = dispatch_queue_create("com.test.queue", DISPATCH_QUEUE_CONCURRENT);

3.Global Queue & Main Queue

  • Global Queue其实就是系统创建的Concurrent Dispatch Queue

  • Main Queue 其实就是系统创建的位于主线程的Serial Dispatch Queue

通常情况我们会把这2个队列放在一起使用,也是我们最常用的开异步线程-执行异步任务-回主线程的一种方式

  • Global Queue的优先级:
    DISPATCH_QUEUE_PRIORITY_HIGH 2

DISPATCH_QUEUE_PRIORITY_DEFAULT 0

DISPATCH_QUEUE_PRIORITY_LOW (-2)

DISPATCH_QUEUE_PRIORITY_BACKGROUND INT16_MIN

  • 异步主线程 dispatch_async(dispatch_get_main_queue(), ^{});

在日常工作中,除了在其他线程返回主线程的时候需要用这个方法,还有一些时候我们在主线程中直接调用异步主线程,这是利用dispatch_async的特性:block中的任务会放在主线程本次runloop之后返回。 这样,有些存在先后顺序的问题就可以得到解决了。

4.dispatch_set_target_queue

dispatch_set_target_queue为自己创建的队列设置优先级

5.dispatch_after

这个是最常用的,用来延迟执行的GCD方法,因为在主线程中我们不能用sleep来延迟方法的调用,所以用它是最合适的

6.dispatch_group

当我们需要监听一个并发队列中,所有任务都完成了,就可以用到这个group,因为并发队列你并不知道哪一个是最后执行的,所以以单独一个任务是无法监听到这个点的,如果把这些单任务都放到同一个group,那么,我们就能通过dispatch_group_notify方法知道什么时候这些任务全部执行完成了。

通过把这个并发队列任务统一加入group中,group每次runloop的时候都会调用一个方法dispatch_group_wait(group, DISPATCH_TIME_NOW),用来检查group中的任务是否已经完成,如果已经完成了,那么会执行dispatch_group_notify的block

7.dispatch_barrier

此方法的作用是在并发队列中,完成在它之前提交到队列中的任务后打断,单独执行其block,并在执行完成之后才能继续执行在他之后提交到队列中的任务。

这其实就起到了一个线程锁的作用,在多个线程同时操作一个对象的时候,读可以放在并发进行,当写的时候,我们就可以用dispatch_barrier_async方法,效果杠杠的。

8.dispatch_sync

dispatch_sync 会在当前线程执行队列,并且阻塞当前线程中之后运行的代码,所以,同步线程非常有可能导致死锁现象,我们这边就举一个死锁的例子:

dispatch_sync(dispatch_get_main_queue(), ^{

     NSLog(@"有没有同步主线程?");

});

根据FIFO(先进先出)的原则,block里面的代码应该在主线程此次runloop后执行,但是由于他是同步队列,所有他之后的代码会等待其执行完成后才能继续执行,2者相互等待,所以就出现了死锁。

9.dispatch_apply

这个方法用于无序查找,在一个数组中,我们能开启多个线程来查找所需要的值

10.dispatch_suspend & dispatch_resume

队列挂起和恢复

11.Semaphore

我们可以通过设置信号量的大小,来解决并发过多导致资源吃紧的情况,以单核CPU做并发为例,一个CPU永远只能干一件事情,那如何同时处理多个事件呢,聪明的内核工程师让CPU干第一件事情,一定时间后停下来,存取进度,干第二件事情以此类推,所以如果开启非常多的线程,单核CPU会变得非常吃力,即使多核CPU,核心数也是有限的,所以合理分配线程,变得至关重要,那么如何发挥多核CPU的性能呢?如果让一个核心模拟传很多线程,经常干一半放下干另一件事情,那效率也会变低,所以我们要合理安排,将单一任务或者一组相关任务并发至全局队列中运算或者将多个不相关的任务或者关联不紧密的任务并发至用户队列中运算,所以用好信号量,合理分配CPU资源,程序也能得到优化,当日常使用中,信号量也许我们只起到了一个计数的作用,真的有点大材小用。

dispatch_semaphore_t semapore = dispatch_semaphore_create(10);//为了让一次输出10个,初始信号量为10

dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);

for(int i = 0; i < 20; i++){

     //第二个参数为timeout 递减信号量,如果信号量小于零,会一直等待到一个信号发生

    dispatch_semaphore_wait(semapore, DISPATCH_TIME_FOREVER);//每进来1次,信号量-1;进来10次后就一直hold住,直到信号量大于0;

    dispatch_async(queue, ^{

        NSLog(@"%i",i);

        sleep(2);

        //递增信号量的值,如果上一个值小于0,该方法返回前会唤醒一个等待的线程

        dispatch_semaphore_signal(semapore);//由于这里只是log,所以处理速度非常快,我就模拟2秒后信号量+1;

    });
}

12.dispatch_once

这个函数一般是用来做一个真的单例,也是非常常用的。


2017-03-07

一、 sunny分享

CFRunLoopSource

  • Source是RunLoop的数据源抽象类(protocol)
  • RunLoop定义了两个Version的Source:

Source0:处理App内部事件、App自己负责管理(触发),如UIEvent、CFSocket
Source1:由RunLoop和内核管理,Mach port驱动,如CFMachPort、CFMessagePort

CFRunLoopMode

  • RunLoop在同一段时间只能且必须在一种特定Mode下Run

  • 更换Mode时,需要停止当前Loop,然后重启新Loop

  • Mode是iOS App滑动顺畅的关键

  • 可以定制自己的Mode

NSDefaultRunLoopMode

默认状态、空闲状态

UITrackingRunLoopMode

滑动ScrollView时

UIInitializationRunLoopMode

私有,App启动时

NSRunLoopCommonModes

Mode集合


2017-03-08

一、iOS 无需SDK,一行代码调用主流地图

高德地图、百度地图和腾讯地图url规则:

/*

 必要参数的意思:sourceApplication:该app名字; poi name:目的地名称;lat/lon:目的地经纬度

 dev 参数进行解释:dev支持的值为"0"和"1",即是否需要进行国测局坐标加密。 如果传入的坐标已经是国测局坐标则传入0,如果传入的是地球坐标,则该参数传入1

 */

#define GaoDeNavUrl @"iosamap://navi?sourceApplication=%@&backScheme=%@&poiname=%@&poiid=BGVIS&lat=%@&lon=%@&dev=0&style=2"

#define GaoDeGPSUrl @"iosamap://viewMap?sourceApplication=%@&poiname=%@&lat=%@&lon=%@&dev=0"

/*

 百度地图的参数意思就比较简单了,对mode做一个解释,mode为调用地图之后的导航方式,除了walking(步行)还有driving(驾车)和transit(公交)

 origin=latlng:0,0  这个参数虽然意思上是要给一个当前坐标,但是可以随意设置,这里设置两个0,不影响导航

 */

#define BaiDuNavUrl @"baidumap://map/direction?origin=latlng:0,0|name:我的位置&destination=latlng:%@,%@|name:%@&mode=walking"

#define BaiDuGPSUrl @"baidumap://map/marker?location=%@,%@&title=我的位置&content=%@"

//腾讯地图导航

#define TXGPSUrl    @"qqmap://map/marker?marker=coord:%@,%@;title:%@;addr:%@"

#define TXNavUrl    @"qqmap://map/routeplan?type=walk&from=我的位置&to=%@&tocoord=%@,%@&referer=%@"

2017-03-09

一、UIView形变动画

  • 当UIView发生变换(transformed)后,不要使用frame属性,因为它往往不能代表视图的正确位置,使用bounds和center属性代替。见UIView(UIViewGeometry)中的说明

  • anchorPoint在视图左上角为(0,0),在视图右下角为(1,1),默认(0.5,0.5)。苹果文档中注释是错误的(normalized layer coordinates - '(0, 0)' is the bottom left corner of the bounds rect)。

  • 如果更改了一个图层的anchorPoint,那么这个图层会发生位移。iOS围绕某点缩放或旋转的AnchorPoint的设定

  • point、anchorPoint和frame三者的关系:

position.x = frame.origin.x + anchorPoint.x * bounds.size.width;

position.y = frame.origin.y + anchorPoint.y * bounds.size.height

这将是你最后一次纠结position与anchorPoint!

  • 修改position和anchorPoint会影响frame属性。

2017-03-10

一、iOS8.0之后的队列优先级 MBProgressHUD库中有用到

dispatch_queue_create函数,在iOS8之前,不能在创建时设定优先级,所以用dispatch_set_target_queue设置优先级,另外,如果两个队列dispatch_set_target_queue到同一个队列,DISPATCH_QUEUE_SERIAL串行、DISPATCH_QUEUE_CONCURRENT并行对两个队列也有作用,可以参考demo中方法testGCDTarget;iOS 8之后用dispatch_queue_attr_make_with_qos_class直接创建队列并设置优先级等;QOS_CLASS_USER_INITIATED:为iOS8之后的优先级,其它优先级为:

  • QOS_CLASS_USER_INTERACTIVE: user interactive等级表示任务需要被立即执行以提供好的用户体验。使用它来更新UI,响应事件以及需要低延时的小工作量任务。这个等级的工作总量应该保持较小规模。
  • QOS_CLASS_USER_INITIATED:user initiated等级表示任务由UI发起并且可以异步执行。它应该用在用户需要即时的结果同时又要求可以继续交互的任务。
  • QOS_CLASS_UTILITY:utility等级表示需要长时间运行的任务,常常伴随有用户可见的进度指示器。使用它来做计算,I/O,网络,持续的数据填充等任务。这个等级被设计成节能的。
  • QOS_CLASS_BACKGROUND:background等级表示那些用户不会察觉的任务。使用它来执行预加载,维护或是其它不需用户交互和对时间不敏感的任务。

摘自:iOS用YYLabel中方法实现异步画图ChartView


2017-03-11

一、简易音乐播放器(仿网易云音乐)

绕Z轴无限旋转动画代码:

rotationAnimation = [CABasicAnimation animationWithKeyPath:@"transform.rotation.z"];
CABasicAnimation *rotationAnimation = [CABasicAnimation animationWithKeyPath:@"transform.rotation.z"];
rotationAnimation.duration = duration;
rotationAnimation.repeatCount = INFINITY;
rotationAnimation.toValue = @(M_PI * 2);
[self.layer addAnimation:rotationAnimation forKey:@"rotationAnimation"];

继续旋转和暂停旋转代码:

//继续旋转
CFTimeInterval pausedTime = [self.layer timeOffset];
self.layer.speed = 1.0;
self.layer.timeOffset = 0.0;
self.layer.beginTime = 0.0;
CFTimeInterval timeSincePause = [self.layer convertTime:CACurrentMediaTime() fromLayer:nil] - pausedTime;
self.layer.beginTime = timeSincePause;

//暂停旋转
CFTimeInterval currTimeoffset = [self.layer convertTime:CACurrentMediaTime() fromLayer:nil];
self.layer.speed = 0.0;
self.layer.timeOffset = currTimeoffset;
  • 暂停就是将layer的速度设置为0,将timeOffset设置为暂停时动画进行的时间。
  • 继续动画就是将layer的速度设置为1,获取暂停动画时设置的timeOffset和动画的运行时间,计算并设置动画的beginTime。

二、CABasicAnimation使用总结

防止动画结束后回到初始状态

transformAnima.removedOnCompletion = NO;
transformAnima.fillMode = kCAFillModeForwards;

解释:为什么动画结束后返回原状态?
首先我们需要搞明白一点的是,layer动画运行的过程是怎样的?其实在我们给一个视图添加layer动画时,真正移动并不是我们的视图本身,而是 presentation layer 的一个缓存。动画开始时 presentation layer开始移动,原始layer隐藏,动画结束时,presentation layer从屏幕上移除,原始layer显示。这就解释了为什么我们的视图在动画结束后又回到了原来的状态,因为它根本就没动过。
这个同样也可以解释为什么在动画移动过程中,我们为何不能对其进行任何操作。
所以在我们完成layer动画之后,最好将我们的layer属性设置为我们最终状态的属性,然后将presentation layer 移除掉。


2017-03-12

一、UISlider使用总结

1.UISlider默认track的高度是2,怎么修改其高度?
重写其- (CGRect)trackRectForBounds:(CGRect)bounds方法

- (CGRect)trackRectForBounds:(CGRect)bounds
{
    return CGRectMake(0, (bounds.size.height - 2) * 0.5, bounds.size.width, 2);
}

2.UISlider默认的thumb的中心点不能滑动到UISlider的起点和终点,怎么做到像网易云音乐那样可以滑动到起点和终点?

重写其- (CGRect)thumbRectForBounds:(CGRect)bounds trackRect:(CGRect)rect value:(float)value方法:

- (CGRect)thumbRectForBounds:(CGRect)bounds trackRect:(CGRect)rect value:(float)value
{
    CGRect frame = [super thumbRectForBounds:bounds trackRect:rect value:value];
    CGFloat progress = value / (self.maximumValue - self.minimumValue);
    CGFloat pastX = progress * self.frame.size.width;
    frame.origin.x = pastX - frame.size.width * 0.5;
    return frame;
}

3.如果像网易云音乐那样拖拽UISlider的时候只更新时间,不播放对应时间点的音频。而在拖拽结束后才播放该时间点处的音频?

绑定UISlider的UIControlEventTouchUpInside|UIControlEventTouchUpOutside事件可监听拖拽结束。

sliderValueChanged:方法处更新时间。sliderTouchUp:方法处开始播放。

[_slider addTarget:self action:@selector(sliderValueChanged:) forControlEvents:UIControlEventValueChanged];
[_slider addTarget:self action:@selector(sliderTouchUp:) forControlEvents:UIControlEventTouchUpInside|UIControlEventTouchUpOutside];

你可能感兴趣的:(2017-03第二周)