1
2
3
4
5
6
7
8
上古中原大修罗立世

生当封侯兮,死当庙食。
来时乘龙兮,归息莲花。

饮气嗅芳兮,对夜月以清光。
守静长生兮,行神通游千古。 
住心悠然兮,如闻日月浩大。 

最近始于读张居正生平,被郦波教授的讲课吸引住了。又重复听了《百家讲坛—大明名臣》系列中郦波教授对戚继光的解读。通过郦波教授的诠释,对戚继光耀眼千古的军事才华吸引的沸腾而念念不忘。

利刃治军用术以荡平倭寇,创新超前铁甲阵势以碾压蒙古铁骑。如此不世出的军事奇才,如此感怀『但愿海波平』的大修罗,相信孔明和司马懿都会为之震撼。神奇的历史人物在其一生中,总有不少令后人值得留恋的精彩落笔。特别戚继光处在,倭寇杀戮成性,屠戮淫虐沿海妇孺百姓的倭患危国的时代。在对人物的心绪平静过后,平和地回想戚继光有几点非常精彩。

往来之,乘龙以御雨

公元1528年,11月份山东蓬莱连绵下了十几天的连绵秋雨,直至戚继光半夜响亮的落地嘀哭声起,连绵的秋雨才停止,继而晨光朝霞在东方升起。这或许有巧合之处,但是这个事件确也是经考证的确属实的。如果从往后戚继光功绩再来看待当初出生的这个小娃娃,那么我们更相信只有乘龙而来的戚继光,才有如此超凡脱颖的智慧和才干。而当他万历15年落寞归去时,雷雨夜又再度袭来,这不能不叫人对其来去风雨又护国于风雨之感慨。

坚念不息,乘文以武用极

虽然作为身居世袭爵位的官二代,但戚继光从小得益于父亲戚景通立身立世的熏染,从小持心坚稳不怠,文武皆出类拔萃。更不畏惧众人的看法,勇敢挑战武举人的考试。历史确实也会挑人挑时间,戚继光凭借《备俺答策》在庚戌之变中,有着惊人的表现机会。他的策论文《备俺答策》的庚戌之变中广为传播。如此一位金光闪闪的新星,有了绝佳的机会冉冉而升起。

数度沉稳有序,善藏时于器

不过历史和人事,可没有理想中那么美好顺利。戚继光在庚戌之变中精彩的表现,成为了兵部重点培养对象,但却让他在蓟辽前线巡边中暗暗被冷落了三年。但是戚继光拥有乐观豁达的态度,如他所写的”南北驱驰报主情,江花边草笑平生”,让今人都能感受到他疲乏劳累之时,亦有股不忘赏花秋月的精气神。在蓟辽前线巡边潜水三年不忘研读兵法,然后回到山东整肃军务略有成效,接着又再被调往浙江做后勤调度,再度被潜水。

但是在浙江潜水中,戚继光时刻不忘抓住手头上的空挡,坚持做文章做调查报告,对抗倭情势进行分析提交给上级。又是持续不怠的磨砺好一段时间,胡宗宪虽然看到了戚继光的报告,但是还是故意冷落测试他是否可堪当大任。果不其然,再到了一个历史岗位档口—宁绍台参军。令谁也没有想到,当时的浙江巡抚胡宗宪(后来的直浙总督)破格青睐了戚继光。十年藏时而磨器,戚继光由此才开始登上历史的军事舞台。

文谋武攻,覆手间克敌以制胜

首战龙山所,4000对800倭寇,即便倭寇进入了埋伏圈,因4000明军战斗羸弱竟然不敌800倭寇。一战龙山所4000对800,二战雁门岭2万对两千,两次都是以多打少却转胜为败的窝囊实战,这也是个契机让戚继光认真地反省明军的军纪和战力,这后面就有了组建戚家军的大胆措施,庆幸胡宗宪这个伯乐为戚继光多次放开绿灯。凭着戚继光数十年不息磨剑,研习兵法天术的好学干劲。 在被绍兴兵的老练精明所打击而失望后,戚继光从零起步练就出了一只独具派头的3000义乌兵(戚家军雏形),结合他大胆突破地在兵器的创新和阵型创新(鸳鸯阵变两仪阵三才阵)。戚继光手上这才有了一只匹配他军事才能的铁军。

  1. 戚家军第一次作战,宁海遭遇战==>斩杀300倭寇,却无一阵亡。这对于当时窝囊环境下,4000正规明军不敌800倭寇的,戚继光这一仗着实创造出了奇迹。
  2. 戚家军长途奔袭,怒战花街,虽然以远打近,以饿打饱,以少打多==>但歼敌1000多人,仅仅付出3个人的阵亡代价,再一次印证戚家军神奇般的存在。
  3. 伏击战上峰岭,再次以少胜多,虽然确切歼敌数量未统计出,但是还是仅付出了3个人阵亡代价。

