NSTimer的使用
##1. NSRunLoopCommonModes和Timer
当使用NSTimer的scheduledTimerWithTimeInterval
方法时。事实上此时Timer会被加入到当前线程的Run Loop中,且模式是默认的NSDefaultRunLoopMode
。而如果当前线程就是主线程,也就是UI线程时,某些UI事件,比如UIScrollView的拖动操作,会将Run Loop切换成NSEventTrackingRunLoopMode
模式,在这个过程中,默认的NSDefaultRunLoopMode
模式中注册的事件是不会被执行的。也就是说,此时使用scheduledTimerWithTimeInterval
添加到Run Loop中的Timer就不会执行。
所以为了设置一个不被UI干扰的Timer,我们需要手动创建一个Timer,然后使用NSRunLoop的addTimer:forMode:
方法来把Timer按照指定模式加入到Run Loop中。这里使用的模式是:NSRunLoopCommonModes
,这个模式等效于NSDefaultRunLoopMode
和NSEventTrackingRunLoopMode
的结合。(参考Apple文档)
参考代码:
- (void)viewDidLoad
{
[super viewDidLoad];
NSLog(@"主线程 %@", [NSThread currentThread]);
//创建Timer
NSTimer *timer = [NSTimer timerWithTimeInterval:2.0 target:self selector:@selector(timer_callback) userInfo:nil repeats:YES];
//使用NSRunLoopCommonModes模式,把timer加入到当前Run Loop中。
[[NSRunLoop currentRunLoop] addTimer:timer forMode:NSRunLoopCommonModes];
}
//timer的回调方法
- (void)timer_callback
{
NSLog(@"Timer %@", [NSThread currentThread]);
}
输出:
主线程 <NSThread: 0x71501e0>{name = (null), num = 1}
Timer <NSThread: 0x71501e0>{name = (null), num = 1}
Timer <NSThread: 0x71501e0>{name = (null), num = 1}
Timer <NSThread: 0x71501e0>{name = (null), num = 1}
##2. NSThread和Timer
上面讲的NSRunLoopCommonModes
和Timer
中有一个问题,这个Timer本质上是在当前线程的Run Loop中循环执行的,因此Timer的回调方法不是在另一个线程的。那么怎样在真正的多线程环境下运行一个Timer呢?
可以先试试NSThread
。同上,我们还是会把Timer加到Run Loop中,只不过这个是在另一个线程中,因此我们需要手动执行Run Loop
(通过NSRunLoop的run
方法),同时注意在新的线程执行中加入@autoreleasepool
。
完整代码如下:
- (void)viewDidLoad
{
[super viewDidLoad];
NSLog(@"主线程 %@", [NSThread currentThread]);
//创建并执行新的线程
NSThread *thread = [[NSThread alloc] initWithTarget:self selector:@selector(newThread) object:nil];
[thread start];
}
- (void)newThread
{
@autoreleasepool
{
//在当前Run Loop中添加timer,模式是默认的NSDefaultRunLoopMode
[NSTimer scheduledTimerWithTimeInterval:2.0 target:self selector:@selector(timer_callback) userInfo:nil repeats:YES];
//开始执行新线程的Run Loop
[[NSRunLoop currentRunLoop] run];
}
}
//timer的回调方法
- (void)timer_callback
{
NSLog(@"Timer %@", [NSThread currentThread]);
}
输出:
主线程 <NSThread: 0x7118800>{name = (null), num = 1}
Timer <NSThread: 0x715c2e0>{name = (null), num = 3}
Timer <NSThread: 0x715c2e0>{name = (null), num = 3}
Timer <NSThread: 0x715c2e0>{name = (null), num = 3}
##3. GCD中的Timer
GCD中的Timer应该是最灵活的,而且是多线程的。GCD中的Timer是靠Dispatch Source来实现的。
因此先需要声明一个dispatch_source_t
本地变量:
@interface ViewController ()
{
dispatch_source_t _timer;
}
接着通过dispatch_source_create
函数来创建一个专门的Dispatch Source
,接着通过dispatch_source_set_timer
函数来设置Timer
的参数,注意这里的时间参数有些蛋疼。
开始时间的类型是dispatch_time_t
,最好用dispatch_time
或者dispatch_walltime
函数来创建dispatch_time_t
对象。如果需要Timer立即执行,可以传入dispatch_time(DISPATCH_TIME_NOW, 0)
。
internal
和leeway
参数分别表示Timer的间隔时间和精度。类型都是uint64_t
。间隔时间的单位竟然是纳秒。可以借助预定义的NSEC_PER_SEC
宏,比如如果间隔时间是两秒的话,那interval
参数就是:2 * NSEC_PER_SEC
。
leeway就是精度参数,代表系统可以延时的时间间隔,最高精度当然就传0。
然后通过dispatch_source_set_event_handler
函数来设置Dispatch Source
的事件回调,这里当然是使用Block了。
最后所有dispatch_source_t
创建后默认都是暂停状态的,所以必须通过dispatch_resume
函数来开始事件监听。这里就代表着开始Timer。
完整代码:
NSLog(@"主线程 %@", [NSThread currentThread]);
//间隔还是2秒
uint64_t interval = 2 * NSEC_PER_SEC;
//创建一个专门执行timer回调的GCD队列
dispatch_queue_t queue = dispatch_queue_create("my queue", 0);
//创建Timer
_timer = dispatch_source_create(DISPATCH_SOURCE_TYPE_TIMER, 0, 0, queue);
//使用dispatch_source_set_timer函数设置timer参数
dispatch_source_set_timer(_timer, dispatch_time(DISPATCH_TIME_NOW, 0), interval, 0);
//设置回调
dispatch_source_set_event_handler(_timer, ^()
{
NSLog(@"Timer %@", [NSThread currentThread]);
});
//dispatch_source默认是Suspended状态,通过dispatch_resume函数开始它
dispatch_resume(_timer);
输出:
主线程 <NSThread: 0x711fab0>{name = (null), num = 1}
Timer <NSThread: 0x713a380>{name = (null), num = 3}
Timer <NSThread: 0x713a380>{name = (null), num = 3}
Timer <NSThread: 0x713a380>{name = (null), num = 3}
我为什么讨厌产品经理
转载:我为什么讨厌产品经理
半年前的文章《我为什么讨厌程序员》最近老被拎出来,看着一些评论,开始有些脾气,然后我完整得乐坏了……
先补这一篇,我接下打算写一篇文章,来说明自己为什么智商这么低,这么二……
其实也是奇怪,多数PM看到这样的文章会乐呵呵的看完;而多数的程序员看到另一篇文章,反应却不是这样的?
##理由1,其实想做经理,而不是产品
有人在称赞世界上存在过的伟大的产品经理,同时又有很多人说,“人人都是产品经理”。
想做经理么?产品经理,可真是捷径。不管什么专业出生,不管聪明或者愚蠢,不管年轻或者年老,都可以的事情。
如果2012年12月21号,我可以祈祷某一类人消失的话,毫无疑问,我会选择应届生产品经理。别跟我探讨新牛不下地何以学耕的问题,这个跟本文无关,既然你有勇气认为自己是产品经理,就别用新人为自己找借口。
内心屈辱地承认自己的失败,才会奋起直追。
曾经看到一篇某PM对自己工作“平实”的介绍,大家觉得说得非常厚道。但全文的中心思想不过是按照BOSS的要求做事,以及机械式的做事,并且,没有赚多少钱。
丝毫看不出那种产品人应该具备的价值观、观察世界的能力、唾弃以及热爱;做平庸的产品,然后自认问心无愧,不再有所追求。即使自认还有理想,也不过是一个行将就木的人类,逐步在往更高级别的管理者行进而已。当然,他仍然可能失败。
我曾经面试过一个奇葩产品经理,他居然在考各种管理类的证书…… 奇葩地是在于,他以这样的身份工作了不少年。呃,别吐槽工资低,虽难算高薪,但那次招聘,我们的标的是5位数,接近月入一平。
##理由2,浪费纸张、键盘
虽然没有多少程序员会认真地去阅读产品文档、技术实现文档;但是,这些文档并不是最短命的。
最短命的是BRD、PRD……
求你们了,为了世界和平,省点电吧!
##理由3,满嘴跑火车
有一种PM,动动就说,你的成长空间如何如何,为了你的成长如何如何。看似苦口婆心,但他真在乎么,并且他真的有能力来照顾我的成长空间么?假道学而已。成长靠自己,成功靠勤奋,成事看天,这是常识呀。
还有一种PM,有种天然的绝杀噎死人的逻辑。
问:你觉得我们团队的核心优势是什么?
PM答:我们的优势是积累出来的……
PM还说: 给大家分享一个行业数据,……,这个数据说明,我们现在做的事情,做的市场很大呀!!
路人说: 你的数据哪来的?
PM顿而道: 内部数据,呵呵。
##理由4,强奸数据
数据是最理性。
但处理数据的过程中,有太多水分可以兑进去了。
有时产品上线了一段时间,PM给所有分享运营数据。散会后,我们经常会有这样感觉,我的智商受到了挑战。成交率低,就说下单数还可以;下单数也不灵,就取某冤大头的大笔消费,平均客单价不错哦;跳出率高的时候,死不承认过来的垃圾流量有问题,装一副单纯样,还要继续研究。
##理由5,孬种
全宇宙的SB都知道的不靠谱的某种特性,当然SB PM也知道,只要老板说要这么做;他也不反抗。然后,然后,居然扯出像模像样的原型以及各种文档。
在实施过程中,遇到阻碍的时候,他通常会说,老板的意思是这样,我也没有办法……
当然,孬种PM中还有种类型是人畜无害的,唯唯诺诺,但脾气好。所以,你真的会心疼他……
还有一件事情我无法理解的,为什么有些产品经理竟然以下跪为荣?!解释产品设计的过程,需要跪下么?需要摆这种姿态么?你是如何的弱,才需要做这样的事情?!
Q刚毕业过来的时候,也有这个毛病,觉得这样会亲和很多。我不止一次告诫他,别跪下,蹲下都不可以;话太多,说累了,拉把椅子,没椅子你直接盘坐在地毯上也好啊!
亲,跪下的产品经理,差不多都是装出来的吧!
##理由6,不懂技术还有理了?
本条吐槽无力。
刚入门的时候,人人抱着一本人人都是产品经理,这太让人厌恶了;如果人人抱着xx语言入门,才让人喜爱。如果没点聪明才智,PM能做好?如果有点聪明才智,各种语言的入门时间不过个把月甚至个把礼拜而已。我说的这个,并不是与程序员更好的沟通的前提,而是,对自己事业的尊重。
还有种纯程序员转岗的PM,呃,放过我吧,Project Manager?
##理由7,装逼扮用户
跟产品经理沟通的时候,我最多听到的句子、关键字是 用户不会这么觉得如果我是用户。
如果你是一个男人,你去设计女士内衣。我不会肤浅地认为你不懂女人。你作为一名设计师,你即是产品最大的核心。
但是我非常用心的去观察动不动就以用户为名的PM,根本没有办法把他们和产品以及用户联系在一起。他们更多的时候讨论的是这个按钮是这么颜色,应该放在哪里,文案应该怎么写。讨论这些错了么?当然没有错。错就错在,把这些问题当做了核心问题,并且即使这些交互级的问题,也经常做出令人啼笑皆非的决定来。
不知还有多少人顶着所谓的“产品经理综合症”,常常对生活中各种物品提出自己关于如何重新设计的看法,有茶杯、咖啡机、电梯、电灯、座位怎么安排 .etc, 各种你想的到想不到的问题。讨论这些错了么?当然没有错。错就错在,讨论了这么多年,他们还在讨论这些,公转自转,孜孜不倦!
##理由8,好运的混蛋
知道国内几个大平台上出产的PM,但真人,谈吐所体现出的产品人气息,常常是瞬间就冒出一个疑问,是他们成就了那些平台,还是那些平台成就了他们? 我的答案毫无疑问会选择后者。
但他们身上所体现出来的能力,是另一种非产品人的能力。他们中的多数,不得不佩服。想来想来,只有这个名词好运的混蛋能表达这种爱恨交织的感情了。
但把他们当做纯种的PM来看,呃,那这又是一条足以让人讨厌的理由了。
##最后的绝杀
其实,多数PM的工资,不如多数的程序员高。PM,是一个多么奇葩的岗位。
而且,我们还有一个必杀技,可以直接秒杀PM。你遇到的几乎任何事情,只要咆哮以下这段即可。
这不都是你产品经理应该做的事情么?!
我为什么讨厌程序员
PM与工程师的交恶,由来已久。不仅如此,视觉与工程师的交恶也由来已久。
还有些哥们之所以能游刃有余,是因为在产品实现的过程中,已经习惯了打人情牌。
很多产品或工程师朋友可能会交流这些沟通过程中,如何很好的搞定SB般的对方?
这些冲突,我认为没有讨论的必要。等活在这些冲突里的朋友们,level up后,就会脱离这样的苦海,自当能理解“没有必要”的意思。
今天,我想说的是,我为什么讨厌程序员?!
我是谁?我是Producer。
真的很讨厌么?其实不然,我非常爱工程师。按我的经验来看,多数工程师比多数PM,其实更理解用户。
不仅是如此,我的技术涉猎也比较广,django、python、mootools、html/css这些写得很溜,在不知所以的情况下,node.js、Java、PHP、jython、AS等,也照用不误。
技术是什么?程序是什么?这跟程序语言没有本质的关系,极端的说,它就是+1和-1而已。
虽然常会误解为一个纯coder,但自己程序员这种角色,坦诚说,厌恶的很。
按照自己几年前的性格,逮到垃圾程序员,能直接破口大骂。后来,发现人都好面子,不能骂了。所以,憋了很久的气,今天要倒一倒。
##理由1:装逼
他们经常会用一些技术上的关键词来说话,如果关键词的含金量很高,咱听不懂,那没有办法,术业有专攻。但一旦你理解那些所谓的术语有多么简单以及自然的时候,你心里最想说的恐怕是骂娘了。
这样的程序员,比郭小四不相上下。
##理由2:傻逼
我需要你来设计产品的实现,换句话说,就是告诉我程序打算怎么写,数据结构是什么设计的,数据流动的环节是打算如何处理的。
你会给一个方案。基本上会是最笨的办法。然后,哥还得夸你,嗯,做的不错,有进步;不夸的话,你会觉得自己的价值没有得到尊重。
用谢耳朵的话说,你在你的领域做的非常出色,但你做的事情没有任何意义。
大白话:你是一名出色的傻逼。
##理由3:冷忽悠
程序员都很木讷?都不会忽悠?
知道什么叫冷暴力忽悠么?!
我们当前的结构不支持这样的实现的⋯⋯
这个技术上是没有办法实现的⋯⋯
但按照我的经验来看,对于你无法佩服的程序员,别信他们这样的鬼话,他们不仅仅会忽悠你,他们同时也在忽悠自己,因为他们自己真是这么想的。
碰到你非常佩服的程序员,请相信我,再逼一逼他,一起探讨各种hack的方法,有些所谓的“不可能”就迎刃而解了。
真遇到了不可能,那就让它去吧。有一天,灵感会光临的。
理由4:看起来很忙,出BUG都是有原因的
会存在没有原因的bug么?!!
不过,呃,这个bug的原因不是我造成的⋯⋯
靠!
##理由5:缺乏想象力,不思进取
对程序员来说,最大的侮辱,是重构他的代码,并且效率提升了100、10000倍。
但多数程序员不会碰到这样的事情,所以还有个办法,你直接骂他在写垃圾代码就好了。
但这算是侮辱么?!
真正的coder最关心的是对方重构的方式和思维的哲学,是不是质变性的。如果有人只为了骂而骂,为了重构而重构,coder倒是要开心了,又来个傻逼。
我认为现在这个行业的从业人员实在太多,弱者也实在太多,为了照顾弱者的脆弱心灵,容忍他们贫乏的想象力,容忍他们的不思进取,同时还谎称他们人不错,很上进;才是让我们的互联网技术力量无法跟老美他们匹敌的原罪。
##理由6:不知廉耻
写垃圾代码,做垃圾的解决方式,然后自鸣得意去分享。
被斥责的时候,会说,你不尊重人,退一万步讲,我就算写垃圾代码,那也是我能力所限呀。
我最厌恶这种固步自封的家伙,简直就是不知廉耻的流氓。
Excerpted from 我为什么讨厌程序员
使用CocoaPods进行Xcode的项目依赖管理
我们来看一个Podfile文件事例
platform :ios, '5.0'
inhibit_all_warnings!
pod 'SDWebImage','~>3.2'
pod 'JASidePanels','~>1.3.1'
pod 'BlocksKit','~>1.8'
pod 'TTTAttributedLabel','~>1.7'
pod 'MBProgressHUD','~>0.6'
pod 'RTLabel','~>1.0'
pod 'KNSemiModalViewController','~>0.3'
pod 'SFHFKeychainUtils','~>0.0.1'
pod 'MBMvc',:git => 'git@github.com:alibaba/MBMvc.git'
pod 'RSA',:path => 'libs/Sources/RSA'
可以看到依赖的库不单单可以依赖官方库,还可以直接依赖某个git上的库(git=>标示)或者本地的库(path=>标示) 不过自己依赖的库需要自己写podspec文件,这个下节会详述
如上的Podfile有一个比较坑爹的事情就是 你这样写的话,只有对项目中的第一个target的起效,如果你的项目中有多个target的话怎么把依赖关系直接应用到每个target上,如下在
target :XXX_91 do
pod 'Resources',:path => 'Resources'
end
target :XXX_Weiphone do
pod 'Resources',:path => 'Resources'
end
target :XXX_Release do
pod 'Resources',:path => 'Resources'
end
target :XXX_PreRelease do
pod 'Resources',:path => 'Resources'
end
为每个target都加上一个pod依赖,那么这个target会被Pod整体的加上依赖和配置,因为依赖是会被继承的,除非特别指定:exclusive => true
详情见 http://docs.cocoapods.org/podfile.html#target
当你写好了Podfile,就可以直接执行pod install,这个时候pod就是为您建立起一个Pods项目,并且简历好一个workspace来包含Pods项目和你自己的项目,并在你的项目中直接依赖Pods项目产出的lib包
如果以后Podfile文件有变动的话,需要更新项目配置就不是执行pod install了 而是执行’pod update’,他会更新整个依赖和Pods项目..具体的依赖关系 可以直接cat Podfile.lock 来查看
##怎么编写自己的PodSpec
PodSpec是对一个Pod项目的描述,pod可以根据PodSpec文件来指导需要拉那些文件下来编译以及设置编译的参数
详细的信息可以看http://docs.cocoapods.org/specification.html
Pod::Spec.new do |s|
s.name = "MBMvc"
s.version = "1.0.0"
s.summary = "MBMvc is a Message Based MVC framework."
s.homepage = "https://github.com/alibaba/MBMvc"
s.license = { :type => 'GPL2' , :text => <<-LICENSE
(C) 2007-2013 Alibaba Group Holding Limited
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License version 2 as
published by the Free Software Foundation.
LICENSE
}
s.author = { "文通" => "wentong@taobao.com" }
s.source = { :git => "https://github.com/alibaba/MBMvc.git", :tag => "1.0.0" }
s.platform = :ios, '6.1'
s.ios.deployment_target = '4.3'
s.source_files = 'MBMvc/**/*.{h,m}'
s.public_header_files = 'MBMvc/**/*.h'
s.requires_arc = true
s.prefix_header_contents = <<-EOS
#ifdef DEBUG
#define TBMB_DEBUG
#endif
EOS
end
如上可以看到一个很简单的描述文件..他执行了需要编译的源文件和头文件 以及是否支持ARC(这个很帅,不需要再自己去设置-fno-obj-arc了)..
但是很多时候我们依赖的不是源码而是framework甚至带有Resource,这里给出一个支持直接依赖framework和Resource的例子:
Pod::Spec.new do |s|
s.name = "Huoyan"
s.version = "1.0.0"
s.summary = "Huoyan"
s.source_files = '**/*.h'
s.preserve_paths = 'huoyan.framework','TBScanLib.framework'
s.requires_arc = true
s.frameworks = 'huoyan','TBScanLib'
s.resource = "huoyan.bundle"
s.xcconfig = { 'FRAMEWORK_SEARCH_PATHS' => '"$(SRCROOT)/libs/Frameworks/Huoyan"' }
end
通过preserve_paths 留下framework文件,然后通过xcconfig来设置framework扫描路径来依赖framework
而资源文件的拷贝直接用s.resource = "huoyan.bundle"的方式 依赖
##模块化
通过PodSpec的subspec 可以使一个项目能模块化输出功能 ,一个例子:
Pod::Spec.new do |s|
s.name = "iOS_Util"
s.version = "0.10.0"
s.summary = "Some iOS Util"
s.license = 'MIT'
s.author = { "文通" => "wentong@taobao.com" }
s.platform = :ios, '6.1'
s.ios.deployment_target = '4.3'
s.subspec 'Common' do |cos|
cos.source_files = 'iOS_Util/Common/*.{h,m}'
cos.public_header_files = 'iOS_Util/Common/*.h'
end
s.subspec 'Core' do |cs|
cs.source_files = 'iOS_Util/Core/*.{h,m}'
cs.public_header_files = 'iOS_Util/Core/*.h'
cs.dependency 'libextobjc', '0.2.5'
end
s.subspec 'Json' do |js|
js.source_files = 'iOS_Util/Json/*.{h,m}'
js.public_header_files = 'iOS_Util/Json/*.h'
js.dependency 'iOS_Util/Core'
end
s.subspec 'Bean' do |bs|
bs.source_files = 'iOS_Util/Bean/*.{h,m}'
bs.public_header_files = 'iOS_Util/Bean/*.h'
bs.dependency 'iOS_Util/Core'
end
s.subspec 'DB' do |ds|
ds.source_files = 'iOS_Util/DB/*.{h,m}'
ds.public_header_files = 'iOS_Util/DB/*.h'
ds.dependency 'FMDB/standard' ,'~> 2.1'
ds.dependency 'iOS_Util/Common'
ds.dependency 'iOS_Util/Core'
end
s.subspec 'WebP' do |ws|
ws.source_files = 'iOS_Util/WebP/*.{h,m}'
ws.public_header_files = 'iOS_Util/WebP/*.h'
ws.dependency 'libwebp' ,'~> 0.3.0-rc7'
ws.frameworks = 'CoreGraphics'
end
s.subspec 'Location' do |ls|
ls.source_files = 'iOS_Util/Location/*.{h,m}'
ls.public_header_files = 'iOS_Util/Location/*.h'
ls.dependency 'iOS_Util/Common'
ls.dependency 'iOS_Util/DB'
ls.frameworks = 'CoreLocation' ,'MapKit'
ls.resource = 'iOS_Util/Location/chinaDivision.sqlite'
end
s.subspec 'AMR' do |as|
as.source_files = 'iOS_Util/AMR/**/*.{h,m,mm}'
as.public_header_files = 'iOS_Util/AMR/**/*.h'
as.preserve_paths = "iOS_Util/AMR/**"
as.library = 'opencore-amrnb','opencore-amrwb'
as.xcconfig = { 'LIBRARY_SEARCH_PATHS' => '"$(PODS_ROOT)/iOS_Util/iOS_Util/AMR/lib"' }
end
s.subspec 'Cache' do |cas|
cas.source_files = 'iOS_Util/Cache/*.{h,m,mm}'
cas.public_header_files = 'iOS_Util/Cache/*.h'
cas.dependency 'iOS_Util/Common'
end
s.subspec 'Preference' do |ps|
ps.source_files = 'iOS_Util/Preference/*.{h,m,mm}'
ps.public_header_files = 'iOS_Util/Preference/*.h'
ps.dependency 'iOS_Util/Json'
end
s.requires_arc = true
end
可以看到通过subspec可以区分出不同的模块,而且模块间也能依赖
而在其他项目要用到的时候在Podfile里面可以
pod 'iOS_Util/Json',:git => 'git@gitlab.alibaba-inc.com:tbw/ios_util.git'
这样的方式来直接依赖其中一个模块的代码,而其他模块的代码不会被打包进来
参考
cocoapods添加github上自制类库_2_添加subspec
使用CocoaPods进行Xcode的项目依赖管理
Copyright © 2015 Powered by MWeb, 豫ICP备09002885号-5