这次戚家军首次出鞘,13战13捷的台州大捷。这也是戚家军戚继光横扫天下,立名千古的开始。而这时的戚家军仅3000人。台州大捷之后,倭寇再不敢到江浙一带惹戚老虎,转移蹂躏目标到了福建沿海。当然,戚继光在扩充戚家军到6000人后,再次利剑出鞘支援福建勇闯夺命岛(横屿岛),三次出鞘荡平广东南澳岛倭寇,终于终止了百年祸害的倭寇之乱。这一系列过程处处用兵奇巧得当而又精彩绝伦,自此平息了海疆。

从进京考武举人到『但愿海波平』的过程中,戚继光有两个很有趣的谈话。足以窥探戚继光立心立世的独特与超然。

第一个故事是:

一天,戚继光路过一座古庙里,篝火正鲜红地跳动着。几个僧道模样的人正高声说话。他进去歇了一歇,正起身要走,一个僧人叫住了他:“这位将军,看你一身戎装器宇不凡,可否谈谈你的长生之道啊?”他淡然一笑:“身为司命,义在死绥。方求致身殉国,以帅士志。而乃师人以学长生,是可以训乎?流行坎止,属之彼苍,鞠躬尽瘁,夕死何憾?此将门长生之术也。”

总结为一句话就是,将门之士,志在护国安邦,不惜身命,以殉死而为生,此即是将门长生之道。


第二个故事是:

赶赴福建支援中,有个书生前来拜见戚继光并大谈他的带兵动静之道。而戚继光对静的理解程度却远超书生。戚继光说,比如我在战场上,战场的情况是瞬息万变,但是我只知杀敌。我要是杂念一起,必定会张皇失措,肯定是不败自乱。我是这样,我的士兵也是这样,所以我们平常训练讲究什么呢?奋不顾身,勇敢卓绝,虽然行动如风,但是这一刻心静如恒。这就是我们军人的动静之道。他的意思说,只有人在极其专心的情况下,才能真正进入静的境界。所谓真正的宁静,是心灵的淡定和专一。这真是静的最高境界。听完之后,书生自感惭愧匆忙辞去。

站在今天看来戚继光的见地仍然出奇精辟。戚继光对静的理解就是,虽然行事紧凑,雷厉风行,但用心为一,这比一般静坐却思念飘动更可称为静。所谓静者,一事一用一念。亦相当于,无事则无念无欲。


在东南沿海平息百年倭寇之乱之后,戚继光深得张居正器重,北上驻守蓟辽前线。而后戚继光继续发挥他好学创新的突破力,以古今结合的思路,将战国战车与新火炮结合,以八千兵力组成了铁车阵(可称最早的装甲部队)。凭借这个铁车阵,每次防守大仗,都是以少胜多大败全歼蒙古数万铁骑,吓得蒙古兵谈虎色变数年不敢谈虎(戚),戚继光驻守16年,蒙古军再没有不可一世的嚣张气焰。

北驻长城中,史书称戚继光数战皆震慑环宇,功比秦汉唐各风流大将。细细比对,戚继光的战术思维已经数度超前了那个时代,其中已隐含了现代高维武器打击低维血肉刀兵的思想。故而精彩绝伦,令后人称赞不已。

将陨愁绪流,所谓大人愿化太一

每一个历史人物的冒起也是得益于那个时代和时期的风气和大人物的喜好。胡宗宪将戚继光拉上了历史舞台,张居正提供机会给戚继光练就奇迹一般的功绩。但是大明最终极的老板多数却都是混混噩噩的坐吃山空的主,少有功德千秋的君王。

这也是起起伏伏的历史兴亡的叹息,但 戚继光『但愿海波平』的一生宏愿已然落地,也护佑了沿海数方数代的百姓。又震慑了北掳杀人劫掠屠城的嚣张气焰。所谓中原大人生而来去之宿愿,就是筑基平天下。他已然实现可不枉此生了吧。

大人的丹青灵气,万古辐射不息。

进入春日惊蛰以来,感觉体内能量涌动,而广州又是连绵雨天,不能去天河公园跑步,让人很是不痛快。偶然发现悟空遥控器把百家讲坛集成得很完整,浏览之中发现『大明名臣—张居正』系列很长且点题很有特点。 于是一口气几个小时看完了前面几集,睡前有股惆怅和感慨,半夜入睡竟然梦见张居正和他的小伙伴们,体会到了张居正左右逢源地巧妙连接人情的苦涩和奸魅。沉浸其中,貌似沐浴于当事大人物的光芒心境之中。

而后慢慢浏览《张居正大传》,结合郦波教授的百家讲坛系列。简要地把张居正的整个生平概括成五步。

少年国士,文以策见长。

张居正少年时,除了文气逼人、灵气出众,还有的就是政治心胸策略天然地高明。张居正少时也是自负才干出类拔萃,故写有『凤毛丛劲节,直上尽头竿』。而当时的湖广总督顾璘故意在乡试中将张居正名落孙山,而后有直接跟他表明就是不录用他。但是面对突如其来的冷水遭遇,张居正在这个年纪中表现出异常的冷静和理解能力,即便正常心智的大人也未必能够如此淡定地理解如此严重的打击,而张居正就是有如此天然沉稳的理解力。其策甚渊,其力甚笃,出于常人承受范围。

持重而圆柔,与异者交情。

明朝的党争是很惨烈的,从严嵩,到徐阶,再到高拱,张居正皆能够以情谊交之。这三个人相互之间都是水火不容,张居正站位于徐阶恩师之下,确能够巧妙圆柔于严嵩和高拱之间,其中既有交际手腕原因又不缺乏真正的朋友和情义。例如,即便严嵩这个奸佞之徒,举国痛恨之,晚年不得善终,张居正依然坚持将他埋葬入土为安。高拱品性刚烈不饶人,张居正依然能够和他和谐相处,甚至成为高拱难得的知心人。与异者交情,最难得的就是和大太监冯保联结成联盟,太监这种半人,在士大夫眼中地位是不堪的,但是张居正却真的做到了,持自己之气正,而圆柔结交于异类,这种宽大的『容错』心理,正是宰相气度的体现。

奇策徐徐用之,破立旧利益结构。

考成法,这就相当于针对现代大企业或国企患有严重的大企业病,推出的绩效考核标准。对于当时的处境,这又是供宰相抓住的百官的政治小辫子。起到为之后的政策铺垫的巧妙用处,之后的一条鞭法得以徐徐推进,也是得益于连续政策的巧妙布局和忍心的徐徐牵制制衡。

修齐治平,举世非之,用心苦涩。

张居正的老爸虽然是个顽童的性格,但是张居正十几年没能够回家看望父亲,这里面的苦涩遗憾还有愧疚是外人无法体会的。万里皇帝和太后的夺情留任张居正,士大夫群体群起而攻之,批判张居正不守制丁忧之正统礼法,甚至不惜受刑掉肉致残地反对张居正的夺情留任。而这里面的忠于国家与孝顺父亲的无奈,竟然一度让张居正濒临崩溃边缘,真是应了庄子理想中的高渺处境,“举世誉之而不加劝,举世非之而不加沮,定乎内外之分,辩乎荣辱之境,斯已矣。”

激流勇退难,留下身后劫。

激流勇退是智慧之举,张居正后面却在这点留下了遗憾。也遗憾在他所看中的万历明君,却也不过是一位庸碌贪婪淫逸的皇帝。之后更是带来近半个世纪张家的劫难。让人唏嘘不已。

印象笔记格式版 https://www.evernote.com/l/AG5VNoZAikZB9JURWgasfkp5GJAbnSknTdk

上周和两位同事讨论到block使用场景中哪种会发生retain cycle。一位是认为场景A会发生retain cycle,一位是认为场景A可能会发生retain cycle,最好采用全部weakSelf方式来编码,确保无遗漏不使之产生retai cycle。(场景A见下图)

A、基础知识点:

  1. Blocks are a C language extension for creating anonymous functions.

  2. The initial allocation is done on the stack, but the runtime provides a Block_copy function which, given a block pointer, either copies the underlying block object to the heap, setting its reference count to 1 and returning the new block pointer, or (if the block object is already on the heap) increases its reference count by 1. The paired function isBlock_release, which decreases the reference count by 1 and destroys the object if the count reaches zero and is on the heap.
  3. It does not provide a cycle collector; users must explicitly manage the lifetime of their objects, breaking cycles manually or with weak or unsafe references. 『MRC和ARC中都没有引用环回收器,所以ARC中,得更留意retain cycle发生场景』
  4. 根据Block在内存中的位置分为三种类型NSGlobalBlock,NSStackBlock, NSMallocBlock。
    • a、NSGlobalBlock:类似函数,位于text段(《obj-c高级编程》一书又说位于data段,但实质上两种都没关系);
    • b、NSStackBlock:位于栈内存,函数返回后Block将无效;
    • c、NSMallocBlock:位于堆内存(在heap内存区域,这是block和obj发生retain cycle的关键点)。以内存管理的理解方式,则相应说明三点,
      • a1、『NSGlobalBlock:retain、copy、release操作都无效;』;
      • b1、『NSStackBlock:retain、release操作无效。必须注意的是,NSStackBlock在函数返回后,Block内存将被回收。即使retain也没用。需要警惕的是,[[mutableAarry addObject:stackBlock],在函数出栈后,从mutableAarry中取到的stackBlock已经被回收,变成了野指针。正确的做法是先将stackBlock copy到堆上,成为NSMallocBlock类型对象再操作』;
      • c3、『NSMallocBlock:支持retain、release,也是本文重点关注的block类型』)。 Block对不同类型的变量(基本类型)的(截获)存取:a、局部自动变量,在Block中只读(当做常量使用)。b、static变量、全局变量。Block中可以对他进行读写。因为全局变量或静态变量在内存中的地址是固定的。c、Block变量,被__block修饰的变量称作Block变量,基本类型的Block变量等效于全局变量、或静态变量。
  5. BlockA被另一个BlockB使用时,另一个BlockB被copy到堆上时,被使用的BlockA也会被copy。但作为参数的BlockA是不会发生copy的。(作为参数传递的blk是不会被copy,也就是不会被强引用)
  6. MRC中__block是不会引起retain;但在ARC中__block则会引起retain。ARC中应该使用__weak或__unsafe_unretained弱引用。
  7. 一个常见错误使用是,开发者担心retain cycle错误的使用__weak。比如将Block作为参数传给dispatch_async时,系统会将Block拷贝到堆上(GCD把block当参数时,block会被copy到heap上成MalloBlock),如果Block中使用了实例变量,还将retain self,因为dispatch_async并不知道self会在什么时候被释放,为了确保系统调度执行Block中的任务时self没有被意外释放掉,dispatch_async必须自己retain一次self,任务完成后再release self。但这里(瞎担心retain cycle发生)故使用__weak,使dispatch_async没有增加self的引用计数,这使得在系统在调度执行Block之前,self可能已被销毁,但系统并不知道这个情况,导致Block被调度执行时self已经被释放导致crash。 * {试验结果:__weak 修饰的self在异步block回来后已经被释放了,所以确实是无法执行block中self相关操作。但是此时self的地址已经被设置nil,不会造成crash。造成crash的情况,有可能是MRC下用__block修饰,或者其它复杂情况下的ARC。试验中,一个特别现象是,如果blockA最后是self的强引用,如果此时GCD切换入一个新的blockB,那将直接接触对self的强引用,那么GCD_blockB回来后,后面如果有self相关调用将是无效的。}
  8. 可以用dealloc方法来管理一些资源,但不能用来释放实例变量,也不能在dealloc方法里面去掉[super dealloc]方法,在ARC下父类的dealloc同样由编译器来自动完成。(debug方式:可以使用dealloc来检测obj是否在预期中被释放,用chisel在极端情况下检测确切内存地址中是否还存在obj)

B、关键知识点:

  1. 核心点是应指明,对象间是哪种引用类型。是强引用,发生了retain count 加1;还是弱引用,未对retain count做操作。
  2. 两个obj间的retain cycle很容易看出来,(强引用符号===>,弱引用符号- - - >)。就像: ===>objA(self)===>blkB===>objA(self) 三个或多个obj(block)间的retain cycle最容易出现遗漏,三个就像: ===>objA(self)===>objB===>blockC===>objA(self);五个就像:===>objA(self)===>objB===>blockC===>objD===>blockE===>objA(self);
  3. MRC中__block是不会引起retain;但在ARC中__block则会引起retain。ARC中应该使用__weak或__unsafe_unretained弱引用。
  4. block的类型,取决于是否截获(快照)自动变量:globalBlock不依赖于执行时的的状态,所以整个程序中只需一个实例。arc情况下一般都是stackBlock类型便于执行完立即回收,如果有特别需要,可以将stackBlock拷贝到heap上转成mallocBlock再进行使用。(《obj-c高级编程》一书在page 111~112,)
  5. block传递时相应产生的类型:
    • a、作为参数的Block是不会发生copy的。
    • b、将block作为函数返回值时,编译器会自动生成复制到堆上的代码(即转成mallocBlock)。(这里可以看出设计原则为:Block在未来需要使用时将放入heap,只需用一次的放到stack中) mallocBlock常见情况和需要转成mallocBlock情况:stackBlock时,以下方法或函数不用手动复制(即转成mallocBlock),a、cocoa框架方法中函数usingBlock的;b、GCD 的api ,将block做参数(mallocBlock)。需要手动复制成mallocBlock场景:『相反的,在NSArray类的initWithObjects实例方法上传递Block时需要手动复制』《objc-c高级编程》

psNote: 以上2、3点中红色的===>,就是引起retain cycle的强引用,应该改成 - - - > 弱引用,使用__weak符号修饰。 强引用符号===>,弱引用符号- - - >,无引用 ~ ~ ~>

场景试验

以下是几种场景的检验。


试验一:场景A,静态方法传参block (去除图中@weak标识符)

引用流程是:

===>self~ ~ ~>static_method~ ~ ~>blk_callback(stackBlock)[http返回后,以blk_callback(mallocBlock)回调]<==>blk_callback(mallocBlock)===>self

试验一两个关键点,self不会在第一步强引用blk_callback(stackBlock);潜在的闭环不成立。 回调的block_callbank(stackBlock转成mallocBlock) ,会对self强引用一次,等待block执行完后方式对self的强引用。

这里合适的处理策略,应该让block_callback 强引用self,避免block_callback回调时,self成了野指针或nil值。


试验二:场景B,典型的三元retain cycle (去除图中__weak标识符)

引用流程是:

===>self===>vc_obj===>vc_tapBlock===>self

这是典型的三元引用环场景。应该使用 __weak修饰符后的 weakSelf,将vc_tapBlock对self的引用设置为弱引用。


试验三:场景C,还是典型的三元retain cycle (去除图中__weak标识符)

引用流程:

===>self===>_mcs_notiObj===>bulk_notiCallback===> self

同上,试验二。


试验四:场景D,GCD函数传参block

引用流程:

===>self~~~>GCD_blkPara(mallocBlock)===>self

self不存在持有dispatch_async的block参数的可能,因此需要block_gcd_para强引用self,当block回调时,保证self还未释放。这个用法正确,相反若使用weakSelf则是不对的。


试验五:cocoa中基础obj使用usingBlock

引用流程:

===>self===>arrObj===>block_usingBlock===>self

由于self对arrObj的强引用是初始引用,无法weak操作,所以只能(必须)在最后一步将block_usingBlock对self的强引用设置成weakSelf;


试验六:业务多重嵌套,block和GCD深度强引用

从试验一到试验五,我们其实可以归纳出引用环retain cycle的关键点,明确第一步是哪种引用(strong还是weak),如果是weak那么肯定不存在retain cycle放心使用;如果是strong引用,那么最后一步是否使用到self,如果使用必须采用weakSelf方式;

所以,再复杂的实际嵌套业务也不会出现模糊无法判断的情况。

简要总结

关键是检查第一步:确认self是否强引用了block,最容易出现的情况是持有实例obj中自定义block; 检查最后一步block是否强引用self:如果第一步是强引用;

Result Note :

  1. 场景A,是不会产生retain cycle;场景B是典型的三角引用环;
  2. 全部weakSelf的方式,看似多一步确保不发生retain cycle,但是违背了Cocoa(MRC、ARC)对block内存管理的初衷,严重的一方面会产生致命crash(如上A-8),或者无法执行相应任务块。不理性地一方面就像担心程序处处bug,所以处处都@try exception,增加了性能负担;
  3. 《obj-c高级编程》并没有推荐将stackBlock转成mallocBlock再使用。而是在特殊需求下,可以在stackBlock特点不能满足时,将其转成mallocBlock再使用,说明了转化的方式和技巧。
  4. 《obj-c高级编程》书重点点名,两种Block是不应转成mallocBlock的(本身就是,而且不应该去管理到这区域的block)。如上B-7点的,这两种为a、cocoa框架方法中函数usingBlock的;b、GCD 的api ,将block做参数(mallocBlock)。

参考资料 :

  1. http://clang.llvm.org/docs/AutomaticReferenceCounting.html 2.《objective-c高级编程 iOS和Mac OX多线程和内存管理》
  2. http://tanqisen.github.io/blog/2013/04/19/gcd-block-cycle-retain/

工具使用:

  • 对象地址检测工具:LLDB 的facebook增强版 chisel,Chisel is a collection of LLDB commands to assist in the debugging of iOS apps.https://github.com/facebook/chisel

概念:

**retain cycle: **retain cycle,即『强引用环』,表现为两个或多个obj(blk)相互强引用导致相互无法释放,最后成了内存中的孤岛,是内存泄露的一种典型情况。实质的逻辑,就类似于线程死锁,数据互持。