关于objective-c:初探SwiftUI打通任督二脉

序言开年的第一篇文章,明天分享的是SwiftUI,SwiftUI进去好几年,之前始终没学习,所以当初才开始;如果大家还留在 iOS 开发,这们语言也是一个趋势; 目前待业中.... 不得不说已逝的2023年,大家开始都抱着一解封,经济都会向上转好,可是事实不是咱们设想那样;目前我也在学习 SwiftUI,并且致力找工作中....;至于 2024 年经济如何,咱们作为老百姓在大环境和寰球经济影响下;坦然面对,晋升本人。 这里不得不说国人百折不挠的精力。“卷” -- 致力吧Coding人 SwiftUI体验Xcode创立我的项目之后呈现工程默认创立的UI界面;如下 一开始心里对本人说:"SwiftUI作为iOS开发新的UI体系,为啥初创的我的项目这么多代码,给初学者看到,一种压迫感,心想这语法好简单,不想学了";不论你是不是这样心里,我刚开始看见,这么一坨代码,没什么心理,于是索性删掉;按本人能了解学习的形式来操作;于是做了简化: import SwiftUIimport SwiftDatastruct ContentView: View { var body: some View { Text("hello,word”) }}#Preview { ContentView() .modelContainer(for: Item.self, inMemory: true)}关键字 some关键字some啥玩意儿,齐全生疏;先看看View;点击进入源码构造查看: @available(iOS 13.0, macOS 10.15, tvOS 13.0, watchOS 6.0, *)public protocol View { /// The type of view representing the body of this view. /// /// When you create a custom view, Swift infers this type from your /// implementation of the required ``View/body-swift.property`` property. associatedtype Body : View @ViewBuilder @MainActor var body: Self.Body { get }}一堆英文注解预计大家不喜爱看,我就没贴出来了;简略来说:View 是一个泛型协定,它定义了所有视图类型须要遵循的接口,通过some润饰;示意 "我返回一个满足View 协定的某种类型"。some关键字通知 Swift,尽管咱们晓得body必须返回一个View,但咱们不确定具体是哪种 View(例如,Text, Image, VStack 等)。 ...

February 28, 2024 · 4 min · jiezi

关于objective-c:心遇iOS端会话页性能优化-ReactiveObjC实践篇

本文作者:尚尧一、背景心遇作为一款社交产品,音讯会话页必然是用户使用量最大的页面之一,因此会话页的用户体验将尤为重要。同时,心遇有着陌生人社交属性,用户的会话量动辄上万,会话页也面临着较大的性能挑战。因而,会话页的性能优化既是重点,也是难点。 本文将举例会话页已知的性能问题,剖析实现弊病,最初通过引入 ReactiveObjC 来更优雅的解决问题。 二、 ReactiveObjC 简介ReactiveObjC 是一个基于响应式编程 (Reactive Programming) 范式的开源框架了,它联合了函数式编程、观察者模式、事件流解决等多种编程思维,从而让开发者更加高效地解决异步事件和数据流。其外围思路是将事件形象成一个个信号,再依据需要对信号进行组合操作,最初订阅解决信号。通过应用 ReactiveObjC ,写法上由命令式改为申明式,使得代码的逻辑变得更紧凑清晰。 三、实际场景一:会话数据源解决存在的问题问题剖析心遇会话页如图所示: 会话页的数据源来源于 DataSource 。DataSource 保护着一个有序的会话数组,外部监听着各种事件,比方会话更新、会话草稿更新、置顶会话变更等等。当触发事件后, DataSource 可能会从新绑定会话外显音讯、过滤、排序会话数组,最初告诉最上层业务侧刷新页面。结构图如下: 局部实现代码如下: // 会话变更的IM回调- (void)didUpdateRecentSession:(NIMRecentSession *)recentSession { // 更新会话的外显音讯 [recentSession updateLastMessage]; // 过滤非本人家族的会话 [self filterFamilyRecentSession]; // 从新排序 [self customSortRecentSessions]; // 告诉观察者数据变更 [self dispatchObservers];}// 置顶数据变更- (void)stickTopInfoDidUpdate:(NSArray *)infos { self.stickTopInfos = infos; [self customSortRecentSessions]; [self dispatchObservers];}// 草稿箱变更- (void)dartDidUpdate { [self customSortRecentSessions]; [self dispatchObservers];}// 家族数据变更- (void)familyInfoDidUpdate { [self filterFamilyRecentSession]; [self customSortRecentSessions]; [self dispatchObservers];}这里须要解释的是 [recentSession updateLastMessage] 的调用。因为心遇的业务须要,局部音讯是不须要外显到会话页的。因而当收到一条新音讯时,须要从新更新该会话的外显音讯。外显音讯的更新逻辑如下: ...

May 4, 2023 · 2 min · jiezi

关于objective-c:富豪谷底求生纪录片记录

昨天看到知乎推这个主题,十分感兴趣。遂花了一天的工夫把选集看完了(B站有资源),把从中总结到的货色记录了下来。 一共32条,没工夫看全片的同学能够看看上面的总结。 总之我是筹备把这32条背下来了。 ————以下是总结注释———— 1、赚大钱比拟容易,只赚个几百几千,是比拟难的; 2、人一慌乱就容易太早放弃,错失很多机会; 3、建设人脉,建设人脉到底有什么用? (1)货源,卖;(2)能够理解他们为啥做xx生意;(3)找到市场需求;(4)须要人力的时候能够找他们帮忙。4、很多亿万大佬都是从销售做起的,不要瞧不起干销售的 5、要对贵的货色相熟,比如说跑车; 6、有想法要开始口头起来,冷空气并不能阻挡什么; 7、游戏能够当放松的时候玩,不能当花工夫在下面。次要的工夫还应该是求提高; 8、多和富人接触,看他们的纪录片; 9、先找市场,而后找产品去满足 10、五个秘诀 (1)一天的开始要头脑清醒,纵观大局,有喧扰的空间能力成事;(2)先做艰难的事;(3)必然准时赴约——第一印象十分重要;(4)找一个导师,从他们的谬误中吸取教训;(5)绝不投机取巧,否则会造成顾客不满。11、找到一个行业,入行容易不容易被同行打压的 12、明确方向之后,就是要招集能补救本身毛病的人才 13、开始守业的第一步 (1)你要确定理解这一行所有眉角:办法就是真正深刻去这个行业开始做货色(2)能胜利的办法是身边围绕的都是比本人聪慧的人(3)让团队的人置信你,如果他们不置信你,那么你什么都做不成;(4)要打造品牌,他人一看到某个中央,就想到某个产品14、永远别让人看到你缓和;你要放弃沉着,把握住底线 15、永远不要得意洋洋,不因挫折低沉,然而小的胜利十分重要。 16、守业胜利的一点是找到各行业的专家 17、练习谈话,要读书、演讲,有煽动性。 18、治理团队时激励一个人不须要当面和他谈激励他,只有散会时把优秀员工的体现进去,看看他的反馈就行了,如果反馈不好,那可能就是找错人了。 19、领导者有不同的风格 (1)也就是心愿员工感觉是我为他们做事,把想法反馈给他;(2)"我就晓得设个时限,让你自由发挥会胜利的"20、要乐观,也要思考事件的结果:每次都感觉本人要失败了 21、化敌为友是最好的办法; 22、做leader有时候有些业余货色不是很理解,然而也肯定要硬气的说,说完之后再转给上司来说。 23、要找好的员工,如果没有适宜的职位,能够为他发明一个职位; 24、删掉过多的音讯,分明阐明需要,从头到尾把握要点; 25、做领导不是为了给团队压力,而是为了让他们明确,只有把事件干成了,团队每个人的付出才有意义; 26、致力的时候也要打造本人的品牌; 27、不要坐等良机落入手中,没有那么好的事; 28、举例:如果有一个员工给你汇报工作的时候没有实现,那你作为领导者应该怎么办? (1)回复:让你来负责这部分内容,代表你就是领导者而不是员工,作为领导者要积极主动。你没有问我想要什么样的格调,没有问我进度如何,没有用狠劲来问我到底该怎么做,而是坐等我的安顿。我卖房子、找烧烤师、找店铺,有千百万事件要做,所以事关你的工作的,你要全权处理。你想要胜利就要当个斗牛犬,这是不二法门。我敢保障百分百被动的人不会胜利。要么回公司下班,要么拿出狠劲来。这是你该做的事。(2)形象模板 【1】表明员工的重要性;【2】说我作为领导者有多忙;【3】形象鸡汤教诲;【4】最初说下冀望的后果,也就是心愿胜利。<<<< 领导的形象能力要很强。29、要多问有意义的问题,最简略的办法就是问形象的问题 30、口头前要做好查看; 31、再难再缓和都要放弃微笑,你能够向员工说本人很缓和,然而肯定要放弃笑容。语言表达进去即可,不要用表情来裸露你的心田。举个例子:你在很缓和的时候给员工说当初的状况,如果100米之外听不到你谈话的人也从你的表情中看出你很慌乱,和平时不一样,那么这就不是合格的表情治理。 32、不要发脾气,这个领导不发脾气,也不让员工发脾气; (1)原话是:局面难看的时候,不要随之起舞,压力下能不能放弃沉着正式业余与否的差异。

April 10, 2022 · 1 min · jiezi

关于objective-c:经济机器如何运转笔记

视频链接:【Ray Dalio】三十分钟看懂经济机器如何运行(比尔盖茨举荐) 影响经济的三要素 生产率的进步短期债权周期长期债务周期了解交易每次交易中:买方应用 货币/信用 向卖方获取 商品、服务或股票资产 收入总额 = 货币 + 信用 收入总额是经济的驱动力 价格 = 收入总额 / 销量 所有的经济周期和能源都是交易造成的。 市场收入总额 / 产销总量 ,就失去了理解经济市场的全副信息 信贷是经济中最重要的组成部分。 信贷产生债权,债权是贷款人的资产,是借款人的负债。 为什么借贷如此重要?因为借款人获取信贷后,能够减少收入,收入是一个经济的驱动力,一个人的收入就是另一个人的支出。 因为一个人的收入是另外一个人的支出,当一个人的支出减少时,银行违心贷更多的钱给借款人。 也正是因为如此,才产生了经济周期。 生产率在长期内最要害,但信贷在短期内最重要。 债权能够让购买时,生产高于产出,但在还债时不得不让 生产低于产出。 债权稳定有两大周期: 小周期:5-8年大周期:75-100年 如果没有信贷的生存是怎么样的? 没有信贷时,收入等于支出,如果想收入更多,那么就须要进步生产率,进行翻新或者进步工作量。 信贷的实质是向将来的本人借钱,将来你的收入肯定要小于支出,以便偿还债务,于是便人为的制作了一个周期: 现实生活中,大部分的钱实际上就是信贷,美国国内的信贷总额大概为50万亿美元,而货币总额只有大概3万亿美元。 短期债权周期信贷容易取得,大家就会取得更多的钱,但社会产出是肯定的,就会导致通用收缩。 反之就会导致通货紧缩: 长期债务周期消退能够通过降低利率来调节,但去杠杆化无奈应用降低利率调节,因为利率曾经非常低了: 贷款人进行放贷,借款人进行借贷。 如何应答去杠杆化呢? 削减收入缩小债权:债权重组财产再调配:富人转给富人发行货币只有衡量好这4个步骤,能力谐和的去杠杆化。 央行能够减少货币发行量,来补救隐没的信贷。 总结不要让债权的增长速度超过支出不要让支出的增长速度超过生产率:因为这最终将会使你失去竞争力,本人的价值配不上工资就会被裁员。尽所有努力提高生产率

April 10, 2022 · 1 min · jiezi

关于objective-c:3天时间从零到上架AppStore流程记录

3天工夫从零到上架AppStore流程记录清明假期刚过去一周,我如愿以偿把本人想要的一个App上架了AppStore 从有idea到技术选型,从设计稿到框架开发,从提审AppStore到上架一共经验了3天,这3天里我踩了很多坑,忙得焦头烂额,期间发现网上很多信息都是过期的,所以本着一个总结的想法,写了这篇文章。 一、idea到设计稿(一)idea灵感起源平时我下班很忙,休闲时会刷手机看B站、Youtube视频,但因为平时关注了太多人,想看的却只有那几个,但还是会忍不住点击算法举荐的视频。 "xdm洁净又卫生啊""大佬别这样""守护每一个不切实际的幻想"B站内容丰盛,每次不仅看不到想看的视频,反而花了比预计好几倍的工夫。 所以我想要做一个产品,喜爱可能帮我追踪B站、Youtube平台上我最想关注的人的动静,不必我每次关上B站、Youtube去查看内容,所以我依照素日工作里排期的流程给本人做了性能的排期: - 喵酱爱订阅 - 展现导航栏 - 搜寻页面 - 写搜寻的cgi - 写解析用户状态的cgi - 要反对右滑勾销订阅 - 把用户状态数据更新到缓存 - 把缓存落到DB - 每次启动读取DB - 制作宣传视频 - 制作启动教育页 - 分割页 - 分割跳转等页面 - 性能优化 - 加一个by xxx 关注,而后谈起一个半屏 - 加一个 xx、yy和另外x人关注了此账号 - 须要退出loading等逻辑 - 全局extension call:按id注册,还是全副接管 - 开始更新订阅数据- 搜寻页面 - 呈现导航栏 - 呈现敞开按钮 - 点击搜寻要能过渡到另外一个搜寻界面 - 开始接B站搜寻的api - 开始实现订阅该Up主性能 - 滑动删除 - 搜寻交互调整- 订阅数据 - 开始更新订阅数据,获取更全的信息 - 按timeline排序接下来要做的: - 反对Youtube订阅 - 实现B站跳转逻辑 - 实现Youtube跳转逻辑 - 实现清理红点逻辑 - 调整搜寻两头页逻辑 - 「对于」页面设计 - 帮忙和常见问题 - 隐衷政策 - 视觉还原和调整(增加束缚) - 增加主页元素的联动束缚 - 主页下滑不到最底部 - 导航栏色彩问题 - 修bug - 订阅完之后,不会立即呈现 - Youtube API有限调用 - App选一个图标(二)本人搞定设计稿有了idea之后,依照正经开发流程,相当于是产品计划确定了,接下来就须要设计稿了。 ...

April 9, 2022 · 3 min · jiezi

关于objective-c:Swift怎样使用OC枚举多个枚举以及OC多枚举实现

前段时间我同学写的OC版本的SDK给第三方用,第三方用Swift写的我的项目,不会应用Swift去调用OC的枚举,起初我同学找到我,我就给他写了一下,废话不多说,间接上代码。 OC枚举:typedef NS_ENUM(NSInteger, SwiftUseOCEnum) { SwiftUseOCEnumNone = 1<<0,SwiftUseOCEnumOne = 1<<1,SwiftUseOCEnumTwo = 1<<2,SwiftUseOCEnumThree = 1<<3,}; @interface Test : NSObject -(void)test:(SwiftUseOCEnum)ocEnum; @end OC枚举多枚举实现:-(void)test:(SwiftUseOCEnum)ocEnum{ if(ocEnum == SwiftUseOCEnumNone){ NSLog(@"SwiftUseOCEnumNone");}else if (ocEnum == SwiftUseOCEnumOne) { NSLog(@"SwiftUseOCEnumOne");}else if (ocEnum == SwiftUseOCEnumTwo) { NSLog(@"SwiftUseOCEnumTwo");}else if (ocEnum == SwiftUseOCEnumThree) { NSLog(@"SwiftUseOCEnumThree");}else{ //传了多个枚举进来 if((ocEnum & SwiftUseOCEnumNone) == SwiftUseOCEnumNone){ NSLog(@"多个枚举蕴含了SwiftUseOCEnumNone"); } if((ocEnum & SwiftUseOCEnumOne) == SwiftUseOCEnumOne){ NSLog(@"多个枚举蕴含了SwiftUseOCEnumOne"); } if((ocEnum & SwiftUseOCEnumTwo) == SwiftUseOCEnumTwo){ NSLog(@"多个枚举蕴含了SwiftUseOCEnumTwo"); } if((ocEnum & SwiftUseOCEnumThree) == SwiftUseOCEnumThree){ NSLog(@"多个枚举蕴含了SwiftUseOCEnumThree"); }}} ...

March 30, 2022 · 1 min · jiezi

关于objective-c:RGBYUVHSV和HSL区别和关联

RGB、YUV、HSV和HSL区别和关联近期在做的一个需要和色彩转换有关系,所以本篇将开发过程中比拟常见的 四种色彩 进行一番梳理。 一、RGB色彩空间从咱们最常见的RGB色彩登程,RGB别离对应着 Red(红)、Green(绿)、Blue(蓝),也就是咱们平时所说的三原色,调整这三种色彩的比例,能够搭配出所有的色调。 这时你可能就要问了,YUV、HSV、HSL也能形容所有色调啊,为啥RGB是最罕用的捏? 这就要回归到事实了,事实里显示器显像时,每一个像素点前面对应着 3个发光二极管,这3个二极管能够别离收回 红、绿、蓝 三种色彩,因而绝大部分人所能接触的色彩只与RGB有关系。 RGB(红绿蓝)是根据人眼辨认的色彩定义出的空间,可示意大部分色彩。但在科学研究个别不采纳RGB色彩空间,因为它的细节难以进行数字化的调整。它将色调,亮度,饱和度三个量放在一起示意,很难离开。它是最通用的面向硬件的黑白模型。该模型用于黑白监视器和一大类黑白视频摄像。 二、YUV色彩空间YUV 多呈现在音视频合成畛域,音视频合成畛域要求在表白同样内容时,争取占用更少的空间。同个视频,YUV空间要比RGB空间描述省下来一半的空间耗费(YUV4:2:0)。 YUV(也称:YCbCr):Y示意亮堂度,UV的作用是形容影像色调及饱和度。 次要的采样格局有 YUV4:2:0(最罕用)、YUV4:2:2 和 YUV4:4:4 ,也就是说 RGB 次要用于屏幕图像的展现,而 YUV 多用于采集与编码。 YUV 和 RGB 互相转换的公式为: 三、HSV(HSB) 和 HSL能够发现 RGB 次要为硬件显示器服务,YUV 次要为音视频编解码服务,这么说下来和色调最密切的 设计师 该用哪种颜色呢? 他们也有本人行业特地关注的色彩,次要应用 HSV 和 HSL。 (一)为什么RGB不适用于图像处理人眼对于RGB这三种色彩重量的敏感水平是不一样的,在单色中,人眼对红色最不敏感,蓝色最敏感,所以 RGB 色彩空间是一种平均性较差的色彩空间。如果色彩的相似性间接用欧氏间隔来度量,其后果与人眼视觉会有较大的偏差。对于某一种色彩,咱们很难揣测出较为准确的三个重量数值来示意。 简略来说,如果计算不同色彩之间的对比度,如果应用 RGB 来计算: (R1-R2)^2 + (G1-G2)^2 + (B1-B2)^2 即便两组色彩数值雷同,人的感触还是不一样的,比方这里我选三个色彩: RGB_1:110,0,110RGB_2:60,0,100RGB_3:160,0,110 能够看到只管 RGB_1 和 RGB_3 间隔 RGB_2 计算的欧式偏差是一样的,但咱们还是会显著感觉 RGB_1 相比 RGB_3 更靠近 RGB_2 ,因为 RGB_3 看上去比 RGB_1 和 RGB_2 更亮一些。 ...

March 20, 2022 · 6 min · jiezi

关于objective-c:CPU是海王聊聊-主子线程-和-同异步-的关系

最近表弟始终在找实习,常常会问我一些问题,有些问题在没有经验过实在工作时是真的不好了解的,所以我开了这个【表弟专栏】,专门为找工作的表弟解决一些纳闷。这篇文章从计算机倒退的角度登程,形容为什么计算机须要划分 "主/子线程" 和 "同/异步",心愿这个故事你能喜爱。 开天辟地,计算机"老计"诞生话说几十年前,老计(初代CPU)诞生了,老计诞生初期帮人们解决了诸多繁琐的计算工作,他能够无休无止地依照人们给的指令进行运算。 就像图中一样,人们产生着各种问题,而后把问题丢入到一个队列中去,老计依照人们丢入的指令按程序进行计算。 但就像当初互联网越来越卷一样,老计工作几年之后,发现要做的事件切实是太多了,有些计算要算完要搞到几十年后了,这可咋办呢? 这时人们认为老计该退休了,换了大计出场,大计的长处是相当卷,计算速度是老计的N倍,大大晋升了计算的速度。 事实问题,大计海王进化(主/子线程)大计有一天在诚诚恳恳工作时,被主管拉到了办公室,开始批评大计: "大计啊,我晓得你工作起来很致力,但你有点死板了啊,所有人给你调配的工作你都是按程序操作,导致用户说咱们的产品就像在挤牙膏,一点点挤出来。你能再放慢工作速度吗?不能只靠加班来实现工作啊!" 大计一脸mmp像,心想:“我特么要做什么,不还是你给到的指令啊”,但大计有着当代互联网人独特的指标("保住这份工"),所以大计也只能说: "好的主管,这事我想想方法,今天给你个回答。" 大计这天工作到凌晨3点,上班后和总线一起去喝酒,说起了白天被主管叼这件事,总线说: "这事好办啊,我平时给你传的各种指令,优先级不高的你让它不要走同一条门路传输呀,咱们像渣男一样搞个高并发!给每个指令都送去和煦,钓住她们,让她们认为你是专一的男人,实际上你在同时和多人来往。" 大计一听直呼妙计妙计,总线不愧是和各个部门打交道的社交花,把泡妞能力用到工作上间接无缝连接。 之前大计只有一条指令流水线时,他是按走流程,指令1解决完了,才解决指令2,是一个对指令专一的女子。 大计当即画出了这样的指令图: 大计当初能"同时"解决 3条指令:M_0、A_0和B_0,说是"同时",实际上只是大计在这三条指令里频繁切换,M_0实现40%,就去招呼下A_0,A_0解决完80%后,而后再去招呼下B_0指令。 也就是说尽管工作时长是肯定的,但给主管一种同时在解决3个指令的错觉。就靠着这套海王策略,大计终于实现了刁钻主管的需要。 不急的话,这事能够等等再做(同/异步)随着大计海王能力的扩大,当初最高时能够"同时"和几百个指令妹子约会,曾经是不可被代替的重要角色,当初主管每天看到大计都得磕个头,大计的话语权也越来越大,甚至开始反过来给主管提需要了: "主管啊,你可要晓得我主线程是负责和用户交互的重要线程啊,你每天在主线程丢那么指令,十分影响用户的体验了啊" "你有些渲染UI的逻辑太频繁了,导致我做了很多无用的渲染,而且渲染逻辑也分重要水平啊,不重要的指令你等我闲暇了再解决啊" 主管连连拍板,说好的好的:"咱们下次增加指令时,会通知你这个指令是否须要立即施行,如果不那么重要能够等你闲暇下来再解决。" 于是乎,同步、异步 的概念诞生了: 之前大计在解决 主线程 里的指令时,因为同一个线程指令是没有优先级的概念的,所以 线程里的指令还是按程序解决的:M_0 -> M_1 -> M_2 -> M_3 -> M_4 但有了 异步 的概念后,假如这里的 指令M_1 不那么重要,那么大计在解决时是能够把 M_1 放到前面解决的:M_0 -> M_2 -> M_3 -> M_4 -> M_1 小结本篇【表弟专栏】给大家聊了 主/子线程 诞生的必要性,形容了CPU海王实质,以及和同/异步的区别和关联。对于 主/子线程 和 同/异步 的问题,大家能够在评论区一起探讨。最初如果大家喜爱这个专题,能够关注我的公众号:冰以东,会继续更新【表弟专栏】,心愿能帮忙大家找到更好的工作。

March 19, 2022 · 1 min · jiezi

关于objective-c:SourceTree代码变更和FoxMail邮件管理效率小计俩

代码变更溯源工作时,咱们常常会想要查看一个类文件的变更历史,最常见的场景是:"卧槽,谁改了我的代码" 新版本的Xcode溯源自我感觉相当难用,所以这里咱们介绍一个工具 SourceTree 来实现这项工作。 将我的项目工程加载到 SourceTree当咱们把我的项目工程拖到 SourceTree 之后,能够看到如下的内容: 其中BNBitcoinIndexApp是我的我的项目工程名。 检索文件抉择 ①文件状态 -> ②搜寻文件 -> ③查看选中的批改日志 查看文件变更如此能够看到所有改变到该文件的commit(是按工夫顺序排列)。 邮件治理公司会发几十封、多的时候上百封邮件,邮箱被各种咱们不关怀的邮件塞满,慢慢地有价值的邮件也会被咱们疏忽。 这时咱们学会对邮件进行治理,邮件治理分为两个步骤:首先是对邮件进行分类,其次是对邮件进行规定过滤。 邮件组 点击 Foxmail -> 新建文件夹,假如你对专利比拟感兴趣,能够新建文件夹专利相干: 邮件过滤这里咱们应用 Foxmail 自带的 规定匹配: 任意右键点击一封右键,而后抉择 创立规定,创立如下的规定: 点击利用到历史邮件,稍等加载一会咱们的邮件分组就实现了:

March 19, 2022 · 1 min · jiezi

关于objective-c:SpringBoot架构关键词全解

一、外围配置文件一个我的项目中 application.yml 和 application.properties 只能有一个。 (一)application.properties# 设置内嵌Tomcat端口号server.port = 8081#设置上下文根server.servlet.context-path = /springboot(二)application.ymlYML文件格式是YAML (YAML Aint Markup Language)编写的文件格式,YAML是一种直观的可能被电脑辨认的的数据数据序列化格局,并且容易被人类浏览, 容易和脚本语言交互的,能够被反对YAML. YML文件是以数据为外围的,比传统的xml形式更加简洁。 server: port: 8081 servlet: context-path: 二、获取SpringBoot自定义配置的字段(一)通过 @Value 注解读取自定义配置字段配置 application.properties # 设置内嵌Tomcat端口号server.port = 8081#设置上下文根server.servlet.context-path = /springbootschool.name = binSchoolwebsit=http://bninecoding.com应用 @Value("${school.name}") @Value(${school.name})private String schoolName;(二)将自定义配置映射成对象波及注解: @Component // 将此类交给spring容器进行治理@ConfigurationProperties(prefix = "school")@Autowired配置 application.properties server.servlet.context-path = /springbootschool.name = binSchoolschool.websit = binSchoolabc.name = binSchoolabc.websit = binSchool创立 school 类: @Component@ConfigurationProperties(prefix = "school")public class School { private String name; private String websit; public String getName() { retutrn name; } public void setname(String name) { this.name = name; } ...}因为@Component这样做之后曾经将 school 类加载到 spring 容器里了,所以应用时能够应用 @Autowired 注入进来: ...

March 13, 2022 · 3 min · jiezi

关于objective-c:操作数据库你不得不知道的-事务-理念

无论是 iOS操作DB、还是 SpringBoot中Service操作Mybatis,都须要波及到 事务 的概念,如果你在新增一个 DB 增删改 的接口时,没有思考到事务,那么你新增的接口肯定是有问题的。 了解事务事务是指对系统进行的一组操作,为了保证系统的完整性,事务须要具备ACID个性,具体如下: 1. 原子性(Atomic)一个事务蕴含多个操作,这些操作要么全副执行,要么全都不执行。实现事务的原子性,要反对回滚操作,在某个操作失败后,回滚到事务执行之前的状态。 回滚实际上是一个比拟高层形象的概念,大多数DB在实现事务时,是在事务操作的数据快照上进行的(比方,MVCC),并不批改理论的数据,如果有错并不会提交,所以很天然的反对回滚。 而在其余反对简略事务的零碎中,不会在快照上更新,而间接操作理论数据。能够先预演一边所有要执行的操作,如果失败则这些操作不会被执行,通过这种形式很简略的实现了原子性。 2. 一致性(Consistency)一致性是指事务使得零碎从一个统一的状态转换到另一个统一状态。事务的一致性决定了一个零碎设计和实现的复杂度。事务能够不同水平的一致性: 强一致性:读操作能够立刻读到提交的更新操作。弱一致性:提交的更新操作,不肯定立刻会被读操作读到,此种状况会存在一个不统一窗口,指的是读操作能够读到最新值的一段时间。最终一致性:是弱一致性的特例。事务更新一份数据,最终一致性保障在没有其余事务更新同样的值的话,最终所有的事务都会读到之前事务更新的最新值。如果没有谬误产生,不统一窗口的大小依赖于:通信提早,零碎负载等。其余一致性变体还有: 枯燥一致性:如果一个过程曾经读到一个值,那么后续不会读到更早的值。会话一致性:保障客户端和服务器交互的会话过程中,读操作能够读到更新操作后的最新值。3. 隔离性(Isolation)并发事务之间相互影响的水平,比方一个事务会不会读取到另一个未提交的事务批改的数据。在事务并发操作时,可能呈现的问题有: 脏读:事务A批改了一个数据,但未提交,事务B读到了事务A未提交的更新后果,如果事务A提交失败,事务B读到的就是脏数据。不可反复读:在同一个事务中,对于同一份数据读取到的后果不统一。比方,事务B在事务A提交前读到的后果,和提交后读到的后果可能不同。 不可反复读呈现的起因就是事务并发批改记录,要防止这种状况,最简略的办法就是对要批改的记录加锁,这回导致锁竞争加剧,影响性能。另一种办法是通过MVCC能够在无锁的状况下,防止不可反复读。幻读:在同一个事务中,同一个查问屡次返回的后果不统一。事务A新增了一条记录,事务B在事务A提交前后各执行了一次查问操作,发现后一次比前一次多了一条记录。 幻读是因为并发事务减少记录导致的,这个不能像不可反复读通过记录加锁解决,因为对于新增的记录根本无法加锁。须要将事务串行化,能力防止幻读。事务的隔离级别从低到高有: Read Uncommitted:最低的隔离级别,什么都不须要做,一个事务能够读到另一个事务未提交的后果。所有的并发事务问题都会产生。Read Committed:只有在事务提交后,其更新后果才会被其余事务看见。 能够解决脏读问题。Repeated Read:在一个事务中,对于同一份数据的读取后果总是雷同的,无论是否有其余事务对这份数据进行操作,以及这个事务是否提交。 能够解决脏读、不可反复读。Serialization:事务串行化执行,隔离级别最高,就义了零碎的并发性。 能够解决并发事务的所有问题。通常,在工程实际中,为了性能的思考会对隔离性进行折中。 4. 持久性(Durability)事务提交后,对系统的影响是永恒的。 示例举一个近期开发过程中遇到的例子: 珍藏音乐的业务,有一个 songID(音乐id) 和 localId(珍藏生成的id)在咱们珍藏一首音乐时:>> 步骤1:首先须要将 songId 和 localId 的映射关系存到 映射DB>> 步骤2:接着能力将 音乐 存到珍藏业务里。如果步骤1失败了,那就肯定不能执行步骤2,同理,如果步骤2失败了,也要同步回滚掉步骤1的数据。也就是说:整个珍藏业务肯定是 事务型的。文章首发:问我社区

March 13, 2022 · 1 min · jiezi

关于objective-c:编写第一个GETPOST接口renrenfast框架系列1

配置好 renren-fast 脚手架,学习完 Spring MVC 架构后,我须要具体调试 renren-fast 的接口,比方要新增某个接口。 什么是前后端拆散运行 renren-fast 我的项目时,咱们拜访 http://localhost:8080/renren-... 的后果: 能够看到,接口给出了相应的回应,状态码 401 Unauthorized 代表客户端谬误,指的是因为不足指标资源要求的身份验证凭证,发送的申请未失去满足。 运行 renren-fast-vue 我的项目时,咱们拜访 http://localhost:8001/#/login : 接着应用Chrome自带的网络工具: 点击 Headers 能够查看 Request URL: 以此,咱们确认拜访 后盾接口为:http://localhost:8080/renren-... 同时还能够看到如下获取页面信息的 headers: 申请的拜访接口是:http://localhost:8001/static/... 至此,咱们便搞清楚了 renren-fast 前后端拆散的业务是在说什么,即:后端逻辑应用 renren-fast 实现,前端申请获取的页面数据,应用 renren-fast-vue 实现。 写一个无需鉴权的GET接口接下来咱们基于 renren-fast 框架仿照 "/captcha.jpg" 写第一个接口, "/captcha.jpg"接口代码: /** * 验证码 */ @GetMapping("captcha.jpg") public void captcha(HttpServletResponse response, String uuid)throws IOException { response.setHeader("Cache-Control", "no-store, no-cache"); response.setContentType("image/jpeg"); //获取图片验证码 BufferedImage image = sysCaptchaService.getCaptcha(uuid); ServletOutputStream out = response.getOutputStream(); ImageIO.write(image, "jpg", out); IOUtils.closeQuietly(out); }咱们写一个 /testInterface 接口,在没写接口之前,咱们拜访: http://localhost:8080/renren-... ,返回如下款式: ...

March 13, 2022 · 1 min · jiezi

关于objective-c:时序控制和引用计数理念

明天在听一个T10的问难,其中有一个理念让我觉得很有意思,记录一下。 背景: 下载视频业务层均应用Service,同一个下载工作只能对应一个播放行为。如果两个业务依照如下程序下载同一个视频,就会呈现问题: 最后听到这个问题,我第一想到的计划是代码管制时序,在业务B前查看业务A,保障调用业务B时,A的业务曾经停掉了。 但这样的解决形式仅仅是为了解决以后问题给出的计划,如果后续有业务C、业务D也遇到相似的状况,那还是须要非凡解决。 问难的同学给出的一个计划很简略,但很有意思: 它对播放器做了援用计数的逻辑,只有援用计数归零了,才会彻底移除下载工作。 援用计数咱们熟记于心,但能想到用这种计划解决问题的,我感觉很牛逼。 **这个公众号会继续更新技术计划、关注业内技术动向,关注一下老本不高,错过干货损失不小。↓↓↓**

March 9, 2022 · 1 min · jiezi

关于objective-c:打造高可用iOS进度条

前言做全屏的需要时,因为进度条会从半屏背景下的「根本不可能曝光」,变成全屏场景下「高频曝光」,所以须要打造一个丝滑、高可用的进度条,想当初我Debug到凌晨4点,就是为了解决暂停后进度条的动画问题。 明天把这个进度条的架构、设计逻辑和踩过的坑都整顿一下。 本文波及的代码已开源至Github:打造高可用进度条 接口介绍BNCommonProgressBar.h// 变更进度,animateWithDuration是传入动画工夫- (void)setValue:(CGFloat)value;- (void)setValue:(CGFloat)value animateWithDuration:(NSTimeInterval)duration time:(NSTimeInterval)time;- (void)setValue:(CGFloat)value animateWithDuration:(NSTimeInterval)duration completion:(void (^__nullable)(BOOL finished))completion;// 重置所有状态,会将进度重置到0- (void)reset;// 暂停动画- (void)pauseAnimation;// 复原动画- (void)resumeAnimation;// 清理动画状态,手动拖拽时先清理动画状态- (void)removeProgressAnimation;一、为什么 UISlider 不满足「高可用」的指标?在论述 UISlider 不满足「高可用」指标之前,咱们先思考一下,满足什么样的条件的进度条,才能够算是「高可用」? 我想出四个指标: UI可高度定制晦涩的回调动画可定制的响应范畴响应手势,且无卡顿问题其中 UISlider 可满足其中 3 和 4,因为 UISlider 是零碎提供的组件,「UI可高度定制」这条必定不满足。 且 UISlider 对于动画的解决不够弱小,在视频播放的场景下,视频播放器会定时高频的回调视频播放进度,更新进度的动画要足够晦涩,但实际上应用 UISlider 的成果是上面这样的: 所以 UISlider 不满足 第2点:「晦涩的回调动画」,而视频号场景下,视频进度回调更新进度条进度是高曝光的场景,肯定要把这个动画做得足够晦涩。 在这样的背景下,放弃 UISlider ,自定义进度条是惟一的抉择。 二、定制一份「高可用」进度条Tips:BNCommonProgressBar是咱们定制的进度条的类名,首先先看一下BNCommonProgressBar实现的成果: BNCommonProgressBar 设计与需要绝对应: 指标:UI可高度定制 --> 计划:自定义UI指标:晦涩的回调动画 --> 计划:动画解决指标:可定制的响应范畴 --> 计划:手势范畴解决指标:响应手势,且无卡顿问题 --> 计划:拖拽手势解决,卡顿问题解决所以 BNCommonProgressBar 的设计也就分为 4个模块。 (一)自定义UIBNCommonProgressBar初始化办法为: - (instancetype)initWithFrame:(CGRect)frame barHeight:(CGFloat)progressBarHeight dotHeight:(CGFloat)dotHeight defaultColor:(UIColor *)defaultColor inProgressColor:(UIColor *)inProgressColor dragColor:(UIColor *)dragColor cornerRadius:(CGFloat)cornerRadius progressBarIconImage:(UIImage *)progressBarIconImage enablePanProgressIcon:(BOOL)enablePanProgressIcon;容许业务层配置进度条高度、进度圆点高度、默认色彩、处于进度拖拽时的色彩、是否容许拖拽等,相比 UISlider 有更高的自定义水平。 ...

March 9, 2022 · 2 min · jiezi

关于objective-c:使用-Hexo-Github-搭建博客

始终想搭建属于本人的博客,用来记录本人的所学。 但都因为各种各样奇怪的工夫安顿搁置了,导致我甚至开始用起了公司的git工具记录知识点.. 明天终于把博客搭建结束,顺便也把搭建博客路上遇到的坑记录下来。 先备常识(一)应用 Hexo & Github 搭建博客的起因首先,咱们思考一下,相比间接把内容发在 简书/掘金 平台上,自建博客有什么劣势? 通过本人的域名能够拜访博客,不必放心平台跑路/保护危险能够本人抉择博客款式本人的博客相当于本人的一份名片,通过优化SEO能够做到很好的扩充影响力的成果。当初搭建博客能够有N多工具,比方:买腾讯云服务器搭建博客、应用Jelly配置又或者本人自建网站。 但以上流程对于只是想领有一份记录本人所学的博客需要来说,工夫老本和收入老本都是偏高的。 Hexo 能够让咱们便捷地搭建博客,应用别人曾经设计好的博客模板;github的pages性能能够为咱们提供收费的服务器,让咱们零老本搭建属于本人的博客。 便捷、低成本、扩展性好 就是采纳 Hexo & Github 的次要起因。 (二)username/nickname On Github要辨别要 github 上 username 和 nickname 的区别,这点在搭建博客时十分重要 (三)配置 Hexo要配置好 Hexo ,此外理解肯定的 nodeJs 根底能够让你更分明你在配置过程中都在做什么,而不是只跟着教程操作而已,比方 npm install 是在做什么、以及为什么 package.json 能够很便捷地关联库的起因等等。 (四)命令行操作工具有肯定的命令行操作根底,能灵便应用 iTerm ,相熟 git 语法更佳。 (五)辨别 brew、npm、pip、apt-get 指令。这四个指令实际上是 不同零碎下治理软件包的指令。 一、brew即Homebrew,是Mac OSX上的软件包管理工具,能在Mac中不便的装置软件或者卸载软件, 只须要一个命令。默认都是装置到brew的指定目录“/usr/local/Cellar”下,而后在“/usr/local/bin”下创立对应的软连贯来应用的。如果装置多个不同版本的库,能够批改对应的软连贯就能够了二、npm(全称Node Package Manager,即“node包管理器”)是Node.js預設的、用JavaScript編寫的軟體套件管理系統。三、pippython软件包管理系统,能够利用它装置python包,默认都装置到以后python版本的python3.7/site-packages文件夹下四、apt-getlinux命令,实用于deb包治理式的操作系统,次要用于主动从互联网的软件仓库中搜寻、装置、降级、卸载软件或操作系统(六)理解 hexo 指令的含意比方 hexo init 、hexo d -g 、hexo s。 搭建博客流程(一)应用 github pages 创立繁难博客1. 在github建设博客仓库首先在本人的 github 上创立一个仓库,命名为 : username.github.io ...

March 9, 2022 · 1 min · jiezi

关于objective-c:iOS编译过程的原理和应用

浏览本文你将播种: iOS编译流程明确 __text、__data 的含意iOS dSYM 的作用和生成形式明确iOS crash堆栈符号化解析流程source: iOS编译过程的原理和利用 iOS 如何调试第三方统计到的解体报告 iOS Link Map File 文件阐明 前言个别能够将编程语言分为两种,编译语言和直译式语言。 像C++,Objective C都是编译语言。编译语言在执行的时候,必须先通过编译器生成机器码,机器码能够间接在CPU上执行,所以执行效率较高。 像JavaScript,Python都是直译式语言。直译式语言不须要通过编译的过程,而是在执行的时候通过一个两头的解释器将代码解释为CPU能够执行的代码。所以,较编译语言来说,直译式语言效率低一些,然而编写的更灵便,也就是为啥JS大法好。 iOS开发目前的罕用语言是:Objective和Swift。二者都是编译语言,换句话说都是须要编译能力执行的。二者的编译都是依赖于Clang + LLVM. 篇幅限度,本文只关注Objective C,因为原理上大同小异。 可能会有同学想问,我不懂编译的过程,写代码也没问题啊?这点我是不否定的。然而,充沛了解了编译的过程,会对你的开发大有帮忙。本文的最初,会以以下几个例子,来解说如何正当利用XCode和编译 __attribute__Clang正告解决预处理插入编译期脚本进步我的项目编译速度对于不想看我啰里八嗦讲一大堆原理的同学,能够间接跳到本文的最初一个章节。 一、iOS编译Objective C采纳Clang(swift采纳swiftc)作为编译器前端,LLVM作为编译器后端。 简略的编译过程如图 (一)编译器前端编译器前端的工作是进行:词法剖析,语法分析,语义剖析,生成中间代码(intermediate representation )。在这个过程中,会进行类型查看,如果发现错误或者正告会标注进去在哪一行。 (二)编译器后端编译器后端会进行机器无关的代码优化,生成机器语言,并且进行机器相干的代码优化。iOS的编译过程,后端的解决如下LVVM优化器会进行BitCode的生成,链接期优化等等。 LLVM机器码生成器会针对不同的架构,比方arm64等生成不同的机器码。 二、执行一次XCode build的流程当你在XCode中,抉择build的时候(快捷键command+B),会执行如下过程 编译信息写入辅助文件,创立编译后的文件架构(name.app)解决文件打包信息,例如在debug环境下 Entitlements:{ "application-identifier" = "app的bundleid"; "aps-environment" = development;}执行CocoaPod编译前脚本 例如对于应用CocoaPod的工程会执行CheckPods Manifest.lock编译各个.m文件,应用CompileC和clang命令。 CompileC ClassName.o ClassName.m normal x86_64 objective-c com.apple.compilers.llvm.clang.1_0.compilerexport LANG=en_US.US-ASCIIexport PATH="..."clang -x objective-c -arch x86_64 -fmessage-length=0 -fobjc-arc... -Wno-missing-field-initializers ... -DDEBUG=1 ... -isysroot iPhoneSimulator10.1.sdk -fasm-blocks ... -I 上文提到的文件 -F 所须要的Framework -iquote 所须要的Framework ... -c ClassName.c -o ClassName.o通过这个编译的命令,咱们能够看到 ...

March 9, 2022 · 5 min · jiezi

关于objective-c:Proxy消息转发实战

情谊提醒:点击查看本文所波及的demo代码 导语代理即是代表受权方处理事务(From Wikipedia)。 思考一下咱们生存中什么时候会用到代理呢? 租房、买房时,咱们须要一位中介帮咱们分割房东,解决手续上的事件,升高咱们和房东的沟通老本。叫外卖时,咱们须要外卖小哥帮咱们送外卖,好让咱们有更多工夫去专一别的事件。所以能够了解为中介帮咱们解决两个层面上的问题: 缩小相互依赖的问题缩小做反复的事件所以从实质上来说,Proxy体现的还是"中间层"的设计思维,具体利用于"音讯转发"的业务场景。 循环援用在讲述明天这个Demo前,咱们先回忆一下之前咱们接触过的Proxy的利用场景,我想你脑海中必定第一工夫浮现出:应用Proxy解决NSTimer循环援用的问题。 所以咱们首先聊一聊 Proxy 应用最刚需的 「解决循环援用」的场景。 循环援用是怎么产生的下图是内存失常回收的过程: 上面是产生循环援用导致内存透露的过程: 验证是否产生循环援用的最佳形式就是判断是否产生了一个援用环。 NSTimer 循环援用问题NSTimer 问题最乏味的点是,网上对于 NSTImer 为什么会导致循环援用的解释 80%都是不清晰的,比方这样一个最广泛的说法: 这样的说法就好似有人问小明:"NSTimer为什么会导致循环援用?" 小明却答复:"NSTimer会导致循环援用"。 演出了一出"搁着搁着呢"的好戏。 循环援用肯定是 ViewController 和 NSTimer 互相强援用,但为什么 NSTimer addTarget 会导致循环援用,但平时咱们应用的 UIButton addTarget却不会导致循环援用呢? 答复分明这个问题,才算是说分明了"NSTimer为什么会导致循环援用"。 其实解答这个问题也很简略 咱们查一下大苹果提供的文档阐明, 对于 UIControl : The control does not retain the object in the target parameter. It is your responsibility to maintain a strong reference to the target object while it is attached to a control. ...

March 9, 2022 · 2 min · jiezi

关于objective-c:如何优雅地压缩一张图片

平时开发中咱们常常会接到这样的需要:上传图片前,须要把图片压缩到100KB以下,且要尽可能的清晰。 作为一名有直觉的开发,咱们即刻三下五除二搞个压缩因子: NSData *imageData = UIImageJPEGRepresentation(image, 0.8);(为什么不必 UIImagePNGRepresentation 而应用 UIImageJPEGRepresentation 能够Google,这里不做解释了哈)后果发现用户可能会抉择10MB的图片,0.8的压缩因子只能压到3MB,这必定不能够的哇。 那咱们来个循环压!直到压到指标为止! NSData *imageData = UIImageJPEGRepresentation(image, imageCompressRate);; while (imageData.length > 100 * 1024) { imageCompressRate -= 0.1; imageData = UIImageJPEGRepresentation(image, imageCompressRate); }你写完下面这个代码,测试了几张图片,称心如意地提交了代码,但过了几天发现有人在上传图片的时候卡死了!这是肿么肥四! 一查原来是刚写的 while循环压 死循环了!What?! 拿了用户反馈的图片进行复现,后果发现如果是很大的图,压缩因子即便再怎么小,也压不动了。 上面以11MB的图片为例,看看压缩后果和压缩因子的关系: 从这个压的后果咱们能够失去两个论断: 只靠压是不能把一张图片有限压为靠近0大小的压缩因子在1~0.8期间变更影响显著,0.8之后压完差别不大好家伙,居然怎么压都压不为0,那咱们的 while循环压 绝壁会呈现死循环的可能呀,那怎么办呢? 解决方案最初必定会说的啦,在介绍解决方案前,咱们先思考一下, UIImageJPEGRepresentation 为什么会呈现"压不动"的状况? 图片的大小是用 : 宽 x 高 x 位深 来计算的,UIImageJPEGRepresentation 的压缩形式理论是将 几个像素点用一个像素点来示意,也就是在 位深 上做文章,不会影响图片的 bitmap(宽 x 高), 所以如果图片比拟大,那么听凭用 UIImageJPEGRepresentation 怎么压,都会压到一个阈值,不会靠近于0的。 原理搞清楚了,咱们来思考下怎么 优雅地压缩一张图片吧!先间接上代码: /* 依据图片大小,获取图片压缩因子 */+ (CGFloat)getCompressRateByImageSize:(CGFloat)imageSize targetSize:(CGFloat)targetSize { NSUInteger rate = (NSUInteger)(imageSize / targetSize); rate = (rate == 0) ? 1 : rate; // 默认0.8压缩因子 CGFloat maxCompressRate = 0.8; CGFloat minCompressRate = 0.2; // 反比例压缩函数 CGFloat compressRate = 0.8 / rate; compressRate = MIN(MAX(compressRate, minCompressRate), maxCompressRate); return compressRate;}/*! * @brief 使图片压缩后刚好小于指定大小 * * @param image 以后要压缩的图 maxLength 压缩后的大小 * * @return 图片对象 */+ (NSData *)compressImageSize:(UIImage *)image toByte:(NSUInteger)maxLength { // 压 NSData *data = UIImageJPEGRepresentation(image, 1); if (data.length < maxLength) { return data; } CGFloat compressRate = [self.class getCompressRateByImageSize:data.length targetSize:maxLength]; data = UIImageJPEGRepresentation(image, compressRate); if (data.length < maxLength) { return data; } // 缩 UIImage *resultImage = [UIImage imageWithData:data]; NSUInteger lastDataLength = 0; while (data.length > maxLength && data.length != lastDataLength) { lastDataLength = data.length; CGFloat ratio = (CGFloat)maxLength / data.length; CGSize size = CGSizeMake((NSUInteger)(resultImage.size.width * sqrtf(ratio)), (NSUInteger)(resultImage.size.height * sqrtf(ratio))); if (CGSizeEqualToSize(size, CGSizeZero) || size.width < 10 || size.height < 10) { break; } UIGraphicsBeginImageContext(size); [resultImage drawInRect:CGRectMake(0, 0, size.width, size.height)]; resultImage = UIGraphicsGetImageFromCurrentImageContext(); UIGraphicsEndImageContext(); data = UIImageJPEGRepresentation(resultImage, compressRate); } return data;}逻辑也不简单,既然 单纯压 会呈现 压不动的状况,那咱们就先压后缩,鉴于 UIImageJPEGRepresentation 压缩存在疾速损失的状况(达到阈值后,压1次和2次的成果都是一样的), ...

March 9, 2022 · 2 min · jiezi

关于objective-c:ObjectiveC-之-block

前言作为iOS开发,咱们素日里会高频应用block,block十分重要,在学习Swift闭包时,我忽然感觉能够将 Objective-C block 和 Swift闭包 一起比照学习。 如果你针对上面的问题曾经有了比拟深的了解,那么能够略过本篇文章: block 的数据结构block 的内存机制block 和 weakify/strongify 的关联Swift闭包和 Objective-C block的区别dispatch_block_t 的利用场景一、block的数据结构(一)block 语法解析作为硬核派,理解block数据结构咱们必定不能Google他人的论断,咱们有本人的clang工具,应用clang工具,能够将 OC 代码转成 C++ 代码。 首先,咱们筹备 main.m 这个类,类内容为: // main.m int main() { return 1;}咱们切到main.m类所在文件夹,应用指令xcrun -sdk iphoneos clang -arch arm64 -rewrite-objc main.m,发现语法解析生成的cpp代码如下main.cpp: // main.cpp#import <UIKit/UIKit.h>int main() { void (^block)(void) = ^ { NSLog(@"Hello World!"); }; block(); return 1;}接着咱们在main.m中增加block代码: // main.m int main() { void (^block)(void) = ^ { }; block(); return 1;}持续应用clang解析main.m,发现生成的cpp代码如下: // main.cppstruct __main_block_impl_0 { struct __block_impl impl; struct __main_block_desc_0* Desc; __main_block_impl_0(void *fp, struct __main_block_desc_0 *desc, int flags=0) { impl.isa = &_NSConcreteStackBlock; impl.Flags = flags; impl.FuncPtr = fp; Desc = desc; }};static void __main_block_func_0(struct __main_block_impl_0 *__cself) { NSLog((NSString *)&__NSConstantStringImpl__var_folders_9b_w0ymsg0n3yqdlb90w49xqmz40000gn_T_main_2428cf_mi_0); }static struct __main_block_desc_0 { size_t reserved; size_t Block_size;} __main_block_desc_0_DATA = { 0, sizeof(struct __main_block_impl_0)};int main() { void (*block)(void) = ((void (*)())&__main_block_impl_0((void *)__main_block_func_0, &__main_block_desc_0_DATA)); ((void (*)(__block_impl *))((__block_impl *)block)->FuncPtr)((__block_impl *)block); return 1;}static struct IMAGE_INFO { unsigned version; unsigned flag; } _OBJC_IMAGE_INFO = { 0, 2 };去除强制转换后咱们能够看出申明block的时候,block底层调用了__main_block_iml_0构造体,传入的参数别离是__main_block_func_0(办法函数)和&__main_block_desc_0_DATA(构造体地址)。 ...

March 6, 2022 · 4 min · jiezi

关于objective-c:Nodejs-学习笔记

Nodejs 学习笔记**这个公众号会继续更新技术计划、关注业内技术动向,关注一下老本不高,错过干货损失不小。↓↓↓** 一、Nodejs介绍(一)根本介绍 Node.js 是一个 JavaScript 运行环境(runtime),它让 JavaScript 能够开发后端程序,简直能实现其它后端语言的所有性能。 Nodejs最善于的是解决高并发:在 Java、PHP 或者 .net 等服务器端语言中,会为每一个客户端连贯创立一个新的线程。而每个线程须要消耗大概 2MB 内存。也就是说,实践上一个 8GB 内存的服务器能够同时连贯的最大用户数为 4000 个左右。 要让 Web 应用程序反对更多的用户,就须要减少服务器的数量,而 Web 应用程序的硬件老本就回升了。 Node.js 不为每个客户连贯创立一个新的线程,而仅仅应用一个线程。当有用户连贯,就触发一个外部事件,通过非阻塞 I/O 、事件驱动机制,让 Node.js 程序宏观上也是并行的。 应用 Node.js,一个 8GB内存 的服务器,能够同时解决超过4万用户的连贯。 (二)开发工具 举荐应用 VSCode 装置好 Node Snippets 插件 二、HTTP模块、URL模块、supervisor工具(一)HTTP模块如果应用 PHP 来编写后端diamante,须要 Apache 或者 Nginx 的 HTTP 服务器,来解决客户端的申请响应。不过对 Node.js 来说,概念齐全不一样。应用 Node.js 时,咱们不仅仅在实现一个利用,同时还实现了整个 HTTP 服务器。 Tips : ctrl + c 终止服务器 // 示意引入 http 模块var http = require('http');/* request : 获取 url 传过来的信息 response : 给浏览器响应信息*/http.createServer(function (request, response) { // 设置响应头 response.writeHead(200, {'Content-Type': 'text/plain'}); // 示意给咱们的页面下面输入的一句话并且完结响应 response.end('Hello World');}).listen(8081); // 端口console.log('Server running at http://127.0.0.1:8081/');(二)URL模块url 模块也是 nodejs 内置模块。 ...

March 6, 2022 · 6 min · jiezi

关于objective-c:iOS开发面试只需知道这些技术基本通关性能优化篇

在性能优化中一个最具参考价值的属性是FPS:FramesPerSecond,其实就是屏幕刷新率,苹果的iphone举荐的刷新率是60Hz,也就是说GPU每秒钟刷新屏幕60次,这每刷新一次就是一帧frame,FPS也就是每秒钟刷新多少帧画面。静止不变的页面FPS值是0,这个值是没有参考意义的,只有当页面在执行动画或者滑动的时候,FPS值才具备参考价值,FPS值的大小体现了页面的晦涩水平高下,当低于45的时候卡顿会比拟显著。 图层混合: 每一个 layer是一个纹理,所有的纹理都以某种形式重叠在彼此的顶部。对于屏幕上的每一个像素,GPU须要算出怎么混合这些纹理来失去像素 RGB的值。 当 Sa = 0.5时,RGB值为(0.5,  0, 0),能够看出,当两个不是齐全不通明的 CALayer笼罩在一起时,GPU大量做这种复合操作,随着这中操作的越多,GPU越繁忙,性能必定会受到影响。 公式: R = S + D * ( 1 – Sa ) 后果的色彩是源色调(顶端纹理)+指标色彩(低一层的纹理)*(1-源色彩的透明度)。当 Sa = 1时,R = S,GPU将不会做任何合成,而是简略从这个层拷贝,不须要思考它下方的任何货色(因为都被它遮挡住了),这节俭了 GPU相当大的工作量。 一、入门级1、用 ARC治理内存 2、在正确的中央应用  reuseIdentifier 3、尽量把 views设置为通明 4、防止过于宏大的  XIB 5、不要阻塞主线程 6、在 ImageViews中调整图片大小。如果要在 UIImageView中显示一个来自  bundle的图片,你应保障图片的大小和 UIImageView的大小雷同。在运行中缩放图片是很消耗资源的,特地是 UIImageView嵌套在UIScrollView中的状况下。如果图片是从远端服务加载的你不能管制图片大小,比方在下载前调整到适合大小的话,你能够在下载实现后,最好是用  backgroundthread,缩放一次,而后在 UIImageView中应用缩放后的图片。 7、抉择正确的 Collection。 Arrays:有序的一组值。应用  index来  lookup很快,应用  value lookup很慢,插入/删除很慢。Dictionaries:存储键值对。用键来查找比拟快。Sets:无序的一组值。用值来查找很快,插入/删除很快。8、关上 gzip压缩。app可能大量依赖于服务器资源,问题是咱们的指标是挪动设施,因而你就不能指望网络情况有多好。减小文档的一个形式就是在服务端和你的 app中关上 gzip。这对于文字这种能有更高压缩率的数据来说会有更显著的效用。iOS曾经在  NSURLConnection中默认反对了 gzip压缩,当然  AFNetworking这些基于它的框架亦然。 二、中级1、重用和提早加载(lazy load) Views 更多的  view意味着更多的渲染,也就是更多的 CPU和内存耗费,对于那种嵌套了很多view在UIScrollView里边的  app更是如此。这里咱们用到的技巧就是模拟 UITableView和 UICollectionView的操作:不要一次创立所有的 subview,而是当须要时才创立,当它们实现了使命,把他们放进一个可重用的队列中。这样的话你就只须要在滚动产生时创立你的 views,防止了不划算的内存调配。2、Cache, Cache,还是   Cache! ...

August 25, 2021 · 3 min · jiezi

关于objective-c:八天让iOS开发者上手Flutter七

上一篇文章咱们曾经实现首页聊天页面的导航条和列表展现,明天的工作是实现搜寻cell的展现和点击之后的搜寻页面的性能。 自定义SearchCell新建search_cell.dart文件 实现SearchCell代码SearchCell的话,因为仅仅只是展现,点击之后就进入搜寻页了,应该来说是不须要状态的,所以用一个StatelessWidget就够了。而后布局的形式应用一个Container蕴含一个Row,Row里面包一个图片和文本就能够了。布局的形式其实多种多样,能实现就好了。残缺代码如下: 一个有意思的中央是,flutter外面的Image竟然能够设置色彩,而且色彩是设置给图片的。比方我的放大镜图片本来是彩色的,设置红色之后,竟然真的变红色了!!! 应用SearchCellSearchCell的展现就算写完了,而后在ChatPage.dart中应用,咱们把ListView的itemBuilder办法抽取进去,而后因为咱们多加了一个SearchCell,所以itemCount须要加1,而后取_chatList数据的时候也要解决一下下标。 自定义SearchPage新建search_page.dart文件 简略实现SearchPageSearchPage作为一个页面,应用StatelessWidget必定是无奈胜任的,所以应用一个StatefulWidget。而因为AppBar的款式和咱们须要显示的效果图还是有差异的,所以这里咱们不应用Scaffold提供的AppBar了。咱们自定义一个SearchBar,配合一个ListView来搭建根本的布局。这外面根本没有陈腐的货色,就简略贴一下代码: SearchBar因为是在SearchPage中应用的,所以就间接定义在SearchPage中了,代码也是先简略定义如下: 点击跳转到SearchPage 在搜寻cell外面实现点击办法,而后跳转到SearchPage,显示成果如图: 实现SearchBarSearchBar的布局SearchBar的布局,最外层分为高低两个局部,下面的局部是零碎状态栏的高度。上面的局部就是显示搜寻条的高度。而搜寻条的布局,应用Row分隔为左右两个局部,左侧蕴含放大镜,文本输入框和删除图片。右侧就是一个返回下级页面的勾销。这里次要提一下flutter中的文本框,跟iOS中UITextField真的很不一样,UITextField中左侧的图标,右侧的删除,都是封装在外部的。而在flutter中,文本框TextField真的就只有文本框,没有其余的货色,都须要本人增加。残缺代码如下: SearchBar事件处理勾销的解决点击勾销须要pop到上一个界面,给勾销加一个GestureDetector实现onTap就好了。代码如下: 革除按钮性能实现首先作为一个开发者,有一个学习的气氛跟一个交换圈子特地重要,这是一个我的iOS开发公众号:编程大鑫,不论你是小白还是大牛都欢送入驻 ,让咱们一起提高,独特倒退!(群内会收费提供一些群主珍藏的收费学习书籍材料以及整顿好的几百道面试题和答案文档!)未输出文本的时候,不显示革除按钮。有输出文本的时候,显示革除按钮。点击革除按钮,清空文本内容并暗藏革除按钮。 应用一个bool值_showClear管制革除按钮的显示暗藏。代码如下: 在文本变动的时候,批改_showClear值并刷新状态。文本的变动在TextField的onChanged属性就能够监听到。代码如下: 最初就是革除按钮的点击性能,因为须要清空TextField的文本内容,须要应用到TextEditingController,给TextField的controller属性赋值,而后通过TextEditingController对象革除文本内容。文本革除之后并不会主动调用零碎的onChanged办法,本人手动调用一下就好了。代码如下: SearchBar回调文本框的内容文本框的内容变动的时候,须要回调给SearchBar内部,这样咱们能力在SearchPage页面进行搜寻内容。应用一个回调作为参数就能够实现了。代码如下: onChanged是一个闭包属性,在初始化SearchBar的时候传入,在TextField的文本变动的时候调用闭包,并将文本作为参数回传给SearchBar内部。因为将onChanged作为了必传参数,所以编译器天然会在用到了SearchBar的中央报错。很容易找到报错的中央,加个参数就好了。 SearchBar相干的代码就算差不多实现了,其实能够将SearchBar独自作为一个文件独立进去。接下来就是解决SearchPage了 实现SearchPage搜寻页面的搜寻性能,往细了说,能够搜寻很多内容,咱们这里只是简略的搜寻名字,只有名字蕴含输出的内容,就将搜寻后果展现进去。因为这里对中文名进行搜寻的时候,能匹配到的数据比拟少,所以这里曾经将网络申请返回的名字由中文改为英文名字了。后面展现中文名字的截图就不做批改了。 SearchPage的搜寻性能增加datas数据源要实现SearchPage的搜寻性能,那么它首先必须要有数据源,很显著它的数据源是从首页来的。先定义一个datas,作为必传参数,而后通过内部层层传递过去。 datas定义好了当前,报红色谬误的中央,就是须要传参数的中央,很不便,都不必咱们本人去翻哪里须要加参数了。发现SearchCell外面须要传入数据源,同样的形式,在SearchCell外面定义datas,而后在报错的中央解决。 就这样子顺藤摸瓜,直到来到chat_page将数据源传入就实现了 SearchPage本人的数据源SearchPage须要展现搜寻之后的后果,所以本人定义一个数组用来寄存搜寻的后果。并且临时先应用Text做一个最简略的展现。代码如下: 实现搜寻性能并展现数据搜寻的性能实现很简略,就是判断数据源外面的名字是否蕴含输出的文本,如果蕴含就全副增加并展现。 代码如下: APP上如图,我输出son,显示后果: SearchPage的搜寻后果列表展现SearchPage的搜寻后果列表展现的数据款式,应该和首页是相似的。所以能够间接应用首页的布局。代码如下: SearchPage高亮显示搜到的后果这里的思路是,高亮显示搜到的后果,那么一般的文本必定是不行了,必须是富文本。如何找到搜寻的关键字在文本中的地位呢,这个不必咱们思考了。flutter中对字符串有一个分隔办法split,这个办法跟iOS中的字符串的componentsSeparatedByString:办法相似,依据传入的参数来分隔字符串。这里贴一下iOS的代码: (还是Xcode看着悦目啊)咱们将字符串abcaa以字符a分组,再将分组的后果拼接回原来的字符串。为什么要这么操作?因为从新拼接新字符串的时候,咱们就能够解决富文本字符串了。当初回到flutter中来,flutter中拼接的富文本的形式太不便了,RichText花色拼接TextSpan这个咱们在后面也讲过了。 _searchKey就是咱们输出的文本,在SearchPage中申明属性,在SearchBar的回调中赋值就好了。 当初测试一下,输出son,APP显示如图: 滚动列表叫回键盘ListView的滚动,咱们在后面曾经说过一次,须要将ListView包在NotificationListener外面。而后叫回键盘的代码FocusScope.of(context).requestFocus(FocusNode());这个记住就好了,代码如下: 总结到这里咱们的flutter仿微信Demo性能就差不多完了,还剩最初一篇就是介绍flutter和原生混合开发的一些货色。这个也是理论我的项目中应该常常会遇到的状况。其实写到这里会发现很多货色和原生都是相通的或者相似的。除了新的语言Dart不是很相熟之外,其余很多中央比方很多属性的名字,色彩,闭包,都可能看到原生的影子。flutter创造者们也不会闭门造车,都会去借鉴原生外面的货色。

August 24, 2021 · 1 min · jiezi

关于objective-c:八天让iOS开发者上手Flutter六

筹备网络数据这一步不是很重要,提供一些假数据而已,不是重点嫌麻烦的能够跳过。 先介绍一个网址:点击 这个网址用来搭建咱们须要的网络数据,注册账号非常简单,这里就不多说了。 注册实现之后,新建一个仓库,简简单单取个名字就够了: 之后点击进入仓库,能够看到下图: 会默认生成以一个示例接口,能够看一看示例接口的生成规定。看不懂也没关系,咱们间接间接上手本人新建一个接口,如图所示: 点击右上角的编辑按钮进入编辑模式,新建一个响应chatlist,类型为Array。而后生成chatlist的数据,imageUrl示意每条聊天数据的用户头像。其中用户头像的初始值外面有一段@natural(20,99),这个是Mock.js代码。这里是相干介绍mockjs.com/ 每条聊天数据,除了imageUrl还须要有用户名name和音讯的内容message。@cname用来生成随机的中文名,@cparagraph用来生成随机的中文段落来示意聊天内容。咱们这里只是简略的结构一下这个聊天列表所须要的数据,真正的聊天列表的数据必定是不会这么简略的。。。 编辑的差不多的时候,记得点击保留,保留之后点击红圈中的图标就能够获取到数据 聊天页面导航条筹备工作默认展现页面改为_currentIndex改为0,新建chat目录,将相干文件放在这里。 增加加号按钮加号按钮这个货色,咱们之前曾经增加过相似的了,appBar的actions就是咱们须要增加代码的中央。 如果咱们依照这个思路写下去的话,就须要本人再去实现一个弹出菜单的类。其实flutter提供了咱们一些现成的类能够做到相似的成果。 PopupMenuButtonPopupMenuButton类用来弹出一个菜单,必传参数为itemBuilder,用来实现它须要展现的内容。PopupMenuItem就是用来展现内容的类。这里有一个细节说一下,PopupMenuButton有一个onSelected属性,这个属性是个闭包,意思是选中某个PopupMenuItem的时候,会调用这个闭包。然而有一个前提就是每个PopupMenuItem的value必须不为null的时候,才会执行onSelected闭包,我在这里卡了半天,网上找了半天材料也没有明确讲到这里。其余就没什么好讲的了,都比较简单。 还有一个小细节,如何设置PopupMenuButton的色彩,能够间接设置它的色彩 还能够设置APP的主题的cardColor,不过这个优先级没有间接设置PopupMenuButton色彩高。 申请网络数据pub.flutter-io.cn/ 这个网站能够搜寻flutter应用的包packages。咱们应用http这个包来申请咱们的网络数据。这个包是flutter官网提供的。理论我的项目开发的时候可能并不会应用http这个包,大部分是应用dio来申请网络数据。这里只介绍官网的http包如何应用。 导入http包首先作为一个开发者,有一个学习的气氛跟一个交换圈子特地重要,这是一个我的iOS开发公众号:编程大鑫,不论你是小白还是大牛都欢送入驻 ,让咱们一起提高,独特倒退!(群内会收费提供一些群主珍藏的收费学习书籍材料以及整顿好的几百道面试题和答案文档!) 粘贴实现之后须要更新一下,就是获取一下包对应的代码。能够通过上方的Pub get,也能够在终端中输出flutter packages get 命令执行完之后,就能够应用这个包了。 导入http头文件并取别名 发送申请 申请的发送写在initState外面。getData()前面跟了一个async示意的是异步执行。async须要搭配await应用,await前面跟着的是耗时的代码。所以下面的程序会先打印来了,而后再输入申请的状态码; 点击其余的页面,再次回到以后页面会发现initState办法从新走了一遍,这是因为咱们还没有保留住状态,前面会讲到如何放弃Widget的状态。 解决返回的数据首先介绍一下,在flutter中如何将申请返回的JSON数据转为Map,在咱们iOS开发中是转为字典,而flutter中没有字典这个类型,对应的类型是Map。以及如果将Map转为JSON。在iOS中咱们晓得会应用一个NSJSONSerialization的类用来解决JSON数据。同样的,在flutter中也会有一个专门的类JsonCodec来解决。 JSON和Map相互转 其中的json就是JsonCodec的实例。须要导入'dart:convert'头文件。flutter中还能够通过is来判断是不是某个类型。 新建聊天模型 除了红框内的办法之外,其余没什么陈腐事物。红框内的办法应该说是一个工厂办法,是设计模式的一种,用来初始化对象的。除了默认的初始化办法,还能够应用这个工厂办法来实例化一个Chat对象。模型建设好了之后就能够解决响应的数据了 解决响应的数据 这里用到了Future,对于Future的解说能够看官网文档dart.cn/tutorials/l… 应用FutureBuilder显示界面很多时候咱们会依赖一些异步数据来动静更新UI,比方在关上一个页面时咱们须要先从互联网上获取数据,在获取数据的过程中咱们显示一个加载框,等获取到数据时咱们再渲染页面;当然,通过StatefulWidget咱们齐全能够实现上述这些性能。但因为在理论开发中依赖异步数据更新UI的这种场景十分常见,因而Flutter专门提供了FutureBuilder来疾速实现这种性能。FutureBuilder会依赖一个future,它会依据所依赖的future的状态来动静构建本身。这个future咱们刚刚曾经筹备好了。对于FutureBuilder的介绍就不具体介绍了。 最初残缺代码如下: 不应用FutureBuilder的形式刚刚说了,咱们能够应用FutureBuilder来疾速实现展现异步网络数据,也能够本人实现,当初咱们本人实现一下。应用一个公有变量_chatList记录申请下来的数据,再依据_chatList的值来展现不同的页面。代码如下: 因为getData的返回值是Future的起因,getData()前面能够跟then办法,还能够跟谬误捕捉catchError,实现时的回调whenComplete,超时设置timeOut等等,这种写法挺有意思,一路点上来就完了... 实现超时勾销刷新性能 在每次申请的时候,重置_cancelConnect的值。 放弃Widget的状态咱们来回点击通讯录页面和聊天页面,会发现每次点击进入以后的页面,都会从新的载入,initState()会被从新调用。将通讯录页面滑动到底部,再次点击进入会发现又默认回到了顶部。为什么会呈现这样的问题。正是因为咱们的Widget的状态没有放弃,每次展现都从新创立了。 Dart语言中有一个Mixins的概念:官网的解释是这样,能够给类A Mixins 一个B,那么A就领有了B的属性和办法。有点像OC的类扩大的意思。放弃Widget的状态就须要用到这个语言个性。一共有三个步骤: 1. Mixins 类AutomaticKeepAliveClientMixin 重写wantKeepAlive办法调用父类Builder办法 同样把通讯录页面也实现下面的步骤。而后再次来回点击发现还是没有放弃住状态???这里有一个最大的问题就是咱们的根Widget都没有放弃住状态,那还谈什么放弃子Widget的状态呢。。。 应用PageView来到rootPage.dart文件,咱们会看到body外面间接取了数组的某个元素作为根Widget展现。这样是无奈放弃住状态的,应用PageView才能够放弃住状态。 ...

August 19, 2021 · 1 min · jiezi

关于objective-c:iOS开发面试只需知道这些技术基本通关数据结构与算法篇附加安全加密

一、数据结构汇合构造 线性构造 树形构造 图形构造 1.1、汇合构造 说白了就是一个汇合,就是一个圆圈中有很多个元素,元素与元素之间没有任何关系 这个很简略 1.2、线性构造 说白了就是一个条线上站着很多集体。 这条线不肯定是直的。也能够是弯的。也能够是值的 相当于一条线被分成了好几段的样子 (施展你的想象力)。 线性构造是一对一的关系 1.3、树形构造 说白了 做开发的必定或多或少的晓得 xml 解析 树形构造跟他十分相似。也能够设想成一个金字塔。树形构造是一对多的关系 1.4、图形构造 这个就比较复杂了。他呢 无穷。无际 无向(没有方向)图形机构 你能够了解为多对多 相似于咱们人的交加关系 数据结构的存储 数据结构的存储个别罕用的有两种 顺序存储构造 和 链式存储构造 2.1 顺序存储构造 施展想象力啊。 举个列子。数组。1-2-3-4-5-6-7-8-9-10。这个就是一个顺序存储构造 ,存储是按程序的 举例说明啊。 栈,做开发的都相熟。栈是先进后出 ,后进先出的模式 对不对 ? 他的你能够这样了解,hello world 在栈外面从栈底到栈顶的逻辑顺次为 h-e-l-l-o-w-o-r-l-d 这就是顺序存储,再比方队列 ,队列是先进先出的对吧,从头到尾 h-e-l-l-o-w-o-r-l-d 就是这样排对的 2.2 链式存储构造 再次施展想象力 这个略微简单一点 这个图片我始终弄好 ,回头找美工问问,再贴上 例如 还是一个数组1-2-3-4-5-6-7-8-9-10 链式存储就不一样了 1(地址)-2(地址)-7(地址)-4(地址)-5(地址)-9(地址)-8(地址)-3(地址)-6(地址)-10(地址)。每个数字前面跟着一个地址 而且存储模式不再是程序 ,也就说程序乱了,1(地址)1 前面跟着的这个地址指向的是 2,2 前面的地址指向的是 3,3 前面的地址指向是谁你应该分明了吧。他执行的时候是 1(地址)-2(地址)-3(地址)-4(地址)-5(地址)-6(地址)-7(地址)-8(地址)-9(地址)-10(地址),然而存储的时候就是齐全随机的。明确了? 单向链表\双向链表\循环链表 还是举例子。了解最重要。不要去死记硬背 哪些什么。定义啊。逻辑啊。了解才是最重要滴 3.1 单向链表 A->B->C->D->E->F->G->H. 这就是单向链表 H 是头 A 是尾 像一个只有一个头的火车一样 只能一个头拉着跑 3.2 双向链表 数组和链表区别: ...

August 17, 2021 · 2 min · jiezi

关于objective-c:北京一卡通APP-iOS研发手册v101

前言一、我的项目架构 我的项目依照代码的组织构造和调用关系,能够分为四层,别离是视图层、业务层、根底模块和数据拜访层。其中视图层负责页面展现和与用户交互(如用户的点击事件、划动手势等),业务层则对应具体的业务,例如充值模块、用户信息、乘车码、多扣票款等,根底模块蕴含了我的项目运行各个性能最根本的能力和组件,比方网络模块负责业务的网络申请,平安模块负责报文的加解密和签名验签,存储模块负责本地的数据长久化等等,数据拜访层是对我的项目所用到的所有信息的获取形式的形象和封装,包含从接口获取数据,从本地资源文件、本地缓存以及数据库等获取数据。 上面别离对各个模块做下介绍: 1、视图层2、业务层3、根底模块4、数据拜访层二、我的项目目录构造1、工程目录构造工程目录构造如下: 我的项目采纳了cocoapods的集成形式,目录构造次要分为Base、Common、Macro、Main、Resources、Sources、Supporting Files、Vendors、Pods。 其中, Base目录下是我的项目用到的根底ViewController的封装,蕴含加载WebView的,加载原生页面的,曾经他们的独特基类。Common目录蕴含了我的项目用到的公共代码。Macro是对我的项目罕用的宏的汇总。Main是我的项目的主代码目录。Resources是我的项目的资源文件,蕴含图片、预置离线包等。Sources是程序启动相干的源文件。Supporting Files蕴含了main.m也就是程序的入口文件,还有info.plist程序的配置文件,还有启动图等等。Vendors是手动导入的第三方库。Pods目录下是通过pod导入的第三方库。2、mPaas目录构造mPaaS目录构造如下: └── MPaaS ├── mpaas_sdk.config ├── Targets | └── mPaaSDemo(工程 Target 名称) | ├── mPaaSDemo-mPaaS-Headers.h | ├── mPaaSDemo-Prefix.pch | ├── APMobileFramework | ├── mPaas | ├── meta.config | └── yw_1222.jpg ├── Resources └── Frameworks其中, mpaas_sdk.config:以后工程增加的模块信息,包含版本、增加工夫、资源文件等,由 mPaaS 插件主动保护,不得手动批改。mPaaSDemo-mPaaS-Headers.h:以后工程依赖的 mPaaS 模块的头文件,由 mPaaS 插件主动保护,不得手动批改。mPaaSDemo-Prefix.pch:以后工程 pch 文件的援用,会主动将 mPaaSdemo-mPaaS-Headers.h 退出 mPaaS 模块的头文件。APMobileFramework:mpaas 框架的生命周期治理的 category 文件。mPaas:MPaaSInterface 的 category 文件。meta.config:从 mPaaS 控制台下载的云端元数据。yw_1222.jpg:通过元数据中的 base64code 字段生成的无线保镖验签图片,在挪动网关验签时应用。如不须要挪动网关性能,可删除此图片。Resources & Frameworks:mPaaS 模块的资源文件和二进制文件目录,是以后工程所有 Targets 应用的 mPaaS 模块的并集,由 mPaaS 插件主动保护,不得手动批改。三、开发流程标准1、需要评审 产品在明确需要之后,通过邮件收回需要文档,而后招集大家一起进行需要评审。参加需要评审的人个别蕴含负责该项目标产品经理,已及相干测试人员,负责开发的前后端开发人员,局部重要我的项目领导也会参加评审。 ...

August 17, 2021 · 3 min · jiezi

关于objective-c:iOS开发面试只需知道这些技术基本通关多线程篇

一、过程、线程过程: · 1.过程是一个具备肯定独立性能的程序对于某次数据汇合的一次运行流动,它是操作系统分配资源的根本单元. · 2.过程是指在零碎中正在运行的一个应用程序,就是一段程序的执行过程,咱们能够了解为手机上的一个 app. · 3.每个过程之间是独立的,每个过程均运行在其专用且受爱护的内存空间内,领有独立运行所需的全副资源 线程 · 1.程序执行流的最小单元,线程是过程中的一个实体. · 2.一个过程要想执行工作,必须至多有一条线程.应用程序启动的时候,零碎会默认开启一条线程,也就是主线程 过程和线程的关系 · 1.线程是过程的执行单元,过程的所有工作都在线程中执行 · 2.线程是CPU 分配资源和调度的最小单位 · 3.一个程序能够对应多个过程(多过程),一个过程中可有多个线程,但至多要有一条线程 · 4.同一个过程内的线程共享过程资源 二、多过程、多线程多过程 关上 mac 的流动监视器,能够看到很多个过程同时运行 · 过程是程序在计算机上的一次执行流动。当你运行一个程序,你就启动了一个过程。显然,程序是死的(动态的),过程是活的(动静的)。 · 过程能够分为零碎过程和用户过程。但凡用于实现操作系统的各种性能的过程就是零碎过程,它们就是处于运行状态下的操作系统自身;所有由用户启动的过程都是用户过程。过程是操作系统进行资源分配的单位。 · 过程又被细化为线程,也就是一个过程下有多个能独立运行的更小的单位。在同一个工夫里,同一个计算机系统中如果容许两个或两个以上的过程处于运行状态,这便是多过程。 多线程 1. 同一时间,CPU 只能解决 1 条线程,只有 1 条线程在执行。多线程并发执行,其实是 CPU 疾速地在多条线程之间调度(切换)。如果 CPU 调度线程的工夫足够快,就造成了多线程并发执行的假象 2. 如果线程十分十分多,CPU 会在 N 多线程之间调度,耗费大量的 CPU 资源,每条线程被调度执行的频次会升高(线程的执行效率升高) 3. 多线程的长处: 能适当进步程序的执行效率能适当进步资源利用率(CPU、内存利用率)  多线程的毛病:开启线程须要占用肯定的内存空间(默认状况下,主线程占用 1M,子线程占用 512KB),如果开启大量的线程,会占用大量的内存空间,升高程序的性能线程越多,CPU 在调度线程上的开销就越大 程序设计更加简单:比方线程之间的通信、多线程的数据共享 三、工作、队列首先作为一个开发者,有一个学习的气氛跟一个交换圈子特地重要,这是一个我的iOS开发公众号:编程大鑫,不论你是小白还是大牛都欢送入驻 ,让咱们一起提高,独特倒退!(群内会收费提供一些群主珍藏的收费学习书籍材料以及整顿好的几百道面试题和答案文档!)工作 就是执行操作的意思,也就是在线程中执行的那段代码。在 GCD 中是放在 block 中的。执行工作有两种形式:同步执行(sync)和异步执行(async) 同步(Sync):同步增加工作到指定的队列中,在增加的工作执行完结之前,会始终期待,直到队列外面的工作实现之后再继续执行,即会阻塞线程。只能在以后线程中执行工作(是以后线程,不肯定是主线程),不具备开启新线程的能力。 异步(Async):线程会立刻返回,无需期待就会继续执行上面的工作,不阻塞以后线程。能够在新的线程中执行工作,具备开启新线程的能力(并不一定开启新线程)。如果不是增加到主队列上,异步会在子线程中执行工作 队列 队列(Dispatch Queue):这里的队列指执行工作的期待队列,即用来寄存工作的队列。队列是一种非凡的线性表,采纳 FIFO(先进先出)的准则,即新工作总是被插入到队列的开端,而读取工作的时候总是从队列的头部开始读取。每读取一个工作,则从队列中开释一个工作在 GCD 中有两种队列:串行队列和并发队列。两者都合乎 FIFO(先进先出)的准则。两者的次要区别是:执行程序不同,以及开启线程数不同。 l 串行队列(Serial Dispatch Queue): 同一时间内,队列中只能执行一个工作,只有以后的工作执行实现之后,能力执行下一个工作。(只开启一个线程,一个工作执行结束后,再执行下一个工作)。主队列是主线程上的一个串行队列,是零碎主动为咱们创立的 l 并发队列(Concurrent Dispatch Queue): ...

August 12, 2021 · 3 min · jiezi

关于objective-c:iOS开发面试只需知道这些技术基本通关Runtime篇

一、objc对象的isa的指针指向什么?有什么作用?指向他的类对象,从而能够找到对象上的办法 详解:下图很好的形容了对象,类,元类之间的关系: 图中实线是 super_class 指针,虚线是 isa 指针。 1. Root class (class)其实就是 NSObject,NSObject 是没有超类的,所以 Root class(class)的 superclass指向 nil。 2. 每个 Class 都有一个 isa 指针指向惟一的 Meta class 3. Root class(meta)的 superclass 指向 Root class(class),也就是 NSObject,造成一个回路。 4. 每个 Meta class 的 isa 指针都指向 Root class (meta)。 二、一个NSObject对象占用多少内存空间?受限于内存调配的机制,一个 NSObject对象都会调配 16byte 的内存空间。 然而实际上在 64 位 下,只应用了 8byte;在 32 位下,只应用了 4byte 一个 NSObject 实例对象成员变量所占的大小,实际上是 8 字节 实质是 获取 Obj-C指针所指向的内存的大小,实际上是 16 字节 对象在分配内存空间时,会进行内存对齐,所以在 iOS 中,分配内存空间都是 16 字节 的倍数。能够通过以下网址 :openSource.apple.com/tarballs来查看源代码。 三、说一下对class_rw_t的了解?rw 代表可读可写。 ObjC 类中的属性、办法还有遵循的协定等信息都保留在class_rw_t 中: 四、说一下对class_ro_t的了解?存储了以后类在编译期就曾经确定的属性、办法以及遵循的协定。 五、说一下对isa指针的了解说一下对 isa 指针的了解, 对象的isa 指针指向哪里?isa 指针有哪两种类型? isa等价于 is kind of · 实例对象 isa 指向类对象 · 类对象指 isa 向元类对象 · 元类对象的 isa 指向元类的基类 ...

August 6, 2021 · 3 min · jiezi

关于objective-c:iOS开发面试只需知道这些技术基本通关block篇

一、什么是Block?Block 是将函数及其执行上下文封装起来的对象。比方: 通过 clang -rewrite-objc WYTest.m 命令编译该.m文件,发现该 block 被编译成这个模式: 其中 WYTest 是文件名,blockTest 是办法名,这些能够疏忽。其中WYTest blockTest_block_impl_0 构造体为 --block_impl 构造体为 block外部有 isa指针,所以说其本质也是 OC 对象 block 外部则为: 所以说 Block 是将函数及其执行上下文封装起来的对象 既然 block 外部封装了函数,那么它同样也有参数和返回值。 二、Block变量截获1、局部变量截获 是值截获。 比方: 这里的输入是 6 而不是 2,起因就是对局部变量 num的截获是值截获。同样,在 block里如果批改变量 num,也是有效的,甚至编译器会报错。 打印为 1,2,3 部分对象变量也是一样,截获的是值,而不是指针,在内部将其置为 nil,对 block 没有影响,而该对象调用办法会影响 2、部分动态变量截获 是指针截获。 输入为 2,意味着 num = 1 这里的批改 num 值是无效的,即是指针截获。同样,在 block 里去批改变量 m,也是无效的。 首先作为一个开发者,有一个学习的气氛跟一个交换圈子特地重要,这是本鑫一个iOS开发公众号:编程大鑫,不论你是小白还是大牛都欢送入驻 ,让咱们一起提高,独特倒退!(群内会收费提供一些群主珍藏的收费学习书籍材料以及整顿好的几百道面试题和答案文档!)3、全局变量,动态全局变量截获:不截获,间接取值。咱们同样用 clang 编译看下后果。 编译后 ( impl.isa= &_NSConcreteStackBlock;这里留神到这一句,即阐明该 block是栈 block) 能够看到局部变量被编译成值模式,而动态变量被编成指针模式,全局变量并未截获。而--block 润饰的变量也是以指针模式截获的,并且生成了一个新的构造体对象: 该对象有个属性:num5,即咱们用--block 润饰的变量。这里--forwarding是指向本身的(栈 block)。 个别状况下,如果咱们要对 block 截获的局部变量进行赋值操作需增加--block 修饰符,而对全局变量,动态变量是不须要增加--block 修饰符的。 ...

August 2, 2021 · 1 min · jiezi

关于objective-c:iOS底层面试题中篇

7月,iOS求职跳槽的绝对较少,能在这个时间段求职的,不是被迫,就是对本人的技术很自信;针对7月,特地总结了第二份iOS常见大厂面试题(中); iOS面试题分为 上、中、下三局部,不便大家观看; 请先本人答一答! 话不多说;间接上题本文收录:公众号【iOS进阶宝典《iOS底层面试题(中篇)》】 6: iOS中内省的几个办法?class办法和objc_getClass办法有什么区别?1: 什么是内省?在计算机科学中,内省是指计算机程序在运行时(Run time)查看对象(Object)类型的一种能力,通常也能够称作运行时类型查看。 不应该将内省和反射混同。绝对于内省,反射更进一步,是指计算机程序在运行时(Run time)能够拜访、检测和批改它自身状态或行为的一种能力。2:iOS中内省的几个办法?isMemberOfClass //对象是否是某个类型的对象isKindOfClass //对象是否是某个类型或某个类型子类的对象isSubclassOfClass //某个类对象是否是另一个类型的子类isAncestorOfObject //某个类对象是否是另一个类型的父类respondsToSelector //是否能响应某个办法conformsToProtocol //是否遵循某个协定3:class办法分类办法和对象办法。实例class办法就间接返回object_getClass(self)类class办法间接返回self 4.object_getClass()获取的是类型,对象.isa --->类.isa --->元类.isa ---> 父元类.isa ---> 根元类.isa ---> 本人(还是根元类)7: 分类和扩大有什么区别?能够别离用来做什么?分类有哪些局限性?分类的构造体外面有哪些成员?1:分类次要用来为某个类增加办法,属性,协定(我个别用来为零碎的类扩大办法或者把某个简单的类的依照性能拆到不同的文件里)2:扩大次要用来为某个类原来没有的成员变量、属性、办法。注:办法只是申明(我个别用扩大来申明公有属性,或者把.h的只读属性重写成可读写的)分类和扩大的区别: 分类是在运行时把分类信息合并到类信息中,而扩大是在编译时,就把信息合并到类中的分类申明的属性,只会生成对应的成员变量,不会有getter/setter办法的申明和实现,而扩大会有。分类不可用为类增加实例变量,而扩大能够分类能够为类增加办法的实现,而扩大只能申明办法,而不能实现分类的局限性: 无奈为类增加实例变量,但可通过关联对象进行实现,注:关联对象中内存治理没有weak,用时须要留神野指针的问题,可通过其余方法来实现,具体可参考iOS weak 关键字漫谈分类的办法若和类中本来的实现重名,会笼罩本来办法的实现,注:并不是真正的笼罩 多个分类的办法重名,会调用最初编译的那个分类的实现 分类的构造体里有哪些成员 struct category_t { const char *name; //名字 classref_t cls; //类的援用 struct method_list_t *instanceMethods;//实例办法列表 struct method_list_t *classMethods;//类办法列表 struct protocol_list_t *protocols;//协定列表 struct property_list_t *instanceProperties;//实例属性列表 // 此属性不肯定真正的存在 struct property_list_t *_classProperties;//类属性列表};8:能不能简述一下 Dealloc 的实现机制Dealloc 的实现机制是内容治理局部的重点,把这个知识点弄明确,对于全方位的了解内存治理的只是很有 必要。 1.Dealloc 调用流程 1.首先调用 _objc_rootDealloc()2.接下来调用 rootDealloc()3.这时候会判断是否能够被开释,判断的根据次要有 5 个,判断是否有以上五种状况 ...

July 27, 2021 · 1 min · jiezi

关于objective-c:IOS开发的基础语言是什么

据说是一个什么面向对象的C的,那是什么语言啊,java根底的人能学吗? IOS开发的根底语言是objective-c,有Java根底学起来更加轻松。 IOS开发应用的语言是objective-c(也称object-c),是基于C++的。iOS开发的规范语言是objective-c。是c的一种超集, 它是对c的扩大,反对面向对象编程。像起初的一些高级语言java,c#等都借鉴了该语言的面向对象个性。 当然在iOS开发过程中,也反对c/c++语言与原生的objective-c混编。 扩大材料: Objective-C的长处和特点 根本特点Objective-C是十分实用的语言。它是一个用C写成很小的运行库,令应用程序的尺寸减少很小,和大部分OO零碎应用极大的VM执行工夫会取代了整个零碎的运作相同。Objective-C写成的程序通常不会比其原始码大很多。而其函式库(通常没附在软件发行本)亦和Smalltalk零碎要应用极大的内存来开启一个窗口的状况相同。因而,Objective-C它齐全兼容规范C语言(C++对C语言的兼容仅在于大部分语法上,而在ABI(Application Binary Interface)上,还须要应用extern "C"这种显式申明来与C函数进行兼容),而在此基础上减少了面向对象编程语言的个性以及Smalltalk音讯机制。 Objective-C的最后版本并不支[图片上传中...(vx二维码.jpg-a299f5-1625042289432-0)]持垃圾回收。在过后这是争执的焦点之一,很多人思考到Smalltalk回收时有漫长的死亡工夫,令整个零碎失去功能。Objective-C为防止此问题才不领有这个性能。尽管某些第三方版本已退出这个性能(尤是GNUstep), Apple在其Mac OS X 10.3中仍未引入这个性能。 另外,小编再通知大家,学习一门编程语言有点根底总是好的. 没有根底的话学习起来可能要艰难一些. 如果你有点java根底应该了解起来不是太艰难.但必竟两者不同,在编程个性和语言自身上还是有大差异的. 文章接下来还会继续更新,你也能够私信我及时获取最新材料以及面试相干材料。如果你有什么意见和倡议欢送给我留言。 #### 求喜爱IOS的小伙伴关注 !喜爱的话给一个赞吧!谢谢!谢谢!谢谢!

July 1, 2021 · 1 min · jiezi

关于objective-c:iOS处理多个网络请求的先后依赖关系

在iOS开发中咱们常常会遇到多网络申请的问题,有时候须要在一个网络申请完结之后再去申请另一个网络,其实最简略也是最low的办法就是在一个网络申请胜利的回调中再去申请另外一个接口信息应用信号量计数器来实现的代码如下: static NSString *userSign;static NSString *userId;dispatch_group_t group = dispatch_group_create();dispatch_semaphore_t semaphore = dispatch_semaphore_create(0);dispatch_queue_t queue = dispatch_queue_create(NULL, DISPATCH_QUEUE_SERIAL);dispatch_group_async(group, queue, ^{ [[BFLoginService shareInstance] getImSignWith: @"gjs598" complete:^(id _Nonnull data, RequestError * _Nonnull error) { // 工作1 NSLog(@"获取im签名实现%@", data[@"UserSig"]); userSign = data[@"UserSig"]; dispatch_semaphore_signal(semaphore); }]; dispatch_semaphore_wait(semaphore, DISPATCH_TIME_FOREVER);});dispatch_group_async(group, queue, ^{ [[BFLoginService shareInstance] getImUserId:^(id _Nonnull data, RequestError * _Nonnull error) { // 工作2 NSLog(@"获取用户Id实现%@", data[@"userId"]); userId = data[@"userId"]; dispatch_semaphore_signal(semaphore); }]; dispatch_semaphore_wait(semaphore, DISPATCH_TIME_FOREVER);});dispatch_group_notify(group, queue, ^{ NSLog(@"啊啊啊啊啊啊%@ ----- %@", userId, userSign); // 工作1/工作2都实现了, 当初执行工作3 [[V2TIMManager sharedInstance] login: userId userSig: userSign succ:^{ // NSLog(@"id登陆胜利"); // @weakify(self) [[V2TIMManager sharedInstance] getConversationList: INT_MAX count: INT_MAX succ: ^(NSArray<V2TIMConversation *> *list, uint64_t lastTS, BOOL isFinished) { // @strongify(self) // [self updateConversation: list]; NSLog(@"会话列表%@", list); } fail:^(int code, NSString *msg) { NSLog(@"拉取会话列表失败"); }]; } fail:^(int code, NSString *desc) { NSLog(@"id登陆失败了----------"); NSLog(@"%@", desc); }];});

May 17, 2021 · 1 min · jiezi

关于objective-c:objectivec入门

一. objective-c简介Objective-Objective-C是C语言的严格超集--任何C语言程序不经批改就能够间接通过Objective-C编译器,在Objective-C中应用C语言代码也是齐全非法的。Objective-C被形容为盖在C语言上的薄薄一层,因为Objective-C的原意就是在C语言主体上退出面向对象的个性。扩展名内容类型.h头文件。头文件蕴含类,类型,函数和常数的申明。蕴含头文件时,#import 选项和 #include 选项完全相同,只是它能够确保雷同的文件只会被蕴含一次。.m源代码文件。这是典型的源代码文件扩展名,能够蕴含 Objective-C 和 C 代码。.mm源代码文件。带有这种扩展名的源代码文件,除了能够蕴含Objective-C和C代码以外还能够蕴含C++代码。仅在你的Objective-C代码中的确须要应用C++类或者个性的时候才用这种扩展名。二. 语法1. 字符串大多数Objective-C通常不应用C语言格调的字符串。反之,大多数框架把字符串传递给NSString对象。NSString类提供了字符串的类包装,蕴含了所有你冀望的长处,包含对保留任意长度字符串的内建内存管理机制,反对Unicode,printf格调的格式化工具NSString* myString = @"My Stringn"; NSString* anotherString = [NSString stringWithFormat:@"%d %s", 1, @"String"];2. 类Objective-C 的类规格阐明蕴含了两个局部:定义(interface)与实现(implementation)。定义(interface)局部蕴含了类申明和实例变量的定义,以及类相干的办法。实现(implementation)局部蕴含了类办法的理论代码。interface 定义局部,分明定义了类的名称、数据成员和办法。 以关键字@interface作为开始,@end作为完结@interface MyObject : NSObject { int memberVar1; // 实体变量 id memberVar2; } +(return_type) class_method; // 类办法 -(return_type) instance_method1; // 实例办法 -(return_type) instance_method2: (int) p1; -(return_type) instance_method3: (int) p1 andPar: (int) p2; @end//办法后面的 +/- 号代表函数的类型:加号(+)代表类办法(class method),不须要实例就能够调用,与C++ 的动态函数(static member function)类似。减号(-)即是个别的实例办法(instance method)。//Objective-C定义一个新的办法时,名称内的冒号(:)代表参数传递,不同于C语言以数学函数的括号来传递参数。Objective-C办法使得参数能够夹杂于名称两头,不用全副附缀于办法名称的尾端,能够进步程序可读性。设定色彩RGB值的办法为例: - (void) setColorToRed: (float)red Green: (float)green Blue:(float)blue; /* 宣告办法*/ [myColor setColorToRed: 1.0 Green: 0.8 Blue: 0.2]; /* 呼叫办法*///这个办法的签名是setColorToRed:Green:Blue:。每个冒号前面都带着一个float类别的参数,别离代表红,绿,蓝三色。Implementation ...

April 8, 2021 · 1 min · jiezi

关于objective-c:iOS面试高薪进阶-你会这些呢嘛都是你需要的

这个栏目将继续更新--请iOS的小伙伴关注!做这个的初心是心愿能坚固本人的基础知识,当然也心愿能帮忙更多的开发者!根底>剖析>总结 面试iOS常见根底面试题(附参考答案)iOS底层原理之局部面试题剖析iOS 涨薪: Run Loop 面试题iOS面试反思总结iOS面试题文案及答案附件面试心得iOS 新东方面试面试iOS招聘题 机会在本人手中面试题 拓展:罕用框架和第三方框架Objective-CObjective-C与Swift的贯通编程Objective-C自定义UITextViewObjective-C语言的动态性Objective-C摸索Category底层的本质iOS 安防 优化优化iOS小技巧iOS 内存治理总结iOS开发---数据结构iOS平安攻防—常用工具iOS 多线程 线程间的状态iOS平安:Mach-O Type iOS 各种UI控件属性设置iOS平安根底之钥匙串与哈希iOS自动化布局-AutoLayout束缚优先级SwiftSwift开发之泛型实例Swift实现代码 iOS架构模式之MVPSwift WKWebView与JS的交互应用Swift进阶之路——单例模式、属性传值、代理传值、闭包传值Swift、OC别离实现用" | "隔开数组且只显示一行的小性能

November 6, 2020 · 1 min · jiezi

关于objective-c:ObjectiveC与Swift的混合编程

Swift 被设计用来无缝兼容 Cocoa 和 Objective-C 。在 Swift 中,你能够应用 Objective-C 的 API(包含零碎框架和你自定义的代码),你也能够在 Objective-C中 应用 Swift 的 API。这种兼容性使 Swift 变成了一个简略、不便并且弱小的工具集成到你的 Cocoa 利用开发工作流程中。上面通过一个案例演示,实现Swift与Object-C的混合编程。 作为一个开发者,有一个学习的气氛跟一个交换圈子特地重要,这有个iOS交换群:642363427,不论你是小白还是大牛欢送入驻 ,分享BAT,阿里面试题、面试教训,探讨技术! 步骤一:创立工程文件,名为Person。留神抉择编程语言为Swift。 步骤二:接下来就是要实现OC跟Swift的混合编程啦!首先创立一个Person类将他退出到工程中,语言选择为:Objective-C 步骤三:单击Finsh按钮,会呈现下图中的提示框,此处单击YES,零碎会主动生成桥接文件。 这是能够看到,零碎曾经创立出一个名为Person-Bridging-Header.h文件啦!,而后选中该文件将#import "Person.h"蕴含进去 这是咱们拷贝下零碎创立的桥接文件名,在工程中进行搜寻,能够看到配置文件 步骤四:Person类创立好后,咱们先不必去写代码,接下来再去创立一个House类,不过此类是Swift语言编写的。 在House类中,定义成员变量,初始化办法,以备Person类调用。 为避免前期,连贯时无奈应用,此处对该文件进行编译,如下图。 步骤五:剩下来要做的工作就是编写代码啦!手写在Person类中应用前向申明调用House,而后申明几个成员变量, 为之后测试做筹备,在Person.m文件中去重写description办法,下图中的选中局部,是零碎桥接时生成的文件。 步骤六:在控制器中应用Person和House 步骤七:打印输出后果

October 21, 2020 · 1 min · jiezi

关于objective-c:iOS14剪切板弹提示一探究竟附淘宝实现方法分析和demo

随着iOS 14的公布,剪切板的滥用也被大家所通晓。只有是APP读取剪切板内容,零碎都会在顶部弹出揭示,而且这个揭示不可能敞开。这样,大家在应用APP的过程中就可能看到哪些APP应用了剪切板。 正好咱们本人的利用也应用了剪切板,降级了iOS 14之后弹的着实让人心烦。就想着怎么解决一下,翻了一下UIPasteboard的文档,发现相干的内容并不多。读取UIPasteboard的string、strings、URL、URLs、image、images、color、colors的时候会触发零碎提醒。应用hasStrings、hasURLs、hasImages、hasColors等办法的时候不会触发零碎提醒。那么思路就是尽可能少的去调用会触发零碎提醒的办法,依据其余办法去判断的确须要读取的时候再去调用那些办法。依据咱们本人的状况,只有判断hasStrings为YES就去读取,又不能清剪切板,其实还是有点不尽人意,而后又看到这个属性: @property(readonly, nonatomic) NSInteger changeCount;The number of times the pasteboard’s contents have changed.Whenever the contents of a pasteboard changes—specifically, when pasteboard items are added, modified, or removed—UIPasteboard increments the value of this property. After it increments the change count, UIPasteboard posts the notifications named UIPasteboardChangedNotification (for additions and modifications) and UIPasteboardRemovedNotification (for removals). These notifications include (in the userInfo dictionary) the types of the pasteboard items added or removed. Because UIPasteboard waits until the end of the current event loop before incrementing the change count, notifications can be batched. The class also updates the change count when an app reactivates and another app has changed the pasteboard contents. When users restart a device, the change count is reset to zero.而后就又加了一个条件,记录一下真正读取剪切板时的changeCount,如果下次读取的时候没有发生变化则不读取。这样一来成果就好多了,利用运行生命周期内,基本上只会弹出一次提醒。 ...

September 29, 2020 · 2 min · jiezi

关于objective-c:Swift语法全面解析

Swift介绍 Swift 是一门开发 iOS, macOS, watchOS 和 tvOS 利用的新语言。 Swift是一种平安,疾速和互动的编程语言。 Swift反对代码预览(playgrounds),这个个性能够容许程序员在不编译和运行应用程序的前提下运行 Swift 代码并实时查看后果。 Swift 通过采纳古代编程模式来防止大量常见编程谬误: 变量始终在应用前初始化。查看数组索引超出范围的谬误。查看整数是否溢出。可选值确保明确解决 nil 值。内存被主动治理。错误处理容许从意外故障管制复原。根底局部 常量和变量 申明常量和变量, 常量和变量必须在应用前申明,应用 let 来申明常量,应用 var 来申明变量。 示例: let maximumNumberOfLoginAttempts = 10 var currentLoginAttempt = 0 // 类型注解 var welcomeMessage: String 正文 单行正文双正斜杠(//), 多行正文(/* 多行的 */)。 Swift 的多行正文能够嵌套在其它的多行正文之中。 示例: // 这是一个正文 /* 这也是一个正文, 然而是多行的 */ /* 这是第一个多行正文的结尾 /* 这是第二个被嵌套的多行正文 */ 这是第一个多行正文的结尾 */ 分号 Swift 并不强制要求你在每条语句的结尾处应用分号(;)。 同一行内写多条独立的语句必须用分号分隔。 ...

August 26, 2020 · 16 min · jiezi

关于objective-c:ObjectiveC之数据存储漫谈

存储形式介绍NSKeyedArchiver: 采纳归档的模式来保留数据沙盒中;NSUserDefaults:偏好设置数据存到沙盒的Library/Preferences目录(实质是plist);Write写入形式: 永恒保留在磁盘中;采纳SQLite等数据库来存储数据。1.NSKeyedArchiver:(归档)采纳归档的模式来保留数据,该数据对象须要恪守NSCoding协定,并且该对象对应的类必须提供encodeWithCoder:和initWithCoder:办法。 前一个办法通知零碎怎么对对象进行编码,而后一个办法则是通知零碎怎么对对象进行解码。 毛病:归档的模式来保留数据,只能一次性归档保留以及一次性解压。所以只能针对小量数据,而且对数据操作比拟蠢笨,即如果想改变数据的某一小部分,还是须要解压整个数据或者归档整个数据。 注: initWithCoder什么时候须要调用[super initWithCoder:]• initWithCoder原理:只有解析文件就会调用,xib,storyboard都是文件,因而只有解析这两个文件,就会调用initWithCoder。• 因而如果在storyboard应用自定义view,重写initWithCoder办法,肯定要调用[super initWithCoder:],因为只有零碎才晓得怎么解析storyboard,如果没有调用,就解析不了这个文件。2.NSUserDefaults:(偏好设置,实质是plist)用来保留应用程序设置和属性、用户保留的数据。用户再次关上程序或开机后这些数据依然存在。 NSUserDefaults能够存储的数据类型包含:NSData、NSString、NSNumber、NSDate、NSArray、NSDictionary。如果要存储其余类型,则须要转换为后面的类型,能力用NSUserDefaults存储。 益处:1.不须要关怀文件名 2.疾速做键值对存储害处: 能及时存储,须要做同步操作,把内存中的数据同步到硬盘上底层:就是封装了一个字典 沙盒构造剖析:利用程序包:蕴含了所有的资源文件和可执行文件。Documents:保留利用运行时成成的须要长久化的数据,iTunes同步设施时会备份该目录。(例如,游戏利用可将游戏存到保留在该目录。大型数据不能寄存在这里,一旦寄存,iOS审核不会通过)tmp:保留利用运行时所需的长期数据,应用结束后再将相应的文件从该目录删除。利用没有运行时,零碎也可能会革除该目录下的文件。iTunes同步设施时不会备份该目录。Library/Caches:保留利用运行时生成的须要长久化的数据,iTunes同步设施时不会备份该目录。个别存储体积大、不须要备份的非重要数据。(个别把缓存下载好的文件放在这里)Library/Preference:保留利用的所有偏好设置,iOS的settings(设置)利用会在该目录中查找利用的设置信息。iTunes同步设施会备份该目录。注:在iOS7之前,默认不会马上跟硬盘同步,能够调用[userDefaults synchronize]; 同步操作3. Write写入形式:永恒保留在磁盘中。4. 采纳数据库来存储数据。4.1 SQLiteSQLite (http://www.sqlite.org/docs.html) 是一个轻量级的关系数据库。iOS SDK很早就反对了SQLite,在应用时,只须要退出 libsqlite3.dylib 依赖以及引入 sqlite3.h 头文件即可。然而,原生的SQLite API在应用上相当不敌对,在应用时,十分不便。 4.2 FMDB什么是FMDB FMDB是iOS平台的SQLite数据库框架 FMDB以OC的形式封装了SQLite的C语言APIFMDB的长处 应用起来更加面向对象,省去了很多麻烦、冗余的C语言代码 比照苹果自带的Core Data框架,更加轻量级和灵便 提供了多线程平安的数据库操作方法,无效地避免数据凌乱FMDB有三个次要的类FMDatabase 一个FMDatabase对象就代表一个独自的SQLite数据库 用来执行SQL语句FMResultSet 应用FMDatabase执行查问后的后果集FMDatabaseQueue 用于在多线程中执行多个查问或更新,它是线程平安的通过指定SQLite数据库文件门路来创立FMDatabase对象 //取得数据库文件门路 NSString *doc=[NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES) lastObject];NSString *fileName=[doc stringByAppendingPathComponent:@"user.sqlite"]; FMDatabase *db = [FMDatabase databaseWithPath:path];if (![db open]) { NSLog(@"数据库关上失败!");}执行更新在FMDB中,除查问以外的所有操作,都称为“更新” create、drop、insert、update、delete等 应用executeUpdate:办法执行更新- (BOOL)executeUpdate:(NSString*)sql, ...- (BOOL)executeUpdateWithFormat:(NSString*)format, ...- (BOOL)executeUpdate:(NSString*)sql withArgumentsInArray:(NSArray *)arguments//示例[db executeUpdate:@"UPDATE t_user SET age = ? WHERE name = ?;", @20, @"Jack"]执行查问 ...

August 12, 2020 · 2 min · jiezi

关于objective-c:ObjectiveC之runtime漫谈

runtime简介因为objective-c是一门动静语言,也就是说只有编译器是不够的,还须要一个运行时零碎(runtime system)来执行编译后的代码。这是整个objective-c运行框架的一块基石。runtime简称运行时。其中最次要的就是音讯机制。对于编译期语言,会在编译的时候决定调用哪个函数。对于OC的函数,是动静调用的,在编译的时候并不能决定真正调用哪个函数,只有在运行时才会依据函数的名称找到对应的函数来调用。runtime的作用Objc 在三种层面上与 Runtime 零碎进行交互: 1. 通过 Objective-C 源代码 2. 通过 Foundation 框架的 NSObject 类定义的办法 3. 通过对 Runtime 库函数的间接调用runtime源码苹果和GNU各自保护一个开源的runtime版本,这两个版本之间都在致力的保持一致。 都是运行时的头文件,其中次要应用的函数定义在message.h和runtime.h这两个文件中。通过 Foundation 框架的 NSObject 类定义的办法Cocoa 程序中绝大部分类都是 NSObject 类的子类,所以都继承了 NSObject 的行为。(NSProxy 类是个例外,它是个形象超类)-class办法返回对象的类;-isKindOfClass: 和 -isMemberOfClass: 办法查看对象是否存在于指定的类的继承体系中(是否是其子类或者父类或者以后类的成员变量);-respondsToSelector: 查看对象是否响应指定的音讯;-conformsToProtocol:查看对象是否实现了指定协定类的办法;-methodForSelector: 返回指定办法实现的地址。通过对 Runtime 库函数的间接调用Runtime 零碎是具备公共接口的动静共享库。头文件寄存于/usr/include/objc目录下,这意味着咱们应用时只须要引入objc/Runtime.h头文件即可。 许多函数能够让你应用纯 C 代码来实现 Objc 中同样的性能。除非是写一些 Objc 与其余语言的桥接或是底层的 debug 工作,你在写 Objc 代码时个别不会用到这些 C 语言函数。对于公共接口都有哪些,前面会讲到。我将会参考苹果官网的 API 文档。 Runtime的术语的数据结构SEL它是selector在 Objc 中的示意(Swift 中是 Selector 类)。selector 是办法选择器,其实作用就和名字一样,日常生活中,咱们通过人名分别谁是谁,留神 Objc 在雷同的类中不会有命名雷同的两个办法。selector 对办法名进行包装,以便找到对应的办法实现。它的数据结构是: typedef struct objc_selector *SEL;咱们能够看出它是个映射到办法的 C 字符串,你能够通过 Objc 编译器器命令@selector() 或者 Runtime 零碎的 sel_registerName 函数来获取一个 SEL 类型的办法选择器。 ...

August 10, 2020 · 5 min · jiezi

关于objective-c:synthesize

公布于 2018/08/28 18:10,搬家到思否。Xcode4时,@property只能生成getter、setter办法的申明; 从Xcode5开始,@property 能够主动生成_propertyName成员变量和getter、setter办法的申明和实现。默认状况下,getter、setter办法作用于_propertyName变量。 然而,当同时重写getter和setter两个办法的时候,实现了齐全的自定义实现,无奈对应到默认的变量_propertyName,_propertyName就有效了,须要手动定义一个变量或者应用@synthesize指定一个变量来绑定到属性上。 一、手动定义变量 Girl.h #import <Foundation/Foundation.h>@interface Girl : NSObject@property (nonatomic, copy) NSString *name;@property (nonatomic, copy) NSString *boyFriend;@endGirl.m #import "Girl.h"@implementation Girl/* *************** MARK: 办法一 *************** */{ NSString* _name; //手动注册变量}- (NSString *)name{ if (!_name) { _name = @"zhangli"; } return _name;}- (void)setName:(NSString *)name{ _name = name;}@end如果不去手动定义一个变量,那么独自写getter或setter没有问题,然而同时重写就会报错了。 二、@synthesize /* *************** MARK: 办法二 *************** */@synthesize boyFriend = man; //应用@synthesize,将属性对应于变量- (NSString *)boyFriend { if (!man) { man = @"Jerod"; } return man;}- (void)setBoyFriend:(NSString *)boyFriend{ man = boyFriend;}属性boyFriend本来是对应于_boyFriend变量的,这里通过@synthesize扭转了属性、getter、setter对应的变量。(这点很有用途,比方能够在子类批改父类中readonly属性的值。) ...

July 27, 2020 · 1 min · jiezi

关于objective-c:CGAffineTransformMakeabcdtxty-矩阵运算原理

CGAffineTransformMake(a,b,c,d,tx,ty) a d 缩放, b c 旋转, tx ty 位移 (x , y) -> (x' , y') 的公式 x' = ax + cy + tx y' = bx + dy + ty矩阵的基本知识: struct CGAffineTransform { CGFloat a, b, c, d; CGFloat tx, ty;};CGAffineTransform CGAffineTransformMake(CGFloat a, CGFloat b, CGFloat c, CGFloat d, CGFloat tx, CGFloat ty); 为了把二维图形的变动对立在一个坐标系里,引入了齐次坐标的概念,即把一个图形用一个三维矩阵示意,其中第三列总是(0,0,1),用来作为坐标系的规范。所以所有的变动都由前两列实现。 以上参数在矩阵中的示意为: 运算原理:原坐标设为(X,Y, 1) | a b 0 |(X,Y, 1) ⅹ | c d 0 | = (aX + cY + tx , bX + dY + ty , 1) ; | tx ty 1 |通过矩阵运算后的坐标(aX + cY + tx, bX + dY + ty, 1) 咱们比照一下可知: ...

July 27, 2020 · 1 min · jiezi

20192023年中国网络直播行业前景预测

从狭义角度,网络直播是新型的高互动性视频娱乐。这种直播通常是主播通过视频录制工具,在互联网直播平台上,直播自己唱歌,玩游戏等活动,而受众可以通过弹幕与主播互动,也可以通过虚拟道具进行打赏。当前,网络直播行业正呈现三方分化的形态,包括最为知名的秀场类直播、人气最高的游戏直播,以及新诞生并迅速崛起的泛生活类一对一直播。近两年来,网络直播迅速发展成为一种新的互联网文化业态,行业在经济收入,用户人数,影响力等方面都呈现极高的增长趋势,截止2017年末,全国共约有200家公司开展或从事网络表演(直播)业务,较2016年减少近百家。2017年我国网络表演市场营收达到304.5亿元,相比2016年的218.5亿元,同比增长39%,截止2017年12月底,秀场直播,游戏直播,泛娱乐直播平台的覆盖人群继续扩大,用户规模分别达到3.12亿,2.47亿,3.6亿,相比上年分别增加0.32亿,0.27亿,0.4亿激烈市场竞争状况下,众多直播平台为了避免遭受市场淘汰,展开差异化布局,网络游戏直播平台如斗鱼直播,虎牙直播,熊猫直播,龙珠直播,全民直播等,借助军事演习类游戏和王者荣耀,英雄联盟等巩固内容优势,泛娱乐直播如映客,花椒、一直播,秀色娱乐,腾讯now直播,在生活、户外布局逐渐加深,秀场直播如六间房,YY直播着重在网红选秀和才艺表演方面投入资源,联合制作娱乐节目。 同时,资本市场也点燃了网络直播领域的火种,巨额资本加持网络直播行业,从YY、斗鱼,到红衣教主周鸿祎的花椒直播、国民老公王思聪的熊猫TV,再到百度、阿里巴巴、小米的纷纷入局,资本市场的一同认知,更是证实了网络直播时代的到来。目前直播行业马太效应凸显,用户加速向头部平台靠拢。各直播类型的大部分市场份额已经被几个平台牢牢占据,也就是说,虽然平台数量依旧众多,但赢家通吃的时代正在来临。从政策角度,文化部频频出手,加强网络直播市场的监管。2018年8月20日,工信部、公安部、文化和旅游部、广电总局、国家互联网信息办公室联合下发《关于加强网络直播服务管理工作的通知》部署各地各有关部门进一步加强网络直播服务许可、备案管理、强化网络直播服务基础管理,建立健全长效监管机制。大力开展存量违规网络直播服务清理的工作,2019年1月29日,武汉市软件行业协会,发布了《网络直播平台管理规范》以及《网络直播主播管理规范》,规范首次明确了直播平台中有关主播监控、账号监管平台巡查等多方面的内容。标准于2019年1月29日正式实施2019年网络直播行业必将风起云涌,各大平台密集储备弹药,后起之秀的一对一直播平台风头正紧,抢占市场的局面将会愈发激烈,而传统的直播行业已逐渐突破原有秀场游戏直播的类型,进入生活直播与全民直播的新时代!新一代信息技术的应用是网络直播变革的重要力量,带动行业加速升级,用户个性化时代归属感以及虚拟社群的真实体验加速推动网络直播繁荣扩张,预计到2022年网络表演(直播)市场整体营收规模增量约为615亿元,年平均增速为24.8%总体上讲:目前,泛娱乐网络直播头部市场基本稳定,市场竞争激烈,不建议进入,但一对一直播垂直化发展潜力巨大,比较适合各类型企业投资。

November 4, 2019 · 1 min · jiezi

逻辑回归原理小结

逻辑回归是一个分类算法,它可以处理二元分类以及多元分类。虽然它名字里面有“回归”两个字,却不是一个回归算法。那为什么有“回归”这个误导性的词呢?个人认为,虽然逻辑回归是分类模型,但是它的原理里面却残留着回归模型的影子,本文对逻辑回归原理做一个总结。 从线性回归到逻辑回归 我们知道,线性回归的模型是求出输出特征向量Y和输入样本矩阵X之间的线性关系系数,满足。此时我们的Y是连续的,所以是回归模型。如果我们想要Y是离散的话,怎么办呢?一个可以想到的办法是,我们对于这个Y再做一次函数转换,变为。如果我们令的值在某个实数区间的时候是类别A,在另一个实数区间的时候是类别B,以此类推,就得到了一个分类模型。如果结果的类别只有两种,那么就是一个二元分类模型了。逻辑回归的出发点就是从这来的。下面我们开始引入二元逻辑回归。 二元逻辑回归的模型 上一节我们提到对线性回归的结果做一个在函数g上的转换,可以变化为逻辑回归。这个函数g在逻辑回归中我们一般取为sigmoid函数,形式如下: 它有一个非常好的性质,即当z趋于正无穷时,趋于1,而当z趋于负无穷时,趋于0,这非常适合于我们的分类概率模型。另外,它还有一个很好的导数性质: 这个通过函数对求导很容易得到,后面我们会用到这个式子。 如果我们令中的z为:,这样就得到了二元逻辑回归模型的一般形式: 其中x为样本输入,为模型输出,可以理解为某一分类的概率大小。而为分类模型的要求出的模型参数。对于模型输出,我们让它和我们的二元样本输出y(假设为0和1)有这样的对应关系,如果 ,即, 则y为1。如果,即, 则y为0。y=0.5是临界情况,此时为, 从逻辑回归模型本身无法确定分类。 的值越小,而分类为0的的概率越高,反之,值越大的话分类为1的的概率越高。如果靠近临界点,则分类准确率会下降。 此处我们也可以将模型写成矩阵模式: 其中为模型输出,为 mx1的维度。X为样本特征矩阵,为mxn的维度。为分类的模型系数,为nx1的向量。 理解了二元分类回归的模型,接着我们就要看模型的损失函数了,我们的目标是极小化损失函数来得到对应的模型系数。 二元逻辑回归的损失函数 回顾下线性回归的损失函数,由于线性回归是连续的,所以可以使用模型误差的的平方和来定义损失函数。但是逻辑回归不是连续的,自然线性回归损失函数定义的经验就用不上了。不过我们可以用最大似然法来推导出我们的损失函数。 我们知道,按照第二节二元逻辑回归的定义,假设我们的样本输出是0或者1两类。那么我们有: 把这两个式子写成一个式子,就是: 其中y的取值只能是0或者1。 得到了y的概率分布函数表达式,我们就可以用似然函数最大化来求解我们需要的模型系数。 为了方便求解,这里我们用对数似然函数最大化,对数似然函数取反即为我们的损失函数)。其中: 似然函数的代数表达式为: 其中m为样本的个数。 对似然函数对数化取反的表达式,即损失函数表达式为: 损失函数用矩阵法表达更加简洁: 其中E为全1向量。 二元逻辑回归的损失函数的优化方法 对于二元逻辑回归的损失函数极小化,有比较多的方法,最常见的有梯度下降法,坐标轴下降法,等牛顿法等。这里推导出梯度下降法中每次迭代的公式。由于代数法推导比较的繁琐,我习惯于用矩阵法来做损失函数的优化过程,这里给出矩阵法推导二元逻辑回归梯度的过程。 对于,我们用对向量求导可得: 这一步我们用到了向量求导的链式法则,和下面三个基础求导公式的矩阵形式: 为函数 ...

November 4, 2019 · 1 min · jiezi

ios环信单聊更换头像和昵称

之前的项目因为用户信息以及好友关系是由服务器管理的,所以在开发的时候用户的头像以及用户的昵称是放在服务器中管理的。 我的环信版本是最新的V3.2版本,其他的版本暂且未知,不过我想模式应该八九不离十.如果在easuUI中单纯的使用,并弹出窗口显示的是用户的环信昵称,和默认头像(如果没有设置的话就是一个默认头像)/// 弹出单聊界面窗口EaseMessageViewController *chatController = [[EaseMessageViewController alloc] initWithConversationChatter:@"8001" conversationType:EMConversationTypeChat];如果头像跟昵称放在服务器管理,那么显示的就不符合需求了因为环信的包他们给弄成静态包了,所以无法看到EaseMessageViewController.h或EaseMessageViewController.m的代码。其实要修改成自己定义的头像或者昵称很简单。我们只要创建一个控制器继承EaseMessageViewController.h,并在自定义的控制器中重写指定方法就可以实现头像和昵称的控制.另外即使头像的路径是在服务器上的,也是可以的./// 重写EaseMessageViewController.h中的方法.- (id<IMessageModel>)messageViewController:(EaseMessageViewController *)viewController modelForMessage:(EMMessage *)message{        if (message.direction == EMMessageDirectionSend){ /// 用户发送        //用户可以根据自己的用户体系,根据message设置用户昵称和头像        id<IMessageModel> model = nil;        model = [[EaseMessageModel alloc] initWithMessage:message];        model.avatarImage = [UIImage imageNamed:@"EaseUIResource.bundle/user"];//默认头像        NSString *url = [NSString ddl_webImageRequestWithUrl:self.userInformation.url];        model.avatarURLPath = url;//头像网络地址        model.nickname = self.userInformation.username;//用户昵称        return model;    } else {        //用户可以根据自己的用户体系,根据message设置用户昵称和头像        id<IMessageModel> model = nil;        model = [[EaseMessageModel alloc] initWithMessage:message];        model.avatarImage = [UIImage imageNamed:@"EaseUIResource.bundle/user"];//默认头像        NSString *url = [NSString ddl_webImageRequestWithUrl:self.friend_url];        model.avatarURLPath = url;//头像网络地址        model.nickname = self.friend_username;//用户昵称        return model;    }   }

October 8, 2019 · 1 min · jiezi

iOS知识梳理-runloop

iOS知识梳理 - runlooprunloop其实是个很普遍的东西,基本上是个应用框架都有类似的东西,比如js或flutter里的event loop,Android的looper。 下面我们从最简单的例子来看一下,runloop的本质 溯源最初学编程的时候,有个经典题目是用c/c++实现四则运算。大体上要下面这个可以一直输入的效果: 代码大概像这样: int main(){ string str; double result;//保存结果 while(getline(cin,str)) { // calculate cout << "Result is: " << result << endl; }}由于可以一直输入新的算式,我们自然地使用了一个while循环来处理输入,有新的一行输入就去计算结果并输出,没有新的输入时其实就是阻塞等待。 这其实就是最简单的runloop。 实际应用程序中,无非就是输入的形式更多了一些,执行的任务更复杂了一些,基本框架,其实还是一个while循环。 网上看到一个伪代码挺清晰的: function loop() { initialize(); do { var message = get_next_message(); process_message(message); } while (message != quit);}深入理解runlooprunloop的实际实现还是有大量细节的,core foundation框架也是开源的,这里其实可以挖得很深。 iOS 多线程:『RunLoop』详尽总结,这篇文章讲得挺好,推荐阅读。很多细节本文就不再做太多展开了。 Moderunloop在运行时有个mode的概念,runloop只会处理当前mode的event。 比如,我们有mode1和mode2,mode1对应了3个event,mode2对应了2个event,如果当前runloop处于mode1,就只会处理mode1对应的3个event。 具体而言,runloop暴露出来的是kCFRunLoopDefaultMode和UITrackingRunLoopMode两个mode,通常主线程处于DefaultMode,但是当滑动scrollview时,主线程处于TrackingMode。而默认地,我们的NSTimer事件是绑定在DefaultMode的,这就会导致,当滑动scrollview时,就不会触发NSTimer事件。 为了解决这种问题,runloop给出了一个CommonMode的概念,CommonMode不是一个具体的Mode,可以理解成一组Mode的集合,这里实际上就是DefaultMode和TrackingMode。把Timer加入到CommonMode,则在DefaultMode和TrackingMode都会执行这个Timer了。 Mode这个东西,在实际使用中,除了给Timer的使用带来困扰之外,几乎从未被使用过。 那么,为什么苹果会在Runloop中设计Mode这个东西? 网上并没有找到有价值的讨论,不过可以简单揣测一下,目的可能是,在某些场景下,可以只处理对应场景需要的事件。这可能是基于一种特殊情况的假设:在某种场景下,CPU忙不过来,因此需要停掉其它任务,专注于当前场景的任务。比如,任务A需要90%的CPU,其它杂七杂八事件处理需要40%的CPU,那么,停掉那些杂事专注当前场景,可能是有意义的行为。 如果CPU并不会跑满的话,那么Mode的拆分就意义不大,充其量是副作用极大的优先级调整(低优先级的直接被卡住了诶)。 参考其它平台的EventLoop实现,通常只是提供简单的优先级设置能力即可。 总的来说,个人觉得Mode的设计是比较鸡肋的。 子线程的Runloop默认地,子线程并不会开启Runloop。想一想,这是非常合理的。开启了Runloop后,即使在没有事件时,线程也会进入阻塞等待的状态,需要的时候那叫保活,不需要的时候就是白占资源了。因此,子线程默认情况下是肯定不能开启Runloop的。而没有开启Runloop的情况下,涉及到Runloop的异步调用就都不能用了,主要就是NSTimer,performSelector:afterDelay:,网络请求的回调等。 解决方案是通过[[NSRunLoop currentRunLoop] run]启动当前线程的runloop。 跟Runloop相关的特性autoreleasepool:每次runloop开始时进入pool,结束时离开pool。从而在未显式声明autoreleasepool的时候释放autorelease对象。界面更新:操作UI时会把对应的UIView/CALayer标记为待处理,在每次迭代即将结束时会执行实际的绘制。

October 7, 2019 · 1 min · jiezi

龙纪链利用区块链技术打造全球最大的沙盒游戏

3.0的游戏区块链不再限于游戏模块的区块链化,而是通过区块链的核心共识机制、激励机制,来激发玩家的创造性和主观能动性。由游戏厂商搭建平台,让玩家自主的在游戏中创造一切,包括装备、玩法等等。 图片描述什么是区块链游戏?首先,我们从字面意义上来了解,什么是区块链游戏,游戏我们都不陌生,现实中的人们几乎都有玩过,那我们就先来说说什么是区块链。区块链是一种新型的计算机应用模式,拥有更安全、更保密、去中心化、透明、可追查的特点。用一个玩笑的话形容就是,买家直接与卖家通话,没有中间商赚差价,而且过程完全透明,价格不可恶意炒高更改。 什么是龙纪链?龙纪链 DGEra Chain(www.dgera.net)是基于以太坊开发的大型 3D区块链 RPG 游戏,龙纪链计划打造一款全平台游戏,龙纪链目标打造全球最大的区块链沙盒游戏,致力于把区块链技术应用到大型 3D游戏中。而游戏也正是区块链技术应用最好的落地场景。利用区块链技术,对参与玩家而言这无疑是一个数据透明、高信任度、玩法正轨的可持续性收益项目。这将会颠覆现有游戏行业玩法。 图片描述龙纪链怎么玩?玩家在创始期可以通过ETH兑换龙晶购买一只创始龙,每只龙有固定属性,通过挖矿收益龙币,龙的等级越高产币速率越快。龙币是未来龙纪链的唯一通证。创始期时龙币可以通过挖矿空投等方式获得。龙币在创始期可以加速获得,创始期持有越久,一次性奖励就越多。在创始期过后,龙币可以通过打怪、挖矿、升级、PVP、PVE、副本等形式获得,创始龙和龙晶只在创始期时发售哦,但是会开放交易大厅,交易大厅可以进行创始龙的买卖交易,成年的龙可以繁衍小龙,小龙可以进行赠送。龙巢可以用来查看玩家持有的分红和龙币数量。 如何从龙纪链DGEra Chain中获利?除了培育龙,买卖龙与龙晶,挖矿囤龙币拿分红,等待创始期结束后,平台根据玩家持有的龙币总量,发放的相应持币奖励,幸运哈希和哈希DICE,玩家还可以开龙盒,做每日任务,英雄任务,传奇任务,这些都可以获得相当丰厚的一笔ETH,龙晶,龙币奖励!自己玩无聊,邀请好友,每日签到,随手转发至朋友圈,躺着就可以把奖励领到手啦!龙纪链还有返佣政策,只要你邀请好友成为下家,即可获得ETH!当然还不止这些!创始期结束后,即将开放的打怪,PVP,副本,驯龙师等等各种各样的玩法活动肯定会让玩家龙币拿到手软,奖励不停歇!福利多多期待玩家们去探索去发现,龙纪链希望玩家们能在龙纪链中实现乐趣和价值的双丰收! 图片描述 投资稳赚的赚钱游戏龙纪链:龙纪链尚处第一阶段,因丰厚的赚币红利吸引了一大批玩家,可谓“一龙难求”。据悉,平台将陆续上线PVP、驯龙师系统、时装系统、首个大型资料片黑暗之地、大秘境等玩法,将龙纪链打造成全球最大的区块链沙盒游戏!龙纪链已被大批玩家和币圈人看好,推荐大家尽早关注龙纪链DGEra Chain,希望与你同行! 未来龙纪链还将建设更多通证使用场景,拓宽游戏生态,给币圈的用户们一个全新体验,也将彻底颠覆游戏领域的垄断现象,让更多游戏开发者更专注自己的梦想、让玩家玩到更多有创意高质量游戏。同时将引入更多泛娱乐内容,创造兼容并包的区块链游戏生态。图片描述 龙纪链官网:www.dgera.netTelegram群1:https://t.me/DGEraChain Telegram群2:https://t.me/dgerachain001 币用群1:https://0.plus/DGEraChain 币用群2:https://0.plus/dgerachain001新手指南:https://dgera.zendesk.com/hc/... 进微信群加:suger66699、linec159注册下载链接: https://front.dgera.net/regis...

September 10, 2019 · 1 min · jiezi

前端压缩工具

野子电竞数据官网改版https://www.xxe.io/ 全新登场介绍一下webpack和gulp以及项目中的具体使用现今的很多网页其实可以看做是功能丰富的应用,它们拥有复杂的javascript代码和一大堆依赖包,为了简化开发的复杂度,有很多实践方法模块化:将复杂的程序细化为小的文件类似于typescript这种在js基础上拓展的开发语言,可以简化我们的开发,并且之后可以用bable等工具将其转化成为js即浏览器认识的语言Sass,less等css预处理器webpack是模块打包机,它做的事情是分析你的项目结构,找到js模块以及其他的一些浏览器不能直接运行的语言,并将其打包为合适的格式供浏览器使用Babel编译js的平台,可以使用下一代的es6和es7使用基于js拓展的语言,如 react的JSX模块组织的几种方法通过书写在不同文件中,使用script标签进行加载common.js node.js使用的就是这种方式amd进行加载 require.js使用这种模式es6模块webpackwebpack的特点丰富的插件方便进行工作大量的加载器,包括加载各种静态资源代码分割,提供按需加载的能力发布工具webpack的优势webpack是以commonjs的形式来书写脚本 但对amd和cmd也支持全面,方便旧项目进行代码迁移能被模块化的不仅仅是js了开发便捷 能替代部分grunt和gulp的工作 比如打包 压缩 图片转base64扩展性强 插件机制完善webpack的属性值entry 入口文件output:定义构建后的输出文件module:loaders加载各种资源reslove resolve属性中的extensions数组用于配置程序中可以自行补全哪些文件后缀plugin 提供了丰富的组件来满足不同的需求externals 当我们想在项目中require一些其他的类库或者api 而又不想让这些类库的源码被构建到运行时文件中contextGrunt配置gruntfile.jsmodule.exports = function () {grunt.initConfig({pkg: grunt.file.readJSON(‘package.json’);});grunt.registerTask(‘default’, []);}123456grunt插件介绍contrib-jshint js语法错误勘察contrib-watch 实时监控文件变化 调用相应的任务重新执行contrib-cleancontrib-copycontrib-concatkarma 前端自动化测试化工具uglify 插件uglify: {options: {stripBanner: true,banner:},build: {src:dest}} 在initConfig之后grunt.loadNpmTasks(‘grunt-contrib-ugify’);grunt.registerTask(‘default’,‘ugify’);1234567891011121314banner: 即在生成的压缩文件第一行加一句话说明,pkg可以获得package.json的内容build: 配置了源文件和压缩文件,即规定了要压缩谁 压缩之后生成谁jshintjshint: {build: [‘Gruntfile.js’, ‘src/.js’],options: {jshintrc: ‘.jshintrc’}}123456使用watch插件watch: {build: {files: ['src/.js’, ‘src/.css’],tasks: [‘jshint’, ‘uglify’],options: {spawn: false}}}1234567gulpgulpfile.jsgulp的配置文件var gulp = require(‘gulp’)var less = require(‘gulp-less’)gulp.task(‘testLess’, function () {gulp.src=(‘src/less/index.less’).pip(less()).pip(gulp.dest(‘src/css’))});//定义默认任务,并让gulp监视文件变化自动执行gulp.task(‘default’, [‘watch’], function () {gulp.watch('sass/.scss’,[‘sass’]);});123456789101112gulp常用插件js压缩var gulp = require(‘gulp’);var rename = require(‘gulp-rename’);var uglify = require(‘gulp-uglify’);gulp.task(‘rename’, function () {gulp.src(‘src//*.js’).pipe(uglify()).pipe(rename(‘idnex.min.js’)).pipe(gulp.dest(‘build/js’);});gulp.task(‘default’, [‘rename’]);12345678910html压缩var minifyHtml = require(‘gulp-minify-html’);gulp.task(‘minify-html’, function () {gulp.src('src//.html’)//要压缩的html文件.pipe(minifyHtml())//压缩.pipe(gulp.dest(‘build’))});123456js文件合并var concat = require(‘gulp-concat’);gulp.task(‘concat’, function () {gulp.src('src/**/.js’)//要合并的文件.pipe(concat(‘index.js’))//合并匹配到的js文件并命名为index.js.pipe(gulp.dest(‘build/js’))});123456gulp-lessvar gulp = require(‘gulp’), ...

September 7, 2019 · 1 min · jiezi

只有程序员才懂的痛

小区新搬来一户人家,一个30岁左右的年轻漂亮女人,带着一个4岁的男孩。每天独自一人操持家务,买菜做饭,接送孩子。 时间久了,邻居们都有些议论,知道有一天,一个男人背着大包小包敲开了这家的门。男人刚进屋,女人哭声即传了出来。 女人叫到:“你TMD给我站阳台去,大声喊,我回家了!” 男人说:“天天不是出差就是996加班到半夜,难得今天下班那么早,能回家,你闹啥呢?” 女人:“你就给我站阳台上去,你让街坊邻居都看一遍,老娘不是二奶,不是小三,更TM不是寡妇,老娘有男人,老娘的男人是程序员!!!”

August 20, 2019 · 1 min · jiezi

iOS单元测试详解

似此星辰非昨夜为谁风露立中宵 前言我们在做组件化的过程中,肯定需要做的一步就是拆组件,把我们项目中的各个模块拆分为基础组件、功能组件、业务组件,但是在拆分的过程中很容易就会出现问题,所以我们在做组件化的过程中把每个组件都加上了单元测试,这样可以大大提高了我们组件的健壮性,随着我们的组件完成告一段落,今天就把当初探索学习单元测试的经验分享给大家,每种断言、每种测试场景都对应的有例子,希望能对你能有帮助 什么是单元测试(Unit Testing)又称为模块测试, 是针对程序模块的最小单位来进行正确性检验的测试工作。程序单元是应用的最小可测试部件。在过程化编程中,一个单元就是单个程序、函数、过程等;对于面向对象编程,最小单元就是方法,包括基类(超类)、抽象类、或者派生类(子类)中的方法。 单元测试的发展 1.Xcode在XCode4.x时代集成的是OCUnit 2.XCode5.x时代就升级为了XCTest 3.XCode7增加UI测试单元测试框架XCTest(苹果自带,推荐)KiwiGHUintOCMock单元测试带来的好处测试做了我们期望它做的事情尽早的发现程序的 bug 和不足保证在加入新功能或修改旧功能时代码的正确性缺点: 没有足够的时间编写单元测试,或者说大家都没有写单元测试的习惯XCTestXCTest是Xcode自带的一个测试框架,苹果官方推荐我们使用 XCTestCaseXCTest中的测试类都是继承自XCTestCase XCTestCase类结构 - (void)setUp: 在调用类中的每个测试方法之前调用此方法。 - (void)tearDown 这个方法在类中的每个测试方法调用之后调用- (void)testPerformanceExample 将要度量时间的代码放在这里- (void)testExample 使用XCTAssert和相关函数验证测试结果是否正确断言无条件报错等价测试nil测试布尔测试异常测试无条件报错1.XCTFail.生成一个无条件报错。 等价测试1.XCTAssertEqualObjects 2.XCTAssertNotEqualObjects 3.XCTAssertEqual. 当expression1不等于expression2时报错,这个测试用于C语言的常量 4.XCTAssertNotEqual 5.XCTAssertEqualWithAccuracy. 当expression1和expression2之间的差别高于accuracy 将报错。这种测试适用于floats和doubles这些常量,两者之间的细微差异导致它们不完全相等,但是对所有的常量都有效。 6.XCTAssertNotEqualWithAccuracy Nil(空)测试1.XCTAssertNil. 当expression参数非nil时报错2.XCTAssertNotNil Boolean测试1.XCTAssertTrue. 当expression计算结果为false时报错。2.XCTAssertFalse. 当expression计算结果为true报错。3.XCTAssert. 当expression计算结果为false时报错,与XCTAssertTrue同义。 异常断言测试1.XCTAssertThrows.当expression不抛出异常时报错。2.XCTAssertNoThrow. 当expression抛出异常时报错。 3.XCTAssertThrowsSpecific.当expression针对指定类不抛出异常时报错。4.XCTAssertNoThrowSpecific. 当expression针对指定类抛出异常时报错。任意其他异常都可以;也就是说它不会报错。 5.XCTAssertThrowsSpecificNamed. 当expression针对特定类和特定名字不抛出异常时报错。对于AppKit框架或Foundation框架非常有用,抛出带有特定名字的NSException(NSInvalidArgumentException等)。 6.XCTAssertNoThrowSpecificNamed. 当expression针对特定类和特定名字抛出异常时报错。对于AppKit框架或Foundation框架非常有用,抛出带有特定名字的NSException(NSInvalidArgumentException等) 如何写单元测试找到测试场景 逻辑测试异步测试性能测试准备测试数据 边界测试数据正确测试数据错误测试数据验证结果 使用断言验证单元测试的规范1.合理命名测试用例 1.一个测试方法只测试被测类的一个明确功能, 并命名相应测试方法需要被测试的方法 相应的测试方法 ...

July 16, 2019 · 1 min · jiezi

对iOS代码重构的一点看法

一、原起基本上每一个项目都会经历这样的一个过程,前期的快速迭代,去做市场的试探,这个时候的要求是怎么快怎么来,经过市场试探,找到对应的盈利模式,与及摸准了用户的使用习惯,这个时候产品会进入一个稳步发展的阶段,这个时候很多公司就会开始考虑怎么样更好的去维护这个产品,这个时候重构就来了。 项目重构一方面是对之前开发不合理的地方进行整理重写,另一方面是为后续的扩展和维护打基础。 二、代码重构建议一览图 三、代码重构的具体细节3.1 操作提示操作提示是几乎所有App都会使用到的控件吧,一般都是三方开源的,现在比较流行的都是HUD相关库吧,这种库不仅有文案提示,一般还有对应的图标,显示和消失还伴随着动画。这才更符合移动App的使用体验。对于toast这种老旧的操作提示,直接放弃,UI太难看,不符合时代发展。 3.2 使用系统提供的新控件随着iOS系统的逐年更新,苹果会为我们提供一些更好用的控件来代替就控件。UIAlertView和UIActionSheet这两个控件属于旧时代的产物,苹果已经为我们提供了更好用的UIAlertController,一旦遇到前两个直接使用UIAlertController替换即可。 3.3 机型兼容问题早几年的时候,iOS还有32位操作系统,不过经过几年的发展iOS目前都是64位操作系统了。所以对于一些变量的定义直接使用64位的即可。定义整形变量就使用NSInteger,int就不要使用了;定义浮点型变量的时候,float也不要用了,直接CGFloat。 3.4 系统版本兼容问题对于系统版本的兼容,太老的系统版本就直接放弃吧,对于iOS来说兼容三个大版本,最多4个版本就足够了,对于那些4都不升级的用户,个人认为可以直接放弃了。 3.5 删除不再使用的代码版本的快速迭代过程中或所或少有一些功能是尝试之后失败的,对于这样的功能代码,如果确定是不再使用的,就删除吧。减少工程的体量,代码的维护高质量也会少一些。 3.6 数据模型项目开发中数据尽量做成数据模型,重构的过程中如果遇到这种情况,还是尽量做成模型,方便理解和维护。 不要直接使用字典进行传值,这样对于后期的维护不利;网络请求封装的时候,请求参数尽量单独写成一个参数,这样虽然多写了代码,但是见名知意,方便后续的维护,不要一个字典把所有的参数都扔进去,这样对于后期的维护很不利;网络请求的结果也尽量做成数据模型,不要直接使用字典进行传值。3.7 变量定义变量定义的时候尽量明确类型,除非万不得已,不然不要使用id类型。 3.8 尽量使用大众化的书写方式有的时候一段代码逻辑的编写可能有很多种方式,对于这种情况,我们尽量使用大众化的编写方式,不要为了偷懒,使用一些晦涩难懂的书写方式。导致过段时间自己都看不懂了。 3.9 组件化代码重构一方面是对之前代码的整理,另一方面是对后续扩展和维护打基础。所以重构的工程中,我们应该深入思考,对于一些使用场景比较多的代码,哪怕是多花点时间,也要把它做成通用组件,这样后续的开发能够事半功倍。 3.10 集合初始化对于集合的初始化,比如NSDictionary和NSArray,尽量使用简介的语法糖初始化方式,那种老旧的初始化方式就放弃吧。 3.11 枚举的使用对于一些多类型判断的场景,尽量使用枚举来定义场景类型。这样后续维护方便,代码看上去也更有逼格。

July 16, 2019 · 1 min · jiezi

Block底层实现原理

古巷悠悠岁月深,青石老街印旧痕今夜小楼听风雨,不见当年伞下人 前言Block作为iOS中老生常谈的问题,也是面试中面试官比较喜欢问的 一个问题 ,下面我们通过源码查看block的底层实现原理 什么是BlockBlock:将函数及其上下文组装起来的对象 Block本质就是一个对象创建一个PHJBlock类 @implementation PHJBlock - (void)test { int a = 10; void (^ block)(void) = ^{ NSLog(@"%d", a); }; block(); } @end查看编译后的C++源码 编译: Clang -rewrite-objc PHJBlock.m static void _I_PHJBlock_test(PHJBlock * self, SEL _cmd) { int a = 10; void (* block)(void) = ((void (*)())&__PHJBlock__test_block_impl_0((void *)__PHJBlock__test_block_func_0, &__PHJBlock__test_block_desc_0_DATA, a)); ((void (*)(__block_impl *))((__block_impl *)block)->FuncPtr)((__block_impl *)block); } __PHJBlock__test_block_impl_0的内部实现 struct __PHJBlock__test_block_impl_0 { struct __block_impl impl; struct __PHJBlock__test_block_desc_0* Desc; int a; __PHJBlock__test_block_impl_0(void *fp, struct __PHJBlock__test_block_desc_0 *desc, int _a, int flags=0) : a(_a) { impl.isa = &_NSConcreteStackBlock; impl.Flags = flags; impl.FuncPtr = fp; Desc = desc; } };__block_impl的内部实现 ...

July 15, 2019 · 3 min · jiezi

征集令-云原生技术实践黑客松即将开战赛题由你定

9月,坐标北京。CNBPA(云原生技术实践联盟)与灵雀云要一起搞事情了! 9月6-8日,由CNBPA(云原生技术实践联盟)与灵雀云共同发起的“2019云原生技术实践黑客松编程比赛”将在北京开战!我们希望通过一场以“云原生技术实践”为主题的黑客松活动,为当今云原生领域相关的技术热点、企业转型升级中遇到的迫切需求,寻找新的创意和算法,帮助解决技术落地过程中遇到的实际问题! 赛制说明 7月9日--8月8日 项目征集 本次活动欢迎云原生技术相关领域厂商、互联网厂商、各行业软件开发商、云原生技术终端用户共同参与,进行大赛的项目命题并提供环境、相关场景、专家资源的支持。参与企业将作为本次黑客松大赛的战略合作伙伴! 7月15日-8月15日 选手报名 选手可关注“云原生技术社区”公众号,后续公众号会发布“黑客召集令”,填写个人(团队)参赛信息报名。 8月8日-30日 赛题筛选+公布 此阶段中,报名入围的个人(团队)选手会收到大赛组委会邀约参赛通知。赛题方向经过筛选,将和参赛通知一并发送给参赛选手。 9月6日-8日 组团参赛+评选颁奖 9月6日 :签到,热身9月6日-8日:持续创作9月8日 :演示原型+评选9月8日 :颁奖 赛题示例:流量可视化:ovs本身提供了端口流量的监控数据,同时 ovn 会在每个机器上开启一个 mirror0 端口镜像当前机器的所有容器流量。通过这些信息(或者其他你能想到的信息)你可以如何可视化容器集群的流量呢? 如对大赛项目命题感兴趣的合作伙伴,可联系大赛负责人:付智丹,获取活动相关合作介绍! **Tel/WeChat:13717573070Email:zdfu@alauda.io** 活动相关筹备进程将在“云原生技术社区”微信公众号上发布!敬请关注!!!

July 11, 2019 · 1 min · jiezi

移动端orm框架性能测评

移动端orm框架性能测评flutter_orm_plugin 发布以来,不少团队试用了,我发现大家对这类数据库相关的库,第一反应就是性能如何,之前确实没做太多行业对比,最近觉得还是有必要做一下性能测试,给大家一个交代的。 在ios端,业界比较常用的orm框架应该是苹果官方推出的coredata,还有就是realm了。在android端orm框架我挑了三个比较常用的,greendao,realm和activeandroid。我会用flutter_orm_plugin跟上面提到的ios和android端orm框架做对比。 下面我会分别给出测试用例,测试代码,还有最终数据比较的结果。 测试用例测试用例我列了以下这些 10000次插入数据使用批量接口10000次插入数据10000次读取数据10000次修改数据使用批量接口10000次修改数据10000次删除数据使用批量接口10000次删除数据为什么会有普通插入数据和使用批量接口插入数据的区别,大部分orm框架都会对批量操作有一定的优化,所以需要对批量操作进行测试,但是在平时使用,不一定都能用上批量接口(例如多次数据操作不在同一代码块,或者在不同的模块中都要操作数据),所以我们会分别对普通操作和批量操作进行测试。 android 测试代码首先我们给出flutter_orm_plugin 的测试代码,由于不想因为flutter和原生channel通讯产生误差,我们直接用Luakit来写lua代码做测试(greendao、realm、activeandroid、coredata都不涉及flutter和原生channel通讯),flutter_orm_plugin其实底层就是luakit的orm框架,这个不影响测试准确性。 循环插入Luakit定义orm模型结构并做10000次插入,下面的代码是ios和android通用的。 local Student = { __dbname__ = "test.db", __tablename__ = "Student", studentId = {"TextField",{primary_key = true}}, name = {"TextField",{}}, claName = {"TextField",{}}, teacherName = {"TextField",{}}, score = {"RealField",{}}, } local params = { name = "Student", args = Student, } Table.addTableInfo(params,function () local studentTable = Table("Student”) for i=1,10000 do local s = { studentId = "studentId"..i, name = "name"..i, claName = "claName"..i, teacherName = "teacherName"..i, score = 90, } studentTable(s):save() end end)activeandroid定义orm模型结构并做10000次插入 ...

July 10, 2019 · 7 min · jiezi

ffmpeg开发知识点回顾

视频花屏/卡顿原因如果GOP分组中的P帧丢失会造成解码端的图像发生错误为了避免花屏问题的发生,一般如果发现P帧或者I帧丢失,就不显示本GOP内的所有帧,直到下一个I帧来后,重新刷新图像。时间基tbr: 帧率tbn:time base of streamtbc:time base of codec时间戳PTS: Presentation timestampDTS: Decoding timestampI(intra)/B(bidirectional)/P(predicted)帧时间戳顺序实际帧顺序:I B B P存放帧顺序:I P B B解码时间戳:1 4 2 3展示时间戳:1 2 3 4从哪儿获得PTSAVPacket中的PTSAVFrame中的PTSav_frame_get_baset_effort_timstamp()计算当前帧的PTSPTS=PTS * av_q2d(video_stream->time_base)av_q2d(AVRotional a){ return a.num/(double)a.den }计算下一帧的PTSvideo_clock: 预测的下一帧视频的PTSframe_delay: 1/tbraudio_clock: 音频当前播放的时间戳多媒体格式转换ffmpeg -i out.mp4 -vcodec copy -acodec copy out.flv -i:输入文件vcodec copy:视频编码处理方式acodec copy:音频编码处理方式该条命令的作用是将视频文件out.mp4格式转换为out.flv,音频编码方式保持不变,视频编码方式保持不变。 录音命令ffmpeg -f avfoundation -i :0 out.wav:0 代表音频设备该条命令表示使用AVfoundation框架录制一段音频数据,数据来源是麦克风,输出文件是out.wav,录制完成之后,使用ffplay out.wav命令进行播放。 录屏命令ffmpeg -f avfoundation -i 1 r 30 out.yuv -f: 指定使用AVfoundation采集数据-i: 指定从哪儿采集数据,它是一个文件索引号-r:指定帧率该条命令表示使用AVfoundation框架,以30帧每秒的帧率录制屏幕,输出文件是out.yuv。使用ffplay可以进行播放,但是播放的时候需要制定屏幕尺寸和录制的数据格式,否则播放不出来。 ffmpeg滤镜命令ffmpeg -i in.mov -vf crop=in_w-200:in_h-200 -c:v libx264 -c:a copy out.mp4 ...

July 4, 2019 · 1 min · jiezi

直播知识结构整理

文章内容来自于逻辑教育公开课。 总结:以上主要是对音视频开发中用到的相关知识进行了一个整理,囊括了流媒体知识、直播技术点、直播架构、音频知识点。

July 2, 2019 · 1 min · jiezi

在-iOS-平台实现Ping-和-traceroute

ping 命令Ping是为了测试另一台主机是否可达,现在已经成为一种常用的网络状态检查工具。 常见的ping命令: /**** 往目的追击发送固定包数 ****/ping -c 3 www.baidu.com // ping百度发送3个包/**** 设置两次发包之间的等待时间 ****/ping -i 5 www.baidu.com // 两包之间的时间间隔为5sping -i 0.1 www.baidu.com // 两包之间的时间间隔为0.1s/**** 检查本地网络接口是否已经启动并正在运行 ****/ping 127.0.0.1 (linux: ping 0) ping localhost /**** 超级用户可以利用 -f 几秒钟发送数十万个包给主服务造成压力 *****/sudo ping -f www.baidu.com /**** 让电脑发出蜂鸣声: 响应包到达目时,会发出声音 ****/ping -a www.baidu.com /**** 只打印ping的汇总结果 ****/ping -c 5 -q www.baidu.com/**** 修改ping包(icmp包)的大小 ****/ping -s 100 -c 5 www.baidu.com示例: macdeiMac:PhoneNetSDK ethan$ ping www.baidu.comPING www.a.shifen.com (61.135.169.121): 56 data bytes64 bytes from 61.135.169.121: icmp_seq=0 ttl=49 time=32.559 ms64 bytes from 61.135.169.121: icmp_seq=1 ttl=49 time=32.413 ms64 bytes from 61.135.169.121: icmp_seq=2 ttl=49 time=32.489 ms^C--- www.a.shifen.com ping statistics ---3 packets transmitted, 3 packets received, 0.0% packet lossround-trip min/avg/max/stddev = 32.413/32.487/32.559/0.060 msmacdeiMac:PhoneNetSDK ethan$ 分析以上结果: ...

June 19, 2019 · 4 min · jiezi

归并排序

/**算法思想:把长度为n的输入序列分成两个长度为n/2的子序列;对这两个子序列分别采用归并排序;将两个排序好的子序列合并成一个最终的排序序列。O(n log n)的时间复杂度。代价是需要额外的内存空间。 */ /** 归并排序--将两段排序好的数组结合成一个排序数组 @param leftArray leftArray @param rightArray rightArray @return NSArray */ NSArray *merger(NSArray *leftArray, NSArray *rightArray) { NSUInteger capacity = leftArray.count+rightArray.count; NSMutableArray *arrayM = [NSMutableArray arrayWithCapacity:capacity]; for (NSInteger index=0, i = 0, j=0; index < capacity; index++) { if (i>=leftArray.count) { arrayM[index] = rightArray[j++]; } else if (j >= rightArray.count) { arrayM[index] = leftArray[i++]; } else if (leftArray[i] > rightArray[j]) { arrayM[index] = rightArray[j++]; } else { arrayM[index] = leftArray[i++]; } } return arrayM.copy;}/** 归并排序 ...

June 19, 2019 · 1 min · jiezi

Flutter

如何在Mac上配置Flutter与Android环境变量?在.bash_profile文件里配置

June 18, 2019 · 1 min · jiezi

实战iOSobjectivecswift静态代码分析

在此感谢前交友事业部小伙伴:HaiYi、LV、Yong,曾经的协助! 本文主要阐述使用SonarQube构建iOS:Objective-C、Swift静态代码分析,包括分享遇到的坑,文章有限,一些细节不能到位的,请各位脑补下,谢谢。SonarQube简介旧版Sonar展示维度如下(当时应该是11年左右开始使用的): 旧版sonar展示维度 新版SonarQube已经改变了关注维度,推出质量模型: Bugs:是出现了明显错误或是高度近似期望之外行为的代码。漏洞:是指代码中可能出现被黑客利用的潜在风险点。坏味道:代码异味会困扰代码的维护者并降低他们的开发效率。主要的衡量标准是修复它们所需的时间。建议根据团队需要更新到新版本:至少5.6+以上。新版SonarQube质量模型 SonarQube架构SonarQube平台的组成: 数据库:存放SoanrQube的配置数据,代码质量的快照数据Web服务:用于查看SonarQube配置数据,代码质量的快照数据分析器:对项目代码进行分析,生成质量结果数据并存于数据库中插件:各种语言支持的插件 不要忽略了CI:虽然SonarQube具备分析器,可以对多种编程语言进行构建分析,但是依然建议使用CI工具,例如Jenkins来管理日常构建,让SonarQube仅仅展示最终数据即可。 iOS静态代码分析目前iOS核心开发语言:Objective-C,也有不少项目采用了Swift语言,逐步过渡,因此项目的组成有两种模式: 单一语言使用:Objective-C、Swift混合语言使用:Objective-C+Swift下面通过实战分析两种模式的构建。 iOS静态代码分析的计划Objective-C原以为就跟Java构建一样,如此简单, 美好的计划 实际遇到的坑是很大的,而且很受伤, 踩过坑的路才踏实 捅一万刀也不过分 iOS静态代码分析:Objective-C实战工欲善其事必先利其器,工具如下: 环境工具:XCode 8.2+、Xcpretty 0.2.8、OCLint 0.12、xctool、gcovr构建静态分析插件SonarCFamily:![v2-4ae15f995ff68626b38e6a7ea750f008_hd.jpg][8]官方插件太贵了,找开源吧开源SonarQube Plugin for Objective C(传送门)插件安装参考网上教程,下载jar拷贝到SonarQube项目目录下:extensions/plugins 安装成功的示例 构建脚本run-sonar.sh在Jenkins的Execute shell配置脚本如下,也可以按照项目要求重名更好格式cd $WORKSPACExcodebuild -workspace xxx.xcworkspace -scheme xxx clean build | tee xcodebuild.log | xcpretty --report json-compilation-databasemv build/reports/compilation_db.json compile_commands.jsonoclint-json-compilation-database -exclude Pods -- -report-type pmd -o oclint.xml -max-priority-1 99999 -max-priority-2 99999 -max-priority-3 99999 -rc LONG_LINE=140 -rc LONG_METHOD=80 -rc NCSS_METHOD=50 -rc SHORT_VARIABLE_NAME=1 -rc CYCLOMATIC_COMPLEXITY=13 -rc MINIMUM_CASES_IN_SWITCH=2 -rc NPATH_COMPLEXITY=1500rm -rf sonar-reportsmkdir sonar-reportscat oclint.xml | sed "s#Switch Statements Should Have Default Rule#switch statements should have default#g" \| sed "s#missing hash method#must override hash with isEqual#g" \| sed "s#prefer early exits and continue#use early exits and continue#g" \| sed "s#use boxed expression#replace with boxed expression#g" \| sed "s#use container literal#replace with container literal#g" \| sed "s#use number literal#replace with number literal#g" \| sed "s#use object subscripting#replace with object subscripting#g" \| sed "s#missing default in switch statements#switch statements should have default#g" \| sed "s#unnecessary default statement in covered switch statement#switch statements don't need default when fully covered#g" \| sed "s#covered switch statements dont need default#switch statements don't need default when fully covered#g" > sonar-reports/oclint.xmlrm -f sonar-project.properties cat > sonar-project.properties <<- EOF sonar.projectKey=xxx-iOSsonar.projectName=xxx-iOS sonar.projectVersion=x.x.xsonar.language=objectivec sonar.sources=sources sonar.sourceEncoding=UTF-8 sonar.objectivec.oclint.reportPath=sonar-reports/oclint.xml EOF /bin/sh sonar-scanner -X构建结果 ...

June 15, 2019 · 2 min · jiezi

iOS-中gif图的显示

一、前言iOS开发中,大部分时候我们显示一张静态图就可以了,但是有的时候为了UI表现更生动,我就有可能需要展示gif图来达到效果了。 网上找了一下,显示gif图的框架找到了两个。 SDWebImageYYImage 二、显示本地gif图SDWebImage和YYImage的显示本地图片代码。 //load loacle gif image- (void)loadLocaleGifImage{ //sdwebimage [self labelFactoryWithFrame:CGRectMake(0, 80, kScreenWidth, 20) title:@"SDWebImage"]; NSString *path = [[NSBundle mainBundle] pathForResource:@"test" ofType:@"gif"]; NSData *gifData = [NSData dataWithContentsOfFile:path]; UIImageView *sdImageView = [[UIImageView alloc] initWithFrame:CGRectMake(0, 100, kScreenWidth, kScreenHeight/3)]; sdImageView.image = [UIImage sd_animatedGIFWithData:gifData]; [self.view addSubview:sdImageView]; //yyImage show gif image [self labelFactoryWithFrame:CGRectMake(0, kScreenHeight/2 - 20, kScreenWidth, 20) title:@"yyImage"]; YYImage *yyimage = [YYImage imageNamed:@"test.gif"]; YYAnimatedImageView *yyImageView = [[YYAnimatedImageView alloc] initWithImage:yyimage]; yyImageView.frame = CGRectMake(0, kScreenHeight/2, kScreenWidth, kScreenHeight/3); [self.view addSubview:yyImageView];}三、加载网络的gif图SDWebImage和YYImage的加载网络图片代码。 ...

June 14, 2019 · 1 min · jiezi

对NSArray和NSMutableArray的深拷贝浅拷贝的探究

一、原起下面两个问题,面试的时候应该经常会被问到。 对NSArray和NSMutableArray进行copy和mutableCopy分别会得到什么样的数组?当NSString作为一个对象的属性时,我们应该使用strong还是copy来修饰呢?今年三月份面试的时候,被这两个问题搞得很迷茫,今天特地研究了一下。相信您看完我的这篇文章和我有一样疑惑的您,心里会有一个清晰的答案。 二、NSMutableArray的copy和mutableCopy操作进行探究//1、对NSArray分别使用`copy` & `mutableCopy`进行内存地址的对比 NSArray *orgArr = @[@"ningjianwen", @"kongjiangmei"]; NSArray *copyArr = [orgArr copy]; NSMutableArray *mcopyArr = [orgArr mutableCopy]; [mcopyArr addObject:@"jiangxianjin"]; NSLog(@"NSArray 地址对比结果打印:"); NSLog(@"orgArr 地址: %p", orgArr); NSLog(@"copyArr 地址: %p", copyArr); NSLog(@"mcopyArr 地址: %p", mcopyArr);打印结果如下: 2019-06-13 20:05:48.915949+0800 ArrayCopyAndMutableCopy[54942:3399095] NSArray 地址对比结果打印:2019-06-13 20:05:48.916073+0800 ArrayCopyAndMutableCopy[54942:3399095] orgArr 地址: 0x600003716bc02019-06-13 20:05:48.916189+0800 ArrayCopyAndMutableCopy[54942:3399095] copyArr 地址: 0x600003716bc02019-06-13 20:05:48.916266+0800 ArrayCopyAndMutableCopy[54942:3399095] mcopyArr 地址: 0x600003951b90结果分析:从打印结果可以看出orgArr与copyArr内存地址是一致的,说明copy对NSArray进行的是浅拷贝。mcopyArr与orgArr内存地址是不一致的,说明mutableCopy对NSArray进行的是深拷贝,且拷贝之后数组变成了一个可变数组。 三、NSArray的copy和mutableCopy操作进行探究//2、对NSMutableArray分别使用`copy` & `mutableCopy`进行内存地址的对比 NSMutableArray *orgMArr = [NSMutableArray arrayWithObjects:@"星辰", @"江河",nil]; NSArray *copyMArr = [orgMArr copy]; NSMutableArray *mcopyMArr = [orgMArr mutableCopy]; [mcopyMArr addObject:@"日月"]; NSLog(@"NSMutableArray 地址对比结果打印:"); NSLog(@"orgMArr 地址: %p", orgMArr); NSLog(@"copyMArr 地址: %p", copyMArr); NSLog(@"mcopyMArr 地址: %p", mcopyMArr);打印结果如下: ...

June 14, 2019 · 2 min · jiezi

网上威尼斯网站注单异常提款不了怎么办

出黑qq287281222有很多虚拟的网站黑了一些玩家的钱,给很多网上的朋友造成很大的损失,对于这种情况,经过我长时间的接触被黑的朋友和自己的思考,总结出了一下方法:第一种,假装代理,找人去注册,注册后联系他们说能提款后你朋友就开始充值游戏,第二种,输到本金,再提款,不过本金多也有可能不给提款,运气成分很大,还是在没有说穿的情况下才能用,第3种,找人攻击网站,不给就不停攻击,一般半个月左右,半个月也没有效果就不用再攻击了,他们运营不了也许会给,具体还要看网站情况,这几个办法用的好,也是有点几率出款的,还有网上说的藏分和以分,以前很多网站都能用这种办法,后来网站多少关闭bbin电子游戏,所以后期大多数网站都不能再用这个办法了,被黑本身是很麻烦的事,大部分都是没办法的,所以选择靠谱的网站很重要,更多办法和具体操作可以来找我,这种事情,毕竟是比较复杂,三言两语的说不完也说不清楚 有这些信息可以拿来向银行支付宝等举报这些账户用于赌博的,如果都被查实对他们也算是不小的损失,你还可以威胁网站方就算以后换账户同样会举报。一般多少还是有效果的。网站信息可以向工信部或网络违法犯罪举报网等举报,赌博网本身是贼几乎都会心虚。你也可以明确告诉网站方你的钱是公款,或你是在校学生的生活费学费等等!大家都不妨可以尽量试试,也许你是在世华佗能救活自己呢。最重要还是我们不能贪心,多数被黑原因都是你赢钱超过他们底线了 如果你还舍不得网站上的数字的话,那它就会真的变成数字了。所以我们要舍得,有舍才可能有得。如你被黑10万,本金3万,我觉得能拿回本金三万也不是不可以接受的,所以你可以尽量输到只剩3万左右在跟网站方沟通取款。如果你肯输到剩1万左右的话那取款可能就更容易些了,要是贪心一分也舍不得那就真的没救了

June 4, 2019 · 1 min · jiezi

iOS开发中定义枚举的正确姿势NSENUM-VS-enum

iOS开发中枚举也是经常会用到的数据类型之一。最近在整理别人写的老项目的时候,发现枚举的定义使用了多种方式。 方式1typedef enum { MJPropertyKeyTypeDictionary = 0, // 字典的key MJPropertyKeyTypeArray // 数组的key} MJPropertyKeyType;方式2typedef enum: NSInteger { None, Melee, Fire, Ice, Posion }AttackType;方式3typedef NS_ENUM(NSUInteger, MeiziCategory) { MeiziCategoryAll = 0, MeiziCategoryDaXiong, MeiziCategoryQiaoTun, MeiziCategoryHeisi, MeiziCategoryMeiTui, MeiziCategoryQingXin, MeiziCategoryZaHui};方式4这种比较特殊支持位操作。 typedef NS_OPTIONS(NSUInteger, ActionType) { ActionTypeUp = 1 << 0, // 1 ActionTypeDown = 1 << 1, // 2 ActionTypeRight = 1 << 2, // 4 ActionTypeLeft = 1 << 3, // 8};针对于前三种方式,我们应该使用那一种更新好呢? 这是来自Stack Overflow的解释。 ...

May 31, 2019 · 1 min · jiezi

ObjectiveC的内存管理2从MRC到ARC

罗里吧嗦颠三倒四,单纯的个人笔记。 MRC引用计数上一篇已经有大概讲过。在Objective-C里,每个继承自NSObject的对象都会记录自身的引用计数,一番加加减减之后,变成0就会释放掉。MRC是Mannul Reference Counting的缩写,意思也很简单,这番加加减减都靠手动管理的意思。 使用时的基本原则是:管好自己。每个对象,引用别的对象时加了几次计数最终到了不用的时候就要减几次。不能多也不能少。这样就聚焦了很多。 导致引用计数增加的操作,显式的retain不多说,剩下的就是四个关键字:alloc、new(以及new开头的方法)、copy、mutableCopy,使用这四个关键字得到的对象,就算你自己加的引用计数,回头要自己减掉。 引用计数减少的操作就是release了。 AutoReleaseAutoReleasePool是个自动释放池,加入其中的对象会延迟到Pool“结束”时释放。在MRC中,你可以显式创建一个NSAutoReleasePool,显式地将一个对象加入进去,并显式释放AutoReleasePool: NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];// Code benefitting from a local autorelease pool.NSObject *obj = [[[NSObject alloc] init] autorelease];[pool release];//[pool drain];在ARC中,需要通过特殊的语法: @autoreleasepool { // Code benefitting from a local autorelease pool.}默认地,每个Runloop迭代开始时会创建一个AutoReleasePool,Runloop迭代结束时释放。也就是说,当我们没有显式创建AutoReleasePool时,autorelease的对象会在Runloop迭代结束时释放。当我们显式地创建AutoReleasePool时,其释放时机是我们决定的(显式调用release/drain或block结束)。 主动使用AutoReleasePool的目的通常是为了控制内存峰值。比如,我有一个大循环,每次循环都会创建比较大的autorelease的临时对象。如果不显式释放,这些临时对象会在整个循环结束后才一起释放,期间可能造成内存占用过高。这种情况下就可以在每次循环内声明autoreleasepool,保证临时对象不会堆积。 ARCARC为我们自动地做了很多,屏蔽了很多细节,理论上来说,我们只需要关注对象间的所有权关系即可。上层机制虽然简单,涉及到的细节还是有很多的,可喜可贺的是,ARC是有标准文档的。简直... ARC提供的变量修饰符有以下几个: __strong__weak__unsafe_unretaied__autoreleasing提供的属性修饰符有: assign 对应的所有权类型是 __unsafe_unretained。copy 对应的所有权类型是 __strong。retain 对应的所有权类型是 __strong。strong 对应的所有权类型是 __strong。unsafe_unretained对应的所有权类型是__unsafe_unretained。weak 对应的所有权类型是 __weak。(基本类型默认是assign,对象类型默认是strong) __strong强引用,不多说了。注意声明变量和属性时若未加说明,默认是强引用。 __weak弱引用。当对象被释放时,weak修饰的变量会被置为nil。仔细想想,想要实现这个特性,所有的weak变量都需要放到一个全局的map里,实现成本还是比较高的。 __unsafe_unretained不做任何额外操作。 __autoreleasing__autoreleasing标记的变量等价于调用autorelease方法 想到一个小问题:对于函数返回值,ARC是怎么知道要不要加引用计数呢?看这几行代码: - (void)testMethod{ NSObject *obj = [NSObject new]; NSArray *array = [NSArray array]; // do something}在ARC中,obj和array用完之后都会被自动释放,但是细想之下其实有不少细节。要知道,[NSObject new]返回的对象引用计数是有+1的,而[NSArray array]并不是。这俩玩意儿引用计数差了1,ARC是怎么知道谁要多释放一次的?在MRC中,我们知道new出来的obj需要手动释放,而array就不需要,是通过方法的关键词进行判断。但是方法中的关键词不应该是某种约定吗?ARC难道也会去看一个方法是否是以new开头?看了文档之后发现...ARC还真是这么做的... ...

May 26, 2019 · 1 min · jiezi

ObjectiveC中block的循环引用问题

目标:block执行过程中,self不会释放;执行完可以释放。 最初block中直接使用self会强引用。 self.myBlock = ^() { [self doSomething];};或者使用了对象的属性 self.myBlock = ^() { NSString *str = _str; NSString *str2 = self.str;};在这样的情况下,self强引用block,block也持有该对象,导致循环引用。 要注意的是,只有在self强引用block的时候才会有这样的问题。一般使用GCD或NSOperation时使用的内联block是不会出现循环引用的。 加入weak self__weak __typeof(self) weakSelf = self;self.myBlock = ^() { [weakSelf doSomething];};这样,self持有了block,但block对self是弱引用,就不会导致循环引用了。 而在[weakSelf doSomething]过程中,self是不会释放的,完美。 但是,如果是这样呢? __weak __typeof(self) weakSelf = self;self.myBlock = ^() { [weakSelf doSomething]; [weakSelf doSomething2];};在[weakSelf doSomething]和[weakSelf doSomething2]之间,self可能会被释放掉。这可能会导致奇怪的问题。 加入strong self__weak __typeof(self) weakSelf = self;self.myBlock = ^() { __strong __typeof(self) strongSelf = weakSelf; [strongSelf doSomething]; [strongSelf doSomething2];};这样,block既没有持有self,又能保证block在执行过程中self不被释放,真正达到了最初的目标。 ...

May 26, 2019 · 1 min · jiezi

OCLint-实现-Code-Review-给你的代码提提质量

工程代码质量,一个永恒的话题。好的质量的好处不言而喻,团队成员间除了保持统一的风格和较高的自我约束力之外,还需要一些工具来统计分析代码质量问题。 本文就是针对 OC 项目,提出的一个思路和实践步骤的记录,最后形成了一个可以直接用的脚本。如果觉得文章篇幅过长,则直接可以下载脚本 OCLint is a static code analysis tool for improving quality and reducing defects by inspecting C, C++ and Objective-C code and looking for potential problems ...从官方的解释来看,它通过检查 C、C++、Objective-C 代码来寻找潜在问题,来提高代码质量并减少缺陷的静态代码分析工具 OCLint 的下载和安装有3种方式安装,分别为 Homebrew、源代码编译安装、下载安装包安装。区别: 如果需要自定义 Lint 规则,则需要下载源码编译安装如果仅仅是使用自带的规则来 Lint,那么以上3种安装方式都可以1. Homebrew 安装在安装前,确保安装了 homebrew。步骤简单快捷 brew tap oclint/formulae brew install oclint2. 安装包安装进入 OCLint 在 Github 中的地址,选择 Release。选择最新版本的安装包(目前最新版本为:oclint-0.13.1-x86_64-darwin-17.4.0.tar.gz)解压下载文件。将文件存放到一个合适的位置。(比如我选择将这些需要的源代码存放到 Document 目录下)在终端编辑当前环境的配置文件,我使用的是 zsh,所以编辑 .zshrc 文件。(如果使用系统的终端则编辑 .bash_profile 文件) OCLint_PATH=/Users/liubinpeng/Desktop/oclint/build/oclint-releaseexport PATH=$OCLint_PATH/bin:$PATH将配置文件 source 一下。 source .zshrc // 如果你使用系统的终端则执行 soucer .bash_profile验证是否安装成功。在终端输入 oclint --version3. 源码编译安装homebrew 安装 CMake 和 Ninja 这2个编译工具 ...

May 26, 2019 · 6 min · jiezi

ObjectiveC的内存管理1内存管理概述

概述应用程序开发中,内存管理是个重要的话题。简单而言,语言层面的内存管理基本有三类: 1. 纯粹的手动管理如C和曾经的C++。 char *some_string = malloc(BUFFER_SIZE);// do somethingfree(some_string);这个简单的例子里用完就释放还好,但是有时候这个some_string被传来传去不知道飞哪儿去了,就比较尴尬。纯手动管理的代价是程序员的心智负担比较重。即使后来C++程序员们抽象出RAII这样的实践规范,一定程度上降低了管理的复杂度,但是相对来说成本还是略高。随着语言的发展,已经很少有语言只依赖手动管理内存了。 2. 基于某些机制实现半自动管理这里的某些机制其实通常就是引用计数。毕竟这是最简单的内存管理辅助手段。 引用计数是计算机编程语言中的一种内存管理技术,是指将资源(可以是对象、内存或磁盘空间等等)的被引用次数保存起来,当被引用次数变为零时就将其释放的过程。引用计数大家都了解,不多说,单纯的自动使用引用计数问题在于无法解决循环引用的问题。很多语言选择让程序员付出一点劳动来解决这个问题。早年,Objc选择的是退一步,完全让程序员来管理引用计数的加减,称为MRC,显然管理成本偏高。后来推出了ARC,提供了更健全的机制,程序员只要标识出对象间的引用关系是强引用还是弱引用就可以了,大大降低了程序员的负担。虽然走这个路子的语言不算多,但除了Objc之外还是有好几个的。C++的智能指针跟Objc的ARC就比较相似。而Rust的所有权模型本质上也是类似的。 3. 自动垃圾回收通过GC自动管理内存大概是现在的主流了。对程序员来讲实在是太舒适了,开发时几乎不用考虑内存管理的问题。Java、JavaScript、Python、go等一大票语言都是走的这条路。GC是基于可达性分析算法的,即,从根节点(全局变量、局部变量等等)出发,遍历引用到的对象,所有没遍历到的对象就可以释放了。当然从原理到实际应用中间差了十万八千里。朴素的GC会经常造成Stop The World。一旦Stop-the-world发生,除了GC所需的线程外,其他线程都将停止工作,中断了的线程直到GC任务结束才继续它们的任务。于是很多GC算法被发明出来用于优化、减少。常见的CMS、G1回收算法都极大地减少了STW的时间,但仍然不能完全避免。R大在Java 大内存应用(10G 以上)会不会出现严重的停顿?中提到Zing JVM采用的C4算法是可以完全避免STW的,不过看起来为了避免STW,C4算法会吃掉更多的内存,程序吞吐量会受到影响。 小结总结一下吧,纯手动管理基本上已被淘汰,ARC(暂且把方法二这类都称为ARC吧)和GC对比之下,对开发者,ARC需要程序员付出一定的代价进行管理,GC则基本上完全解放双手;性能上,GC通常会造成STW现象,对响应时间比较敏感的程序,比如高频交易系统,是很难接受的,而ARC不会对性能造成明显影响。 几点有趣的事情1. 总体性能总体性能上,只要不是内存跑得特别满,ARC的总体代价是高于GC的。其实想想就知道了,GC只关注两次回收间的变化,而ARC要对每一次引用的改变进行计数,总体性能上比GC差是很正常的,但由于ARC的耗时是均匀分布在运行时间里的,通常我们不用很关注。关于这个问题可以参考这篇论文。 2. cpython的方案另外比较特别的是,Python的默认解释器CPython中应用了引用计数与垃圾回收相结合的手法,没有循环引用的对象会被引用计数回收,剩下的交给GC处理,大大降低了GC的压力。感觉很有意思。 3. ARC的性能在类ARC方案上,C++提供的能力是比较全面的。这可能跟c++常用于一些性能比较苛刻的场景有关。出于性能原因,使用c++智能指针时有如下指导思想: 对象的所有权不重要时 ,用裸指针对象的所有权唯一时,用unique_ptr,能用unique_ptr就不要用shared_ptr。要处理复杂情况时,可以使用shared_ptr,但需要注意不要滥用。当引用关系不影响所有权时,用weak_ptr。Rust也有类似的能力。而python和Objective-C就没有这么多讲究,所有的引用计数其实都是shared_ptr。Objc在iOS上这么多年,而后来的swift也传承了ARC,基本上可以认为,移动端应用从小到大都不差这么一丢丢性能。 以此推论,绝大部分c++应用也完全没必要关注这几个指针间的差异,操起shared_ptr就是干。

May 25, 2019 · 1 min · jiezi

5G云展厅技术服务支持

联系电话:028-85099799联系邮箱:Service@esstx.cnApp功能截图

May 24, 2019 · 1 min · jiezi

公务员备考攻略复习经验过程

你是不和我一样,面对公考这条路,在很长一段时间里,纠结和彷徨。 自己到底能不能成功? 公务员是我未来想要的生活吗? 万一没有考上怎么办? 不管过去我们的内心经历了多少次的斗争,但当我们在Timing里将目标定位“公务员”的时候,就要让自己平静下来,接受自己的绝对,义无反顾地向前冲刺。 你要调整好自己的心态,不受到别人的干扰,脚踏实地攻克重重难关。 行测第一轮:专项练习 按照模块将五大题型逐一进行学习,掌握每个模块的基本方法,思考角度,一个模块一个模块的进行专项刷题, 这个阶段要花的时间多一点,因为思维习惯是很难短时间内培养出来的,准确率不高的一定先不要追求速度追求做多少题,要结合学到的做题方法去分析想问题的角度是哪里不对,遇到这类题要换角度分析,有意识的去锻炼,去思考,慢慢的,配合刷题就可以纠正过来。 这一步大概用了1个月左右的时间,每天120题左右,每一题都要弄明白原题,模块宝典那么厚,在进行这一步学习的时候我是硬生生的全本做完了。 行测第二轮:做真题套题 1.先按照考试形式做两套国考套题,然后重点是分析。这两套题请认真对待,分析自己的长处,即易得分点,然后了解自己的弱项,即失分多的点,一定要对自己的能力有个客观正确的认识!!!!这点尤其重要!!!!!!!!!! 2.接着就是分析真题题型出题角度,要知道这道题考的是什么,为什么这么出,还有每个模块各个题型的比例大概是多少,哪些是基础分等。 3.然后就是疯狂地刷套题了,计时计时计时!!!重要的事情说三遍!!!!!没刷完一套题就要认真对答案,这个过程其实很慢,错题一定要认真分析!!!!四个选项的区别是什么,陷阱在哪里,为什么选错!!!一定要动脑子思考!!!!!! 这三点不管你用什么复习方法都要特别注意,我复习的时候因为阿甘上岸说里教的技巧特别注重实际解题速度和正确率的平衡,所以并没有在做题时间上太发愁,套卷做了30套左右的时候解题思路就清晰起来,一眼就能看出来哪些是必定能拿分的容易题哪些是有些麻烦的陷阱题,然后区分对待,以拿分为主,这点大家做题时要注意,并不是所有题做完你就分高,动的取舍也是公考成功的重要因素之一! 四、行测第三轮:重复刷真题 只做真题!!!只做真题!!!!只做真题!!!! 有人会说,那些题都做过了,答案都记住了,那有本事你再重新做一遍考满分啊!!基本上该错的题还是会错,因为在有限的时间内是没有思考的空间的,全凭借的是第一反应,第一思维模式,这个东西很难改变,是你多少年学习积累下来的东西。所以,重复做套题!! 大概隔一两周做之前做过的套题,你要要求自己缩短10分钟的时间,你要要求自己成绩至少达到90分,因为你做过啊!!去试试吧,错的还是之前错的题,所以,踏踏实实做吧,别报侥幸心理,大多数人都是过于自信,以为自己做的够了,用一些没报考好职位啊,运气不好啊,题型出偏了等等烂借口来安慰自己,其实你做的还远远不够,还差很远。 五、关于软件 备考的期间选择一些好用的软件,的确可以帮助我们提高效率!!!我推荐2个我常用的工具。一个是印象笔记,一个是MindMaster思维导图。这2个应用都有电脑版和手机版,并且有免费版。有些时候做过的复习笔记不方便用保存,就用印象笔记的扫描功能保存后,保存到云文件里。它的tag标签分类很强大,可以用来整理某个题型的错题本,通过搜索即可快速找到。 另外就是MindMaster思维导图软件,最早是在知乎里看到的口碑安利,确实适合Timing各位道友们使用。它是一款很轻便的思维导图APP,在手机和电脑都可以使用,用于整理基础的知识内容,解放大脑,协助记忆复杂枯燥的内容。 最后,以上复习经验过程仅供参考,希望大家能结合自己复习节奏进行调整,祝顺利

May 24, 2019 · 1 min · jiezi

关于常量的思考与总结

写在前面全局常量作为开发人员一定是一个比较熟悉的概念。全局常量的写法自然也比较多,最近在进行项目的常量重构时看到了各种各样的写法,其中宏定义占大部分,然而有很多使用宏定义是不规范的,而且宏定义只是在预编译阶段进行文本替换,不进行类型检查,从网上看到大量使用宏定义会拖慢编译速度。 所以在定义全局常量时,为了提高开发过程中的规范度和编译速度,宏定义并不是最佳选择。所以我重构的原则是: 能声明成外部常量的,尽量声明成外部常量,万不得已的才使用宏定义。 一、宏计算机科学里的宏(Macro),是一种批量处理的称谓。一般说来,宏是一种规则或模式,或称语法替换 ,用于说明某一特定输入(通常是字符串)如何根据预定义的规则转换成对应的输出(通常也是字符串)。这种替换在预编译时进行,称作宏展开。在我刚刚接触开发的时候,我学习到的定义全局常量的方法就是宏。由于宏只是做字符串的替换,它还是有它的优势的。我们可以使用它来一些常量、函数。 例子:1、定义屏幕相关的常量。 /屏幕宽高,frame,bounds,size#define kBKScreenWidth [[UIScreen mainScreen] bounds].size.width#define kBKScreenHeight [[UIScreen mainScreen] bounds].size.height#define kBKScreenBounds [UIScreen mainScreen].bounds#define kBKScale [[UIScreen mainScreen] scale]2、定义调试的log输出函数。 #pragma mark - DEBUG#ifdef DEBUG// 定义是输出Log#define DLog(format, ...) NSLog(@"Line[%d] %s " format, __LINE__, __PRETTY_FUNCTION__, ##__VA_ARGS__)#else// 定义是输出Log#define DLog(format, ...)#endif从上面的示例可以看出宏定义的关键字是#define.宏定义常量的公式: #define constantA statementA预编译的时候使用constantA部分的内容替换成statementA。对于函数的定义则稍微复杂一些,有参数和无参数。无参数的函数是直接进行字符串的替换,有参数的还要进行参数的替换。 二、extern使用extern关键字声明全局常量,这个应该算是最标准的做法了。这个是后面在网上的帖子中有看到,当然开源代码中也看到过,确定无疑是定义全局常量的最佳选择。 extern定义全局常量分为声明部分和赋值部分,分别放在 .h & .m文件中。 代码示例: UserInfoModelConstants.hextern NSString *const BKUSER_AGE_KEY ;extern NSString *const BKUSER_TELPHONE_KEY ;extern NSString *const BKUSER_ADDRESS_KEY ;extern NSString *const BKUSER_BRIEF_KEY ;UserInfoModelConstants.mNSString *const BKUSER_AGE_KEY = @"XXXXX.userAge";NSString *const BKUSER_TELPHONE_KEY = @"XXXXX.telphoneNO";NSString *const BKUSER_ADDRESS_KEY = @"XXXXX.address"; NSString *const BKUSER_BRIEF_KEY = @"XXXXX.brief";特别提示: ...

May 23, 2019 · 1 min · jiezi

面向切面编程Aspects源码解析

面向切面编程所谓的面向切面编程(AOP),原理就是在不更改正常业务的流程的前提下,通过一个动态代理类,实现对目标对象嵌入的附加的操作。 简单说,就是在不影响我们现在正常业务的情况下,对某些类的某些方法嵌入操作。我们可以很通俗的理解一个方法可以有方法前和方法后这两个切面,当然还可以把方法执行过程看过一个整的切面去hook。 在我们的iOS开发中,AOP的实现方法就是使用Runtime的Swizzling Method改变selector指向的实现,在新的实现中添加新的操作,执行完新实现之后,再处理之前的实现逻辑。 AspectsAspects是iOS平台比较成熟的AOP的框架,这次我们主要来研究一下这个库的源码。 基于Aspects 1.4.1版本。 总览Aspects给出了两个方法,一个类方法一个实例方法,使用起来非常简单。 + (id<AspectToken>)aspect_hookSelector:(SEL)selector withOptions:(AspectOptions)options usingBlock:(id)block error:(NSError **)error;- (id<AspectToken>)aspect_hookSelector:(SEL)selector withOptions:(AspectOptions)options usingBlock:(id)block error:(NSError **)error;传参也很容易理解,selector自然就是我们要hook的方法,options使我们要hook的位置,下面具体再说,block是一个回调,也就是我们所说的要嵌入的代码逻辑,error就是hook失败,当然了失败的原因较多,我们下面会提到。 typedef NS_OPTIONS(NSUInteger, AspectOptions) { AspectPositionAfter = 0, /// Called after the original implementation (default) AspectPositionInstead = 1, /// Will replace the original implementation. AspectPositionBefore = 2, /// Called before the original implementation. AspectOptionAutomaticRemoval = 1 << 3 /// Will remove the hook after the first execution.};options也是一个枚举的类型,看一下里面定义的字段就很容易明白了,AspectPositionAfter是表示嵌入的放大要在被hook方法原来逻辑之前之后执行,AspectPositionBefore是之前执行,AspectPositionInstead表示要用嵌入的代码替换掉之前的逻辑,AspectOptionAutomaticRemoval表示hook执行后,移除hook。 因为是NS_OPTIONS类型,可多选。 ...

May 23, 2019 · 3 min · jiezi

ObjectiveC-Method-Swizzling

Method Swizzling已经被聊烂了,都知道这是Objective-C的黑魔法,可以交换两个方法的实现。今天我也来聊一下Method Swizzling。 使用方法我们先贴上这一大把代码吧 @interface UIViewController (Swizzling)@end@implementation UIViewController (Swizzling)+ (void)load { static dispatch_once_t onceToken; dispatch_once(&onceToken, ^{ Class class = [self class]; SEL originalSelector = @selector(viewWillAppear:); SEL swizzledSelector = @selector(swizzling_viewWillAppear:); Method originalMethod = class_getInstanceMethod(class, originalSelector); Method swizzledMethod = class_getInstanceMethod(class, swizzledSelector); BOOL success = class_addMethod(class, originalSelector, method_getImplementation(swizzledMethod), method_getTypeEncoding(swizzledMethod)); if (success) { class_replaceMethod(class, swizzledSelector, method_getImplementation(originalMethod), method_getTypeEncoding(originalMethod)); } else { method_exchangeImplementations(originalMethod, swizzledMethod); } });}#pragma mark - Method Swizzling- (void)swizzling_viewWillAppear:(BOOL)animated { [self swizzling_viewWillAppear:animated]; NSLog(@"==%@",NSStringFromClass([self class]));}@end好的,上面就是Method Swizzling的使用方法,将方法- (void)swizzling_viewWillAppear:(BOOL)animated和系统级方法- (void)viewWillAppear:(BOOL)animated交换。常用的场景就是埋点,这个咱就不细说了。 ...

May 16, 2019 · 2 min · jiezi

iOS知识梳理-category和extension

category和extensioncategory的能力category主要作用是在不改变原有类的前提下,动态地给这个类添加一些方法。苹果早年一份官方文档指出,category主要有三种用途: 给现有的类添加方法;将一个类的实现拆分成多个独立的源文件;声明私有的方法。这里我们逐个分析。 1. 给现有的类添加方法常见。比如给UIColor添加一些跟16进制色值互转的方法。如下: @interface UIColor (Hex) + (UIColor*) colorWithHex: (NSUInteger) hex;@end@implementation UIColor (Hex)+ (UIColor*) colorWithHex: (NSUInteger)hex { CGFloat red, green, blue, alpha; red = ((CGFloat)((hex >> 16) & 0xFF)) / ((CGFloat)0xFF); green = ((CGFloat)((hex >> 8) & 0xFF)) / ((CGFloat)0xFF); blue = ((CGFloat)((hex >> 0) & 0xFF)) / ((CGFloat)0xFF); alpha = hex > 0xFFFFFF ? ((CGFloat)((hex >> 24) & 0xFF)) / ((CGFloat)0xFF) : 1; return [UIColor colorWithRed: red green:green blue:blue alpha:alpha];}@end2. 将一个类的实现拆分成多个独立的源文件这种在小项目中可能少见一些,但在大型项目中比较多。比如某日活过亿的iOS软件,其配置系统中大约有400份配置,很多功能根据配置的内容会有不同的表现,不同用户拉取不同内容的配置。这400份配置会在用户首次登陆时全量拉取,在以后登陆时增量拉取。注意这都是一条请求带回来的。如果把这400份配置的解析处理都写到一个文件里,这个文件显然会膨胀到一个很恐怖的地步。最好的方式就是让每个业务添加自己的分类,在分类中处理自己的配置内容。 ...

May 14, 2019 · 1 min · jiezi

手把手教你把项目上传到github上

原起为什么要把自己做的一些东西上传到github上?在软件行业竞争日益激烈的行情下,面试的时候,github上有高Star开源项目,绝对是个加分项。同时知识分享也是一种美德。这种双向受益的事,何乐而不为呢。下面就手把手教你把你自己做的项目上传到github上,让你在面试的时候脱颖而出。 项目上传github的步骤步骤快速一览: cd 到本地项目的根目录下git initgit add .git commit -m "提交说明"git remote add origin '你在github为该项目创建的仓库地址'git pull origin mastergit push -u origin master -f第一步:初始化git仓库cd到你的本地根目录下,执行git init,在你的项目下创建了一个git仓库。 第二步:将项目下的所有文件添加到git仓库在上一步的同级目录下执行git add .。提别提示,此步如果怕git add .出问题,可以先使用git status先查看那些文件被修改了,然后在分别添加就保险了,只是那样会比较麻烦,此处看个人习惯。 第三步:加add到缓存区的文件commit到本地仓库还是在刚才的目录,执行git commit -m "提交注释"。 第四步:去github上创建自己的Repository只有本地仓库肯定是不够的,这一步,我们就去github上创建仓库。在github上创建仓库的步骤如下: 第五步:将本地的仓库关联到github上git remote add origin https://github.com/ningjianwe...记得把上面的仓库地址换成你自己的仓库地址。 第六步:上传代码之前先pull以下github上的代码git pull origin master第七步:把代码push到github上git push -u origin master执行完后,如果没有异常,等待执行完就上传成功了,中间可能会让你输入Username和Password,你只要输入github的账号和密码就行了。 注意事项如果执行最后一步的时候报了类似下方的错, 不要慌,在第七步的命令后跟一个参数-f即可。 git push -u origin master -f这个时候会显示项目正在上传,等着上传完就可以在github上看到你的项目了。 温馨提示:不要忘记在github上添加你的SSHkey,这个请自行百度。

May 12, 2019 · 1 min · jiezi

iOS知识梳理-Objc语法中值得注意的东西

属性声明@property、@synthesize和@dynamic objc推荐我们通过set/get方法访问对象的属性。很显然,为每一个属性手动添加set/get方法的声明和实现是个性价比很低的重复劳动。因此,objc提供了一些关键字帮助我们简化这一过程。这几个关键字实际上就是这么回事儿。 单独做了总结:Objective-C的@property、@synthesize和@dynamic 注释常规的//和/* */就不多说了 值得一提的是#pragma语法, 我们可以用#pragma mark - UITableViewDataSource这样的写法来为代码分块,xcode将会提供导航。 参考:#pragma enumtypedef NS_ENUM(NSUInteger, TTGState) { TTGStateOK = 0, TTGStateError, TTGStateUnknow};参考 Enum in OC 空指针NULL:C的空指针 (void *)0nil:objc的空对象 (id)0Nil:空的类指针 (Class)0NSNull:nil的包装类,为了表示dic里面的某个value为空 [NSNull null]总结:前三个数值上是相等的,在objc中用nil比较普遍 参考nil / Nil / NULL / NSNull BOOL常见的就是bool和BOOL。 bool是c语言的,单独的一个类型(_Bool),在数值上true = 1,false = 0 bool类型只有true和false两种状态。 另外,条件判断语句在判断时,是以0为false,非0为true进行判断的。 由此有以下结论: 2 == true //假(bool)2 == true //真2 //真BOOL是ObjC定义的真假值类型,它在64位机器上等价于bool,在32位机器上是signed char 其定义如下,参考objc4-750 /// Type to represent a boolean value.#if defined(__OBJC_BOOL_IS_BOOL) // Honor __OBJC_BOOL_IS_BOOL when available.# if __OBJC_BOOL_IS_BOOL# define OBJC_BOOL_IS_BOOL 1# else# define OBJC_BOOL_IS_BOOL 0# endif#else // __OBJC_BOOL_IS_BOOL not set.# if TARGET_OS_OSX || TARGET_OS_IOSMAC || (TARGET_OS_IOS && !__LP64__ && !__ARM_ARCH_7K)# define OBJC_BOOL_IS_BOOL 0# else# define OBJC_BOOL_IS_BOOL 1# endif#endif#if OBJC_BOOL_IS_BOOL typedef bool BOOL;#else# define OBJC_BOOL_IS_CHAR 1 typedef signed char BOOL; // BOOL is explicitly signed so @encode(BOOL) == "c" rather than "C" // even if -funsigned-char is used.#endifYES和NO ...

May 11, 2019 · 1 min · jiezi

iOS知识梳理-关于ObjectiveC

作为早期的面向对象语言,且直到现在仍然为人们所熟知,Objective-C显然有其独到之处。这里对Objective-C的特点进行简单阐述。 动态?静态?Objc这个语言具有比较强大的动态特性,常提到的包括动态类型、动态绑定、动态加载。动态类型主要指类指针id,其类型是在运行时才能决定的。动态绑定指一个对象的方法可以在运行时添加、替换。动态加载指运行时可以动态地创建新的类。因此我们称Objc是一个动态类型的动态语言。 虽然Objc有着诸多动态特性,但它并不是个像JavaScript、python那样典型的动态语言,有的人会说Objc是个“动静结合”的语言。它虽然有动态类型,但是更常用的是静态类型系统;而动态加载、动态绑定更是高级特性而非基本语法。 总的而言,Objc写起来更像静态语言的感觉(啰嗦),但它有着丰富的动态特性。 消息传递 vs 函数调用对象间的通信形式应当是怎样的?函数调用的方式是指,调用方直接或间接地拿到被调用方的函数地址进行函数调用。而消息传递是指,调用方只是把方法名和参数等信息传递给被调用方,具体的执行由被调用方处理。 显然,消息机制会更加灵活。早年的半篇神作function/bind的救赎(上)中大佬对消息传递和方法调用的区别做了深刻分析,阐述了消息机制的优点,值得一读。不过作者可能把调用机制看得太重了。 内存管理内存管理大体上有三类,开发者手动维护、基于引用计数并提供一些机制解决循环引用、自动垃圾收集。自己手动派主要就是c/c++了,性能最好,缺点是开发者心智负担重。垃圾回收派是最多的,现在流行的语言大多都是gc的路子,Java/JavaScript/python/golang等等。优点是轻松,缺点是通常情况下gc是阻塞的,触发gc时会有几十ms到数百ms的卡顿。现在也有并发gc的技术,不过用得并不广泛,不太了解。Objc属于基于引用计数并提供一些机制解决循环引用的。我们知道,朴素的引用计数存在的问题是无法识别循环引用。Objc提供了ARC机制,开发者标记对象间的引用是强引用还是弱引用,Objc的runtime来解决剩下的事情。使得内存管理的性能和开发者的负担得到了比较好的平衡。同属这一流派的还有个Rust的所有权机制。 方法定义和方法调用的语法这个点只是个小细节了,跟上面的不在一个层面,不过这可能是大部分人初学Objc时觉得跟其它语言最大的不同。 Objc的方法声明是这个样子的: - (void)insertString:(NSString *)aString atIndex:(NSUInteger)loc;它包含了方法标示符(-)、返回类型(void)、方法名称(insertString:atIndex:)、参数类型和参数名称。注意,每个参数前面那一两个次的描述,是算在函数名里面的。其实回头看来,这样的语法是有一定优点的。前两天看一个c++的库,里面有个函数有12个参数,调用的时候就很烦,总担心我填的参数错位了: demoFunc(var1,var2,var3,var4,var5,var6,var7,var8,var9,var10,var11,var12)Objc虽然啰嗦了点,但是看起来其实会比较清晰,比如一个很长的方法: - (id)initWithBitmapDataPlanes:(unsigned char **)planes pixelsWide:(NSInteger)width pixelsHigh:(NSInteger)height bitsPerSample:(NSInteger)bps samplesPerPixel:(NSInteger)spp hasAlpha:(BOOL)alpha isPlanar:(BOOL)isPlanar colorSpaceName:(NSString *)colorSpaceName bitmapFormat:(NSBitmapFormat)bitmapFormat bytesPerRow:(NSInteger)rowBytes bitsPerPixel:(NSInteger)pixelBits;调用时是这个样子: NSBitmapImageRep *bitmap = [[NSBitmapImageRep alloc] initWithBitmapDataPlanes:(unsigned char **)&data pixelsWide:gWidth pixelsHigh:gHeight bitsPerSample:8 samplesPerPixel:3 hasAlpha:NO isPlanar:NO colorSpaceName:NSCalibratedRGBColorSpace bytesPerRow:gWidth*3 bitsPerPixel:24];每个参数是啥会比较明确。

May 11, 2019 · 1 min · jiezi

ios-原生骨架库网络过渡动画封装

最新版 2.0.5,release版本目录关于 TABAnimated实现原理优点演变过程效果图安装 使用Cocoapods手动导入使用步骤扩展回调Tips属性相关强调最后关于 TABAnimatedTABAnimated的起源版本是模仿简书网页的骨架屏动态效果。在v1.9探索过模版模式,但是重复的工作量并不利于快速构建,而且两种模式的存在不合理,所以在v2.1删除了这种设定,但是模版模式的出现到删除,并不是没有收获,相反带来了更合理的实现方案,更便捷的构建方式。 实现原理TABAnimated 需要一个控制视图,进行开关动画。该控制视图下的所有subViews都将加入动画队列。 TABAnimated通过控制视图的subViews的位置及相关信息创建TABCompentLayer。普通控制视图,有一个TABLayer表格视图,每一个cell都有一个TABLayerTABLayer负责管理并显示所有的TABCompentLayer。 当使用约束进行布局时,约束不足且没有数据时,致使subViews的位置信息不能体现出来,TABAnimated会进行数据预填充。 优点集成迅速零耦合,易于将其动画逻辑封装到基础库高性能,极少的内存损耗链式语法,方便快捷,可读性高完全自定制,适应99.99%的视图演变过程看不清楚可以放大一下 简单说明一下:第一张图:原有表格组件, 有数据时的展示情况第二张图:是在该表格组件开启动画后,映射出的动画组,相信你可以看出来,效果并不是很美观。第三张图:针对这个不美观的动画组,通过回调,进行预处理,下文进行说明 效果图| 动态效果 | 卡片投影 | 呼吸灯 | | ------ | ------ | ------ | | | | | | 闪光灯 | 分段视图 | 嵌套表格 || ------ | ------ | ------ | | | | | 安装使用 CocoaPodspod 'TABAnimated'手动导入将TABAnimated文件夹拖入工程 使用步骤您只需要四步 在 didFinishLaunchingWithOptions 中初始化 TABAimated还有其他的全局属性,下文用表格呈现。 **老用户注意:原TABViewAnimated已经改名为TABAnimated**如今的TABViewAnimated已经成为UIView的骨架对象 // init `TABAnimated`, and set the properties you need.[[TABAnimated sharedAnimated] initWithOnlySkeleton];// open log[TABAnimated sharedAnimated].openLog = YES;控制视图初始化tabAnimated普通view: ...

May 10, 2019 · 2 min · jiezi

马甲包审核被拒对应的处理方案

写在前面马甲包审核被拒,目前网上流传最广的就是2.1和4.3,在查看了很多文章之后,从两篇介绍的比较详细的文章中把这两个大礼包对应条款的解决方案,整理出来,为开发马甲包做一个准备,同时分享个大家。 2.1大礼包2.1大礼包主要包含1.1.6、2.3、2.3.1、3.1.1、4.3,5.2.1,5.3.4等条款。 1.1.6 包含虚假信息,功能或误导性元数据一般是因为标题或者icon和截图等有误导的嫌疑,或有些关键词是被苹果列入黑名单的,例如红包包、话费等,但审核条款又没有明确指出。对于上述情况的解决办法是使用保守的文案或素材。2.3.0 含有不经审核也可更改App功能如改变App功能的热更新,这种情况需要把热更新去除,或者对热更新模块代码做深度混淆处理!2.3.1 含有隐藏功能或为记录的功能,包括定向到赌博或彩票网站的开关。常规解决方式:去除隐藏功能模块代码或将需要隐藏功能的代码及定向跳转链接网址做混淆处理,适当增加逻辑复杂度。3.1.1 应用内购以外的支付机制来解锁App中的功能对于第三方支付,尽可能避免使用易扫描的SDK版本,推荐使用H5版本支付。支付跳转链接相应的做屏蔽混淆处理。4.3.0 是另一款应用的复制品,或与另一款应用明显相似。被认为是重复App或马甲包,变更UI和名称,填充无用代码等。A、改名字;B、修改素材及UI色调等,例如修改icon,修改主色调;C、修改功能界面等,可改功能可做小开关;D、填充代码(++最好50%以上++)或注释块;5.2.1 未由拥有并负责提供该应用程序提供的任何服务的法律实体提交。未提供 App 上架所需的行业资质,比如:金融营业许可证、游戏版号等。这个上面讲过些常规方式。5.3.4 含有货币游戏(如:体育下注、赌场游戏等),但未提供相关许可资质。同上,提供资质,审核时最好不要勾选中国区,或使用海外账号。4.3 被拒的原因及处理方案第一种:代码重复(分为三种)1.可能你之前用这套源码上过一个包,现在用这套代码直接改一个logo跟名字再上一个马甲对应的处理方案: 第一步:工程中的文件夹的名字全部进行修改。第二步:每一个工程都有一个类前缀,我们需要取一个长一点的类前缀,并且这个类前缀在你的整个工程一定是一个唯一的字符串,我们假设这个类前缀是PayDayLoan,现在我们需要生成一个控制器,控制器的结尾Controller也需要用一个特定的字符去代替,比如:Director,剩下的View以及object做法类似,就不一一介绍了,做马甲的时候就是把这些名字用另一个唯一的字符去代替,尽量长一点。 第三步:把另一个其他的工程中的类全部导入进来, 主要是混淆代码, 在现有的工程中调用, 可以没有任何效果, 只是单纯调用方法。 敲黑板 单一的加入垃圾代码混淆是没用的!2.如果你的这套源码在一个账号上提交过,但是被拒了,后来因为其他原因你不得不在别的账号上重新提交此源码。对应的处理方案: 这种情况需要在第一个账号做一下处理,xcode新建一个应用,直接用之前提交过的bundleId打包,logo用一个纯白或者纯黑的图片,将这个新建的应用提交到应用市场,构建版本中将之前被拒的包移除,用这个新的应用顶替之前被拒的包,app名字改成“作废-此应用不再提交”后面再随便加一个数次,因为这个名字别人已经用过了,app描述跟app名字一样,剩下的信息全部删除,最后点击保存即可,不需要提交审核。3.你的源码只要提交到itunesconnect里面,就算没提交审核,当你再次使用此源码提交审核的时候.对应的处理方案,参照2的处理方案。第二种:界面功能相似这种情况简单的改源码已经没用了,需要在原有的app上加一些不同的功能。我用借贷类举例说明该如何解决,其他类型的app可以参考。以下举两个应用说明,分别用A应用与B应用代替,你需要如何处理并且如何回复审核人员。 A应用是给没有信用卡的用户使用的一款借款App,B应用是给有信用卡的用户使用的一款借款App。A应用的最高借款额度是1000元, B应用的最高借款额度是25000元。A应用的还款时间是7天与14天, B应用的还款时间是28天。A应用内部有贷款计算器功能, B应用只是一个普通的贷款app,并无其他功能。两款app是我们公司内不同的部门开发的app,分别针对不同的用户人群。 总结 针对界面功能相似的App,我们肯定是要做对应的处理。处理之后我们可以从以下几个方面进行回复:两个App的用户群体差异。两个App的功能差异。两个App同一个公司的两个部门针对不同的用户群体开发(和1.相似)。就是尽可能的找出两个App的不同,称述给苹果的审核人员。第三种:App名字被使用过这一种,可能是最容易被忽略的,取名字之前一定要先搜索appstore有没有同名应用,尽量避免同名应用。 代码混淆工具KLGenerateSpamCode 垃圾代码生成器 CodeMixer,代码混淆,图片改名,批量修改类名 参考文章iOS马甲包审核以及常见审核问题 骨灰级iOS工程师手把手教你如何上架马甲包!

May 10, 2019 · 1 min · jiezi

iOS马甲包上架招式

一、什么是马甲包马甲包是利用App store 规则漏洞,通过技术手段,多次上架同一款产品的方法。马甲包和主产品包拥有同样的内容和功能,除了icon和应用名称不能完全一致,其他基本一致。 二、为什么做马甲包,做马甲包有什么好处?1、导量、刷榜、增加关键字覆盖一个App的关键字是有限的,马甲包能增加我们的搜索关键词,增加我们的App被用户搜索和下载的几率。一个本身质量过硬的App,马甲包能够帮助我们迅速提升排名。 2、抗风险一些不确定效果的新功能,我们可以在马甲包上先做测试,效果OK之后,我们再迭代到主App上,这样即使新功能效果不佳也不会影响主App的流量。 三、马甲包的开发招式1、UI部分在原有的UI的基础上,修改新的UI。启动图修改,坚决不能和之前的一样。logo修改,坚决不能和之前的一样。2、代码部分修改工程中文件夹名字(全部需要修改)。修改项目名字。修改类名,前缀统一的进行统一替换,后缀名也可以根据情况进行修改(view/ViewController/model)。添加混淆代码,修改之前的方法名,往类中添加不相关的方法(此处建议使用 #pragma mark -(此处是马甲包的特殊标记)进行标记,方便后续修改)。修改boundID。在之前App的基础上,增加或者删除部分功能,把两个App之间的差异尽量最大化。四、上架招式上架马甲包,最好是准备一个新的账号,不要影响主App,防止账号被封或者处罚影响主App的正常下载。上架的时候项目描述不要和主App的一样。项目宣传也不要和主App的一样。提供给苹果的测试账号也提供新的。上传马甲包的电脑,不要和上传主App使用同一台电脑(据说会检测上传包的ip)。五、总结马甲包本身是不符合苹果的上架规范的,但是为了让更多的用户下载我们的App,提升我们App的排名,我们不得不想尽办法制作马甲包,顶风作案。开发马甲包我们主要从UI展现和代码实现尽量的把它们做的不像相同的App,但是它们的核心内容是相似的,用户流量最终流向同相同的服务器,实现导量和提升排名的功效。 我们在上架马甲包的时候还要尽量保证主App的安全,所以使用单独的账号上架马甲包,为了提高过审率,还要使用不同的电脑进行包的上传。项目描述&产品宣传等等都不能一样,就是尽量做成两个App,但是呢周期又要短。 最后,马甲包只是一个辅助,我们的App本身一定要有内容,这样才能够留住用户,否则就算用户下载了,很快也会卸载。导致“留住了用户的人,没有留住用户的心”,只留下了用户信息,不能为我们带来实质性的价值。 参考文章 iOS马甲包上架总结

April 29, 2019 · 1 min · jiezi

ObjectiveC的propertysynthesize和dynamic

objc推荐我们通过set/get方法访问对象的属性。很显然,为每一个属性手动添加set/get方法的声明和实现是个性价比很低的重复劳动。因此,objc提供了一些关键字帮助我们简化这一过程。实际上就是这么回事儿。 @property首先来看现在的property: 例如: @interface ViewController : UIViewController@property (nonatomic,assign) BOOL testVar;@end简单理解,相当于声明了成员变量_testVar,声明了实现了set方法setTestVar、get方法testVar,等价于: @interface ViewController : UIViewController{ BOOL _testVar;}- (void)setTestVar:(BOOL)newTestVar;- (BOOL)testVar;@end@implementation ViewController- (void)setTestVar:(BOOL)newTestVar{ _testVar = newTestVar;}- (BOOL)testVar{ return _testVar;}@end@synthesize@synthesize可以指定set/get方法的实现。 1. 默认默认地,@synthesize会生成一个同名的成员变量作为set/get的目标。 例如: @interface ViewController : UIViewController@property (nonatomic,assign) BOOL testVar;@end @implementation ViewController@synthesize testVar;@end等价于: @interface ViewController : UIViewController{ BOOL testVar;}- (void)setTestVar:(BOOL)newTestVar;- (BOOL)testVar;@end@implementation ViewController- (void)setTestVar:(BOOL)newTestVar{ testVar = newTestVar;}- (BOOL)testVar{ return testVar;}@end可以看到,set/get方法指向了成员变量testVar,也不会再默认生成_testVar。 注意,很多人误认为@synthesize默认生成的成员变量是_testVar,据我所知这是不对的。 官方文档: Important: If you use @synthesize without specifying an instance variable name, like this: ...

April 29, 2019 · 1 min · jiezi

2019年iOS常问的基础面试题都会了吧

常问基础面试题:1、return一个类返回的属性,会不会被释放2、单例可不可以被销毁3、NSObject的结构体构造4、runloop有几个run方法,分别适用于什么场景5、runloop的生命周期6、NSObject的load方法是否了解7、Selcetor如何找到其要执行的方法8、什么情况下会造成死锁9、锁的类型10、多线程传值如何做11、多线程的生命周期12、如何让一个线程常驻13、对NSOpretion和GCD的理解14、atomic是绝对线程安全的么15、如何保证线程安全,有哪几种方式16、说说对autoreleasepool的理解以及应用17、定时器的使用方法有哪些,更加精准的定时器应该怎么做18、performselect在哪个线程执行19、oclint是否有用过,testflight自动化测试工具,自动打包工具是否用过20、对http的理解,对socket的理解,对tcp、udp的理解21、加密方式有哪些22、https为啥安全23、对mvvm的理解24、swizzling的理解25、数据结构的理解和常用算法的使用:如:链表反转,快速排序,二叉树遍历,二分查找,以及一些类似的简单算法26、swift如何使用runtime27、autoreleasepool嵌套后发生的一些执行顺序28、fmdb是同步还是异步数据库29、userdefault如何保证快速存取30、category实现原理31、对动画的使用,是否用过coreanimation32、oc与swift的差异化33、对设计模式的深入理解以及阐述推荐文集* iOS面试题大全(附答案)* BAT—最新iOS面试题总结

April 26, 2019 · 1 min · jiezi

iOS开发-图片的解压缩到渲染过程

一.图像从文件到屏幕过程 通常计算机在显示是CPU与GPU协同合作完成一次渲染.接下来我们了解一下CPU/GPU等在这样一次渲染过程中,具体的分工是什么? CPU: 计算视图frame,图片解码,需要绘制纹理图片通过数据总线交给GPUGPU: 纹理混合,顶点变换与计算,像素点的填充计算,渲染到帧缓冲区。时钟信号:垂直同步信号V-Sync / 水平同步信号H-Sync。iOS设备双缓冲机制:显示系统通常会引入两个帧缓冲区,双缓冲机制图片显示到屏幕上是CPU与GPU的协作完成 对应应用来说,图片是最占用手机内存的资源,将一张图片从磁盘中加载出来,并最终显示到屏幕上,中间其实经过了一系列复杂的处理过程。 二.图片加载的工作流程假设我们使用 +imageWithContentsOfFile: 方法从磁盘中加载一张图片,这个时候的图片并没有解压缩;然后将生成的 UIImage 赋值给 UIImageView ;接着一个隐式的 CATransaction 捕获到了 UIImageView 图层树的变化;在主线程的下一个 runloop 到来时,Core Animation 提交了这个隐式的 transaction ,这个过程可能会对图片进行 copy 操作,而受图片是否字节对齐等因素的影响,这个 copy 操作可能会涉及以下部分或全部步骤: 分配内存缓冲区用于管理文件 IO 和解压缩操作;将文件数据从磁盘读到内存中;将压缩的图片数据解码成未压缩的位图形式,这是一个非常耗时的 CPU 操作;最后 Core Animation 中CALayer使用未压缩的位图数据渲染 UIImageView 的图层。CPU计算好图片的Frame,对图片解压之后.就会交给GPU来做图片渲染渲染流程 GPU获取获取图片的坐标将坐标交给顶点着色器(顶点计算)将图片光栅化(获取图片对应屏幕上的像素点)片元着色器计算(计算每个像素点的最终显示的颜色值)从帧缓存区中渲染到屏幕上我们提到了图片的解压缩是一个非常耗时的 CPU 操作,并且它默认是在主线程中执行的。那么当需要加载的图片比较多时,就会对我们应用的响应性造成严重的影响,尤其是在快速滑动的列表上,这个问题会表现得更加突出。 三.为什么要解压缩图片既然图片的解压缩需要消耗大量的 CPU 时间,那么我们为什么还要对图片进行解压缩呢?是否可以不经过解压缩,而直接将图片显示到屏幕上呢?答案是否定的。要想弄明白这个问题,我们首先需要知道什么是位图 其实,位图就是一个像素数组,数组中的每个像素就代表着图片中的一个点。我们在应用中经常用到的 JPEG 和 PNG 图片就是位图 大家可以尝试 UIImage *image = [UIImage imageNamed:@"text.png"];CFDataRef rawData = CGDataProviderCopyData(CGImageGetDataProvider(image.CGImage));打印rawData,这里就是图片的原始数据. 事实上,不管是 JPEG 还是 PNG 图片,都是一种压缩的位图图形格式。只不过 PNG 图片是无损压缩,并且支持 alpha 通道,而 JPEG 图片则是有损压缩,可以指定 0-100% 的压缩比。值得一提的是,在苹果的 SDK 中专门提供了两个函数用来生成 PNG 和 JPEG 图片: ...

April 25, 2019 · 2 min · jiezi

iOS开发-简历中需要特别注意的 3 个点

本次要说的内容,是关于简历的,网上其实流传了很多的简历模版,但是大部分都没有说这些模版到底好在哪,所以很多人就算拿过来用了,最后也是貌似神离。 今天我不讲形式,只说内容,简历中请一定要注意的 3 个问题: 项目描述请尽量简短;自我评价尽量不要用形容词;项目经验请使用倒序;下面分别做下详细说明。1、项目描述请尽量简短。我们先看一个例子: 项目描述:交易资金托管服务是指银行为货物、服务交易或存在资金托管需求的双方(或多方)提供信用中介,一方先将资金存入银行并暂时冻结,待另一方提供了双方约定的货物或服务、或满足了双方约定的其他事项,银行按照协议约定协助完成资金的划转;若双方未达成交易或未实现双方约定的其他事项,银行按协议约定退回交易资金。若交易双方需要银行移交权证,银行则根据协议约定协助完成相关权益证明的交换。如果你看到简历里面这段描述,第一印象是什么?给我的第一感觉就是,这个人对自己太不负责了,项目描述都不是自己的总结,一看就是拷贝粘贴而来。 其次就是这个人可能在问题优先级上的关注不够,比如这么长的项目描述,写出来是希望告诉面试官什么信息呢?说明你的项目很庞大?很出色?然后呢?和写这份简历的你有什么关系? 既然是我们自己的简历,我觉得所有地方的描述,肯定都是为了突出自己的能力、贡献、态度等等,任何不能体现自己这些长处的内容都需要精简,甚至删除。 看看这个项目描述改成这样如何: 项目描述:该系统是一个资金托管服务平台。系统共有十大模块,我带领三个小伙伴负责其中五个主要模块的功能和性能测试,项目发布后没有严重问题反馈。怎么样?看完了什么感觉?是不是一目了然?知道了项目大概功能的同时,也明白了候选人在项目中的角色。2、自我评价尽量不要用形容词。还是先看一个自我评价的例子: 本人对待工作认真,善于沟通,勤于学习能不断提升自身的能力和综合素质;性格开朗,可以快速融入团队,有较强的团队精神;对工作充满热情,适应能力强,创新务实;在工作中用心进取,态度认真,不怕吃苦。如果你看到这个评价,是什么感觉?给我的感觉就是空洞,或者说是说了一堆的废话,我甚至都不会逐字的看完。 形容词谁都会用,但问题是,怎么证明他的可信度呢?不能自己说自己牛掰,然后就真的牛掰了吧? 十全十美是人人都希望达到的境界,但这并不现实,所以与其让自己呈现的完美,倒不如让自己呈现的真实,真实就是在实际的做人做事中具体体现的行事方式。 比如把上面的例子我们改成下面的描述: 有一定的学习能力,曾经紧急接手一个新项目,项目使用的是 Swift 语言,但是我之前都是学习的 OC,所以我花了一个星期的时间进行突击,很快就掌握了 Swift 的基本语法的使用,可以看懂业务中的简单代码实现了;懂得作为测试的坚持,之前有个项目在上线前还有一个 P2 的 bug 没有修复,产品打算带着 bug 上线,可是我看了 bug 后发现,确认它的严重程度和优先级都属于 P2 级别的,于是找开发和产品沟通协商,最终决定还是修复完 bug 再上线。怎么样?是不是很吸引人,没有用xxx的形容词,但是看的人自己就会得出一个结论「这个人很靠谱」,看,这不就是我们要达到的效果吗?与其假惺惺的告诉别人你的各种优势,倒不如好好找个例子让别人看到你做事的态度。 3、项目经验请使用倒序简要罗列。这个就不放例子了,反例都太长了,大概说下情况。 如果参与过的项目非常多,完全没必要每一个都进行罗列,就算写了三页纸一点都改变不了自己分不清轻重的事实。 如果是按所在公司进行项目罗列的话,每个公司可以找一个代表性项目就可以了。 什么是代表性项目?不是项目规模大就好,而是自己的参与度和价值体现最大的项目,记住,自己才是这个简历的主人,项目都是为了衬托自己的。 如果不用公司的时间轴,完全按照项目时间轴罗列的话,同样只需要挑选代表性项目就可以了,哪怕全都是某一个公司的也没有关系,因为你在工作经历中已经提到公司的时间轴了。 如果面试官对其他公司的项目感兴趣,他会主动问起来,这时候再答复就可以,同时,面试官筛选简历时,不会因为项目罗列的不够全而淘汰掉简历。 或者这么说,面试官不会因为简历写的太差而淘汰谁,只会因为简历中没有看到自己需要的能力而被淘汰,所以只管突出的展示自己的能力即可,毕竟简历只是为了获得面试的机会而已,只要获得了面试机会,简历的使命就算完成了。 最后说下时间轴顺序,请使用倒序,请使用倒序,请使用倒序,重要的事要说三遍。 倒序就是把离当前时间点最近的项目放在前面。 一个人带着工作经验去面试,那么他的经验是他的底牌,只有尽早尽快的在经验的展现上吸引住面试官,才更有可能获得面试机会,而对于工作经验,大家当然是更关注最近的实践经验了,况且对于个人来说,肯定越是最近的项目,个人能力的体现也应该是最好的,不然怎么体现出自己的进步呢? 以上,稍微有点啰嗦了,关于简历不知道大家是否还有其他的疑惑或者问题,欢迎进入iOS技术交流群:624212887,一起探讨,更有企业内推机会! 另外,如果看了文章还是不知道简历怎么改的,推荐一个不错的简历指导视频,值得一看;观看地址:第二十七讲:简历指导视频

April 22, 2019 · 1 min · jiezi

iOS常问面试题:三次握手与四次挥手

在面试中,三次握手和四次挥手可以说是问的最频繁的一个知识点了,我相信大家也都看过很多关于三次握手与四次挥手的文章,今天的这篇文章,重点是围绕着面试,我们应该掌握哪些比较重要的点,哪些是比较被面试官给问到的,我觉得如果你能把我下面列举的一些点都记住、理解,我想就差不多了。三次握手当面试官问你为什么需要有三次握手、三次握手的作用、讲讲三次三次握手的时候,我想很多人会这样回答:首先很多人会先讲下握手的过程:第一次握手: 客户端给服务器发送一个 SYN 报文。第二次握手: 服务器收到 SYN 报文之后,会应答一个 SYN+ACK 报文。第三次握手: 客户端收到 SYN+ACK 报文之后,会回应一个 ACK 报文。服务器收到 ACK 报文之后,三次握手建立完成。作用是为了确认双方的接收与发送能力是否正常。这里我顺便解释一下为啥只有三次握手才能确认双方的接受与发送能力是否正常,而两次却不可以:第一次握手: 客户端发送网络包,服务端收到了。这样服务端就能得出结论:客户端的发送能力、服务端的接收能力是正常的。第二次握手: 服务端发包,客户端收到了。这样客户端就能得出结论:服务端的接收、发送能力,客户端的接收、发送能力是正常的。不过此时服务器并不能确认客户端的接收能力是否正常。第三次握手: 客户端发包,服务端收到了。这样服务端就能得出结论:客户端的接收、发送能力正常,服务器自己的发送、接收能力也正常。因此,需要三次握手才能确认双方的接收与发送能力是否正常。这样回答其实也是可以的,但我觉得,这个过程的我们应该要描述的更详细一点,因为三次握手的过程中,双方是由很多状态的改变的,而这些状态,也是面试官可能会问的点。所以我觉得在回答三次握手的时候,我们应该要描述的详细一点,而且描述的详细一点意味着可以扯久一点。加分的描述我觉得应该是这样:刚开始客户端处于 closed 的状态,服务端处于 listen 状态。然后第一次握手: 客户端给服务端发一个 SYN 报文,并指明客户端的初始化序列号ISN(c)。此时客户端处于 SYN_Send 状态。第二次握手: 服务器收到客户端的 SYN 报文之后,会以自己的 SYN 报文作为应答,并且也是指定了自己的初始化序列号 ISN(s),同时会把客户端的 ISN + 1 作为 ACK 的值,表示自己已经收到了客户端的 SYN,此时服务器处于 SYN_REVD 的状态。第三次握手: 客户端收到 SYN 报文之后,会发送一个 ACK 报文,当然,也是一样把服务器的 ISN + 1 作为 ACK 的值,表示已经收到了服务端的 SYN 报文,此时客户端处于 establised 状态。服务器收到 ACK 报文之后,也处于 establised 状态,此时,双方以建立起了链接。三次握手的作用三次握手的作用也是有好多的,多记住几个,保证不亏。例如:确认双方的接受能力、发送能力是否正常。指定自己的初始化序列号,为后面的可靠传送做准备。如果是 https 协议的话,三次握手这个过程,还会进行数字证书的验证以及加密密钥的生成到。单单这样还不足以应付三次握手,面试官可能还会问一些其他的问题,例如:1、(ISN)是固定的吗三次握手的一个重要功能是客户端和服务端交换ISN(Initial Sequence Number), 以便让对方知道接下来接收数据的时候如何按序列号组装数据。如果ISN是固定的,攻击者很容易猜出后续的确认号,因此 ISN 是动态生成的。2、什么是半连接队列服务器第一次收到客户端的 SYN 之后,就会处于 SYN_RCVD 状态,此时双方还没有完全建立其连接,服务器会把此种状态下请求连接放在一个队列里,我们把这种队列称之为半连接队列。当然还有一个全连接队列,就是已经完成三次握手,建立起连接的就会放在全连接队列中。如果队列满了就有可能会出现丢包现象。这里在补充一点关于SYN-ACK 重传次数的问题:服务器发送完SYN-ACK包,如果未收到客户确认包,服务器进行首次重传,等待一段时间仍未收到客户确认包,进行第二次重传,如果重传次数超 过系统规定的最大重传次数,系统将该连接信息从半连接队列中删除。注意,每次重传等待的时间不一定相同,一般会是指数增长,例如间隔时间为 1s, 2s, 4s, 8s, ….3、三次握手过程中可以携带数据吗很多人可能会认为三次握手都不能携带数据,其实第三次握手的时候,是可以携带数据的。也就是说,第一次、第二次握手不可以携带数据,而第三次握手是可以携带数据的。为什么这样呢?大家可以想一个问题,假如第一次握手可以携带数据的话,如果有人要恶意攻击服务器,那他每次都在第一次握手中的 SYN 报文中放入大量的数据,因为攻击者根本就不理服务器的接收、发送能力是否正常,然后疯狂着重复发 SYN 报文的话,这会让服务器花费很多时间、内存空间来接收这些报文。也就是说,第一次握手可以放数据的话,其中一个简单的原因就是会让服务器更加容易受到攻击了。而对于第三次的话,此时客户端已经处于 established 状态,也就是说,对于客户端来说,他已经建立起连接了,并且也已经知道服务器的接收、发送能力是正常的了,所以能携带数据页没啥毛病。关于三次握手的,https 的认证过程能知道一下最好,不过我就不说了,留着写 http 面试相关时的文章再说。四次挥手四次挥手也一样,千万不要对方一个 FIN 报文,我方一个 ACK 报文,再我方一个 FIN 报文,我方一个 ACK 报文。然后结束,最好是说的详细一点,例如想下面这样就差不多了,要把每个阶段的状态记好,我上次面试就被问了几个了,呵呵。我答错了,还以为自己答对了,当时还解释的头头是道,呵呵。刚开始双方都处于 establised 状态,假如是客户端先发起关闭请求,则:第一次挥手: 客户端发送一个 FIN 报文,报文中会指定一个序列号。此时客户端处于CLOSED_WAIT1状态。第二次握手: 服务端收到 FIN 之后,会发送 ACK 报文,且把客户端的序列号值 + 1 作为 ACK 报文的序列号值,表明已经收到客户端的报文了,此时服务端处于CLOSE_WAIT2状态。第三次挥手: 如果服务端也想断开连接了,和客户端的第一次挥手一样,发给 FIN 报文,且指定一个序列号。此时服务端处于 LAST_ACK 的状态。第四次挥手: 客户端收到 FIN 之后,一样发送一个 ACK 报文作为应答,且把服务端的序列号值 + 1 作为自己 ACK 报文的序列号值,此时客户端处于 TIME_WAIT 状态。需要过一阵子以确保服务端收到自己的 ACK 报文之后才会进入 CLOSED 状态服务端收到 ACK 报文之后,就处于关闭连接了,处于 CLOSED 状态。这里特别需要主要的就是TIME_WAIT这个状态了,这个是面试的高频考点,就是要理解,为什么客户端发送 ACK 之后不直接关闭,而是要等一阵子才关闭。这其中的原因就是,要确保服务器是否已经收到了我们的 ACK 报文,如果没有收到的话,服务器会重新发 FIN 报文给客户端,客户端再次收到 FIN 报文之后,就知道之前的 ACK 报文丢失了,然后再次发送 ACK 报文。至于 TIME_WAIT 持续的时间至少是一个报文的来回时间。一般会设置一个计时,如果过了这个计时没有再次收到 FIN 报文,则代表对方成功就是 ACK 报文,此时处于 CLOSED 状态。这里我给出每个状态所包含的含义,有兴趣的可以看看。LISTEN - 侦听来自远方TCP端口的连接请求;SYN-SENT -在发送连接请求后等待匹配的连接请求;SYN-RECEIVED - 在收到和发送一个连接请求后等待对连接请求的确认;ESTABLISHED - 代表一个打开的连接,数据可以传送给用户;FIN-WAIT-1 - 等待远程TCP的连接中断请求,或先前的连接中断请求的确认;FIN-WAIT-2 - 从远程TCP等待连接中断请求;CLOSE-WAIT - 等待从本地用户发来的连接中断请求;CLOSING -等待远程TCP对连接中断的确认;LAST-ACK - 等待原来发向远程TCP的连接中断请求的确认;TIME-WAIT -等待足够的时间以确保远程TCP接收到连接中断请求的确认;CLOSED - 没有任何连接状态;最后,在放在三次握手与四次挥手的图另外附上一份收集的各大厂面试题(附答案) ! 要的可加iOS高级技术群:624212887,群文件直接获取 ...

April 16, 2019 · 1 min · jiezi

为什么很多人吐槽谭浩强的C语言程序设计?

这个观点在网络上已经论战了很长时间,出现这种情况还是历史遗留问题,老谭出的C语言教材几乎是国内的第一版,由于中英翻译问题或者对编程文化理解的差异性导致出来的书多多少少存在一些纰漏。不能只是记住其缺点,老谭书最主要在那个年代起到了普及C语言的作用,这已经足够了,如同做一个产品,首先解决的是有没有问题,最后才是细节完善阶段。最后,如果大家如果在自学遇到困难,想找一个C++的学习环境,可以加入我们的C++学习圈,点击我加入吧,会节约很多时间,减少很多在学习中遇到的难题。为什么很多人吐槽谭浩强的C语言程序设计?如同历史上发生了许多在今天看来很搞笑的事情,社会在进步每个阶段必然有其特殊的产物,现在的C语言教材各种特点的都有,还有直接从外国翻译的经典书籍,同时也把国内很多的C语言学习人的口味给调动起来了,更可恨的是很多老程序员是看了老谭的教材学出来的,回头又对这个教材产生不满,吃水不忘挖井人,在编程书籍匮乏的年代,能有一本书解决大家的燃眉之急,可以没有感激之情,起码不能落井下石。其实这本书典型的中国特色思想的产物,开始上来出来了很多理论,很多人直接被这些理论给绕晕了,但是一旦跨越过去后面显得简单了许多,典型中国式的先苦后甜式的教程。为什么很多人吐槽谭浩强的C语言程序设计?为什么这么多人吐槽老谭这本书?1.这本书本身存在很多语法错误,这种错误主要是理解或者版本更迭没有及时更新造成,中国大学里面很多教材更迭相对偏慢。2.现在有很多种无论从内容质量还是排版形式都相比老谭的高明不少,没有对比就没有伤害,由于更新比较慢差距还是相当明显的,很多人还不解的问,都这么陈旧了为啥还拿出来作为教材使用,其实很多槽点都来源于此。3.大学里面很多设施特别是实验室里的C语言的编译环境还有很多是turbo c很多学生看到网上的评论顿时觉得low了许多,计算机等级考试的题目也还是老谭老版本书籍出的,里面的确存在很多和现在语法相悖的地方,在现在信息传递如此发达的今天,吐槽的力度以及影响程度都是空前的。为什么很多人吐槽谭浩强的C语言程序设计?无论怎么吐槽,一个人编程水平的高低和所在大学,使用的什么教材都没有太直接的关系,很多人在网上买编程教材一定想着买个最新版本的,其实完全没有必要,这些都是客观因素,主观因素才是关键,真要想学的好,什么教材都玩的转,取决于内在的意识形态,驱动力不够有再好的教材也是浪费资源,无论怎么说老谭对于中国计算机行业的贡献还是非常突出的,感激的心情更多点。现在还有很多的论调包含的大致意思就是C语言现在已经不行了,完全没有必要再去学习了,看看招聘职位关于C语言的真是少的可怜,现在明显的都在向高级语言进发了,大趋势是向集成化程度高的语言前进,但是集成化语言本身很多都是C语言完成的,未来可能C语言学习的会变少,但是职位要求以及薪资水平会高的让普通程序员都只有羡慕的份,物以稀为贵,这也是市场规模。踏踏实实学好正在学习的编程语言,把精力放在语言本身,而不是去争论谁好谁坏,即使分出个高低了对于个人又有什么直接好处,认准一种编程语言一直学到最后,然后想办法过度到别的语言,触类旁通。

April 16, 2019 · 1 min · jiezi

iOS面试题:反射是什么?可以举出几个应用场景么?

系统Foundation框架为我们提供了一些方法反射的API,我们可以通过这些API执行将字符串转为SEL等操作。由于OC语言的动态性,这些操作都是发生在运行时的。// SEL和字符串转换FOUNDATION_EXPORT NSString *NSStringFromSelector(SEL aSelector);FOUNDATION_EXPORT SEL NSSelectorFromString(NSString *aSelectorName);// Class和字符串转换FOUNDATION_EXPORT NSString *NSStringFromClass(Class aClass);FOUNDATION_EXPORT Class __nullable NSClassFromString(NSString *aClassName);// Protocol和字符串转换FOUNDATION_EXPORT NSString *NSStringFromProtocol(Protocol *proto) NS_AVAILABLE(10_5, 2_0);FOUNDATION_EXPORT Protocol * __nullable NSProtocolFromString(NSString *namestr) NS_AVAILABLE(10_5, 2_0);通过这些方法,我们可以在运行时选择创建那个实例,并动态选择调用哪个方法。这些操作甚至可以由服务器传回来的参数来控制,我们可以将服务器传回来的类名和方法名,实例为我们的对象。// 假设从服务器获取JSON串,通过这个JSON串获取需要创建的类为ViewController,并且调用这个类的getDataList方法。Class class = NSClassFromString(@“ViewController”);ViewController *vc = [[class alloc] init];SEL selector = NSSelectorFromString(@“getDataList”);[vc performSelector:selector];反射机制使用技巧假设有一天公司产品要实现一个需求:根据后台推送过来的数据,进行动态页面跳转,跳转到页面后根据返回到数据执行对应的操作。遇到这样奇葩的需求,我们当然可以问产品都有哪些情况执行哪些方法,然后写一大堆if else判断或switch判断。但是这种方法实现起来太low了,而且不够灵活,假设后续版本需求变了,还要往其他已有页面中跳转,这不就傻眼了吗….这种情况反射机制就派上用场了,我们可以用反射机制动态的创建类并执行方法。当然也可以通过runtime来实现这个功能,但是我们当前需求反射机制已经足够满足需求了,如果遇到更加复杂的需求可以考虑用runtime来实现。这时候就需要和后台配合了,我们首先需要和后台商量好返回的数据结构,以及数据格式、类型等,返回后我们按照和后台约定的格式,根据后台返回的信息,直接进行反射和调用即可。假设和后台约定格式如下:@{ // 类名 @“className” : @“UserListViewController”, // 数据参数 @“propertys” : @{ @“name”: @“liuxiaozhuang”, @“age”: @3 }, // 调用方法名 @“method” : @“refreshUserInformation” };定义一个UserListViewController类,这个类用于测试,在实际使用中可能会有多个这样的控制器类。#import <UIKit>// 由于使用的KVC赋值,如果不想把这两个属性暴露出来,把这两个属性写在.m文件也可以@interface UserListViewController : UIViewController@property (nonatomic,strong) NSString name;/!< 用户名 /@property (nonatomic,strong) NSNumber age;/!< 用户年龄 // 使用反射机制反射为SEL后,调用的方法 */- (void)refreshUserInformation;@end下面通过反射机制简单实现了控制器跳转的方法,在实际使用中再根据业务需求进行修改即可。因为这篇文章主要是讲反射机制,所以没有使用runtime代码。简单封装的页面跳转方法,只是做演示,代码都是没问题的,使用时可以根据业务需求进行修改。- (void)remoteNotificationDictionary:(NSDictionary *)dict { // 根据字典字段反射出我们想要的类,并初始化控制器 Class class = NSClassFromString(dict[@“className”]); UIViewController *vc = [[class alloc] init]; // 获取参数列表,使用枚举的方式,对控制器属性进行KVC赋值 NSDictionary *parameter = dict[@“propertys”]; [parameter enumerateKeysAndObjectsUsingBlock:^(id _Nonnull key, id _Nonnull obj, BOOL * _Nonnull stop) { // 在属性赋值时,做容错处理,防止因为后台数据导致的异常 if ([vc respondsToSelector:NSSelectorFromString(key)]) { [vc setValue:obj forKey:key]; } }]; [self.navigationController pushViewController:vc animated:YES]; // 从字典中获取方法名,并调用对应的方法 SEL selector = NSSelectorFromString(dict[@“method”]); [vc performSelector:selector];}更多:iOS面试题大全(附答案) ...

April 15, 2019 · 1 min · jiezi

让UINavigationController更好用

去年看到过美团点评技术团队的一篇文章iOS系统中导航栏的转场解决方案与最佳实践,文章对系统导航栏的改造很有意思,最近就试着写点代码练练手。项目地址:DoubleNavigationController这个库还没有在实际项目中检验过,还有很多不完善或者不能满足业务需求的地方,欢迎提issue或者PR。些许疑问为什么要开发这个库?UINavigationController在苹果官方文档上的是这样介绍的:A navigation controller is a container view controller that manages one or more child view controllers in a navigation interface.也就是说UINavigationController 是作为UIViewController的管理者,因此它的NavigationBar不应该从属于任何一个ViewController。但是大部分UI设计者都没有明白苹果设计的用意,因此在业务中经常出现一个场景:在逻辑上应该从属于同一个NavigationController的多个ViewController却拥有不同的NavigationBar样式。不同页面的开发者只能看到对自己开发的便利性,对UINavigationController的理解不到位,到处修改NavigationBar样式,在页面专场过程中NavigationBar出现了各种不可控的问题。这个问题在App支持路由后会变得更为突出,原因是各个页面的跳转关系将会非常复杂且不可预知。DoubleNavigationController解决的便是这个问题,让开发者自由地修改NavigationBar样式,并且不用担心在退回到栈下ViewController后NavigationBar的样式也被修改。简单来说就是,我们修改NavigationBar不再会影响栈内现有的页面样式,而只影响之后Push的新页面。为什么不设计成既不影响栈内现有的页面样式,也不影响新页面?在这里先讲一下DoubleNavigationController的两个设计思想 “先到先得”、“谁用谁修改”。“先到先得”先出现的页面样式不应该受到后出现的页面影响。用户在使用过程中先看到了页面A的样式,接着从A页面跳转到B页面,B页面的导航栏样式与A页面不同,这时用户再返回A页面,从正常逻辑上来说,用户希望看到的A页面导航栏应该还是之前见过的样式,不应该受到B的影响而改变。“谁用谁修改”继续上面一个场景,用户从页面A到页面B,再从页面B跳转到页面C,在上一个场景下我们知道,页面B修改了导航栏样式,使其与页面A不同,当我们跳转到页面C时,此时存在如下两种可能:页面C也对刚才B页面修改过的导航栏样式属性进行了修改。页面C没有对刚才B页面修改过的导航栏样式属性进行修改。在第1种情况下我们很容易确定,C页面的导航栏样式就应该是C页面自己修改的样式。那么在第2种情况下,C页面导航栏应该长什么样?再考虑以下3种方案:跟A页面一样?跟UIAppearance配置一样?跟B页面一样?C页面导航栏跟A页面一样?这个方案在逻辑上就是错误的,因为C页面根本不应该关心它的上上一个页面样式。如下图,假设B页面有两条跳转路径A1和A2,此时C页面的样式就有2种可能,相信绝大多数App的设计都不会出现 “1个页面,2种UI” 的情况吧。C页面导航栏跟UIAppearance配置一样?让C页面保持和UIAppearance配置一致,这里也存在两个问题,一个问题是如果用户没有配置UIAppearance怎么办?还有一个更大的问题是,这么做似乎破坏了苹果对于UINavigationController的定义,这就使得导航栏在逻辑上成为了单个页面所独立持有的个体,在这种情况下倒不如隐藏系统NavigationBar,每个页面是实现一个自己的导航栏来个更方便维护。C页面导航栏跟B页面一样?保持和B页面一样,粗略一想,这和“跟A页面一样?”方案似乎是差不多的,但实际上这两种方案有着本质区别。“跟B页面一样”换一个更好的说法应该是“跟最近一次用户对导航栏修改之后的样式一样”,也就是说C页面只需要关注导航栏本身,而不需要关注谁修改了导航栏,这样一来就满足来上述的设计思想 “谁用谁修改”。实现关于这个库的实现,笔者在这里参考了美团点评的这篇文章iOS系统中导航栏的转场解决方案与最佳实践。在转场的过程中隐藏原有的导航栏并添加假的 NavigationBar,当转场结束后删除假的 NavigationBar 并恢复原有的导航栏,这一过程可以通过 Swizzle 的方式完成,而每个 ViewController 只需要关心自身的样式即可。DoubleNavigationController核心的解决方案与这篇文章提到的是一样的,但是在实现方式和细节上可能与文章中提到的并不一样,另外有一些实现细节在美团点评的这篇文章中并没有过多地透露。几个细节细节1:DoubleNavigationController中选择直接NSKeyedArchiver来复制一个FakeNavigationBar而并没有自定义UIView。细节2:有些时候一个页面的NavigationBar可能会在用户交互过程中动态变化,因此我们需要记录每一次用户对NavigationBar外观的修改,并在适当的时候对FakeNavigationBar外观也进行更新。细节3:由于UIAppearance的原理是在UIView被添加到视图树后才会去改变对象的外观,因此在使用FakeNavigationBar之前需要再一次和当前的navigationBar进行一次UIAppearance属性的复制。参考:iOS UIAppearance 探秘 — HyanCat’s例子clone这个仓库,进到Example目录下执行pod install来运行一个demo。用法通过在ViewController中实现dbn_configNavigationController这个方法来定制导航栏样式。- (void)dbn_configNavigationController:(UINavigationController *)navigationController { [navigationController setNavigationBarHidden:NO animated:NO]; navigationController.navigationBar.barTintColor = [UIColor whiteColor]; navigationController.navigationBar.tintColor = [UIColor purpleColor]; navigationController.navigationBar.titleTextAttributes = @{NSFontAttributeName: [UIFont systemFontOfSize:20], NSForegroundColorAttributeName: [UIColor redColor]};}- (void)dbn_configNavigationItem:(UINavigationItem *)navigationItem { UIBarButtonItem *btnItem = [[UIBarButtonItem alloc] initWithTitle:@“Next” style:UIBarButtonItemStylePlain target:self action:@selector(eventFromButton:)]; navigationItem.rightBarButtonItem = btnItem; navigationItem.title = @“Hello”;}你还可以使用dbn_performBatchUpdates:这个方法来随时更新导航栏样式。[self dbn_performBatchUpdates:^(UINavigationController * _Nullable navigationController) { if (navigationController) { navigationController.navigationBar.tintColor = [UIColor purpleColor]; }}];参考iOS系统中导航栏的转场解决方案与最佳实践UIKit UIAppearance - APPLEiOS UIAppearance 探秘 — HyanCat’s ...

April 12, 2019 · 1 min · jiezi

iOS面试题:什么是离屏渲染?什么情况下会触发?该如何应对?

更多:iOS面试题大全离屏渲染就是在当前屏幕缓冲区以外,新开辟一个缓冲区进行操作。离屏渲染出发的场景有以下:圆角 (maskToBounds并用才会触发)图层蒙版阴影光栅化为什么要有离屏渲染?大家高中物理应该学过显示器是如何显示图像的:需要显示的图像经过CRT电子枪以极快的速度一行一行的扫描,扫描出来就呈现了一帧画面,随后电子枪又会回到初始位置循环扫描,形成了我们看到的图片或视频。为了让显示器的显示跟视频控制器同步,当电子枪新扫描一行的时候,准备扫描的时发送一个水平同步信号(HSync信号),显示器的刷新频率就是HSync信号产生的频率。然后CPU计算好frame等属性,将计算好的内容交给GPU去渲染,GPU渲染好之后就会放入帧缓冲区。然后视频控制器会按照HSync信号逐行读取帧缓冲区的数据,经过可能的数模转换传递给显示器,就显示出来了。具体的大家自行查找资料或询问相关专业人士,这里只参考网上资料做一个简单的描述。离屏渲染的代价很高,想要进行离屏渲染,首选要创建一个新的缓冲区,屏幕渲染会有一个上下文环境的一个概念,离屏渲染的整个过程需要切换上下文环境,先从当前屏幕切换到离屏,等结束后,又要将上下文环境切换回来。这也是为什么会消耗性能的原因了。由于垂直同步的机制,如果在一个 HSync 时间内,CPU 或者 GPU 没有完成内容提交,则那一帧就会被丢弃,等待下一次机会再显示,而这时显示屏会保留之前的内容不变。这就是界面卡顿的原因。[](https://github.com/liberalism…?CPU GPU 在绘制渲染视图时做了大量的工作。离屏渲染发生在 GPU 层面上,会创建新的渲染缓冲区,会触发 OpenGL 的多通道渲染管线,图形上下文的切换会造成额外的开销,增加 GPU 工作量。如果 CPU GPU 累计耗时 16.67 毫秒还没有完成,就会造成卡顿掉帧。圆角属性、蒙层遮罩 都会触发离屏渲染。指定了以上属性,标记了它在新的图形上下文中,在未愈合之前,不可以用于显示的时候就出发了离屏渲染。在OpenGL中,GPU有2种渲染方式On-Screen Rendering:当前屏幕渲染,在当前用于显示的屏幕缓冲区进行渲染操作Off-Screen Rendering:离屏渲染,在当前屏幕缓冲区以外新开辟一个缓冲区进行渲染操作离屏渲染消耗性能的原因需要创建新的缓冲区离屏渲染的整个过程,需要多次切换上下文环境,先是从当前屏幕(On-Screen)切换到离屏(Off-Screen);等到离屏渲染结束以后,将离屏缓冲区的渲染结果显示到屏幕上,又需要将上下文环境从离屏切换到当前屏幕哪些操作会触发离屏渲染?光栅化,layer.shouldRasterize = YES遮罩,layer.mask圆角,同时设置 layer.masksToBounds = YES、layer.cornerRadius大于0考虑通过 CoreGraphics 绘制裁剪圆角,或者叫美工提供圆角图片阴影,layer.shadowXXX,如果设置了 layer.shadowPath 就不会产生离屏渲染

April 12, 2019 · 1 min · jiezi

iOS不使用微信sdk,直接打开小程序

直接贴代码iOS审核不让有支付代码,所以只使用轻度功能的话,可以不使用微信SDK。使用前需要先去微信开放平台绑定。我的封装/** * 开发前需要到微信开放平台把App绑定小程序,然后在小程序的管理员微信上点击同意绑定,就可以转跳了 * 字段解释: * @appid:小程序appid * @username:‘gh’开头的小程序公用id * @path:小程序需要打开页面的路径 * @type:0是正式版,1是开发版,2是体验版 **/-(void)jumpToWechatMiniProgram:(NSString *)appid ghId:(NSString *)username path:(NSString *)path type:(NSString *)miniProgramtype{ NSString *mPath = [path stringByReplacingOccurrencesOfString:@"/" withString:@"%2F"]; NSString *url = [NSString stringWithFormat:@“weixin://app/%@/jumpWxa/?userName=%@&path=%@&miniProgramType=%@&extMsg=",appid,username,mPath,miniProgramtype]; [[UIApplication sharedApplication] openURL:[NSURL URLWithString:url]options:@{} completionHandler:^(BOOL success) { NSLog(@“跳转成功”); }];}调用-(IBAction)jumpWithUrl:(id)sender{ [self jumpToWechatMiniProgram:@“wx8888888888888” ghId:@“gh_88888888888” path:@“pages/index/index?session=自己定的参数” type:@“2”];}获取微信sdk的其他功能iOS中,app互相转跳走的都是openUrl这个接口,通过scheme就可以转跳到目标程序,但是scheme是不审核的,可以随意指定,所以我们可以通过写一个假微信(scheme是weixin),来拦截微信SDK的启动请求,从而获取到对应的启动字符串,然后自己拼接字符串即可。伪造微信在info.plist里添加(注意缩进不要弄错了,最好在模拟器上试,如果安装了微信,是不会跳到我们的假微信里的。):<key>CFBundleURLTypes</key> <array> <dict> <key>CFBundleURLSchemes</key> <array> <string>weixin</string> </array> <key>CFBundleURLName</key> <string>1111</string> </dict> </array>看不到源码页面的话,右键info.plist,选择Open As -> Source Code就能看到了,改完了切回Property List模式,不报错就说明格式是对的。获取转跳参数在appDelegate.m里增加:// 这方法显示已经废弃了,但是只是获取参数还是可以的- (BOOL)application:(UIApplication *)application openURL:(NSURL *)url sourceApplication:(NSString *)sourceApplication annotation:(id)annotation{ //显示截取的urlscheme UIAlertView *alert = [[UIAlertView alloc]initWithTitle:@“接收到的urlScheme” message:url.absoluteString delegate:nil cancelButtonTitle:nil otherButtonTitles:@“确定”, nil]; [alert show];// 复制到剪贴板 UIPasteboard *pasteboard = [UIPasteboard generalPasteboard]; pasteboard.string = url.absoluteString; return YES;}然后就能看到弹窗里的urlscheme就可以了,只要拼接出一个一样的urlscheme,就可以启用微信SDK同样的功能。 ...

April 12, 2019 · 1 min · jiezi

一个int变量被__block修饰与否的区别?

更多:iOS面试题大全没有修饰,被block捕获,是值拷贝。使用__block修饰,会生成一个结构体,复制int的引用地址。达到修改数据。1、block截获自动变量(局部变量)值对于 block 外的变量引用,block 默认是将其复制到其数据结构中来实现访问的。也就是说block的自动变量截获只针对block内部使用的自动变量, 不使用则不截获, 因为截获的自动变量会存储于block的结构体内部, 会导致block体积变大。特别要注意的是默认情况下block只能访问不能修改局部变量的值。2、 __block 修饰的外部变量对于用 __block 修饰的外部变量引用,block 是复制其引用地址来实现访问的。block可以修改__block 修饰的外部变量的值。3、Block的存储域及copy操作先来思考一下:Block是存储在栈上还是堆上呢?其实,block有三种类型:全局块(_NSConcreteGlobalBlock)栈块(_NSConcreteStackBlock)堆块(_NSConcreteMallocBlock)全局块存在于全局内存中, 相当于单例.栈块存在于栈内存中, 超出其作用域则马上被销毁堆块存在于堆内存中, 是一个带引用计数的对象, 需要自行管理其内存简而言之,存储在栈中的Block就是栈块、存储在堆中的就是堆块、既不在栈中也不在堆中的块就是全局块。遇到一个Block,我们怎么这个Block的存储位置呢?(1)Block不访问外界变量(包括栈中和堆中的变量)Block 既不在栈又不在堆中,在代码段中,ARC和MRC下都是如此。此时为全局块。(2)Block访问外界变量MRC 环境下:访问外界变量的 Block 默认存储栈中。ARC 环境下:访问外界变量的 Block 默认存储在堆中(实际是放在栈区,然后ARC情况下自动又拷贝到堆区),自动释放。4、防止 Block 循环引用Block 循环引用的情况:某个类将 block 作为自己的属性变量,然后该类在 block 的方法体里面又使用了该类本身,如下:self.someBlock = ^(Type var){ [self dosomething];};解决办法:(1)ARC 下:使用 __weak__weak typeof(self) weakSelf = self;self.someBlock = ^(Type var){ [weakSelf dosomething];};(2)MRC 下:使用 __block__block typeof(self) blockSelf = self;self.someBlock = ^(Type var){ [blockSelf dosomething];};值得注意的是,在ARC下,使用 __block 也有可能带来的循环引用,如下:// 循环引用 self -> _attributBlock -> tmp -> selftypedef void (^Block)();@interface TestObj : NSObject{ Block _attributBlock;}@end@implementation TestObj- (id)init { self = [super init]; __block id tmp = self; self.attributBlock = ^{ NSLog(@“Self = %@",tmp); tmp = nil; };}- (void)execBlock { self.attributBlock();}@end// 使用类id obj = [[TestObj alloc] init];[obj execBlock]; // 如果不调用此方法,tmp 永远不会置 nil,内存泄露会一直在5、有时候我们经常也会被问到block为什么 常使用copy关键字?block 使用 copy 是从 MRC遗留下来的“传统”,在 MRC 中,方法内部的 block 是在栈区的,使用 copy 可以把它放到堆区.在 ARC 中写不写都行:对于 block 使用 copy 还是 strong 效果是一样的,但写上 copy 也无伤大雅,还能时刻提醒我们:编译器自动对 block 进行了 copy 操作。如果不写 copy ,该类的调用者有可能会忘记或者根本不知道“编译器会自动对 block 进行了 copy 操作” ...

April 11, 2019 · 1 min · jiezi

iOS开发常用设计模式

1 代理模式应用场景:当一个类的某些功能需要由别的类来实现,但是又不确定具体会是哪个类实现。优势:解耦合敏捷原则:开放-封闭原则实例:tableview的 数据源delegate,通过和protocol的配合,完成委托诉求。列表row个数delegate自定义的delegate2 观察者模式应用场景:一般为model层对,controller和view进行的通知方式,不关心谁去接收,只负责发布信息。优势:解耦合敏捷原则:接口隔离原则,开放-封闭原则实例:Notification通知中心,注册通知中心,任何位置可以发送消息,注册观察者的对象可以接收。kvo,键值对改变通知的观察者,平时基本没用过。3 MVC模式应用场景:是一中非常古老的设计模式,通过数据模型,控制器逻辑,视图展示将应用程序进行逻辑划分。优势:使系统,层次清晰,职责分明,易于维护敏捷原则:对扩展开放-对修改封闭实例:model-即数据模型,view-视图展示,controller进行UI展现和数据交互的逻辑控制。4 单例模式应用场景:确保程序运行期某个类,只有一份实例,用于进行资源共享控制。优势:使用简单,延时求值,易于跨模块敏捷原则:单一职责原则实例:[UIApplication sharedApplication]。注意事项:确保使用者只能通过 getInstance方法才能获得,单例类的唯一实例。java,C++中使其没有公有构造函数,私有化并覆盖其构造函数。object c中,重写allocWithZone方法,保证即使用户用 alloc方法直接创建单例类的实例,返回的也只是此单例类的唯一静态变量。5 策略模式应用场景:定义算法族,封装起来,使他们之间可以相互替换。优势:使算法的变化独立于使用算法的用户敏捷原则:接口隔离原则;多用组合,少用继承;针对接口编程,而非实现。实例:排序算法,NSArray的sortedArrayUsingSelector;经典的鸭子会叫,会飞案例。注意事项:1.剥离类中易于变化的行为,通过组合的方式嵌入抽象基类2.变化的行为抽象基类为,所有可变变化的父类3.用户类的最终实例,通过注入行为实例的方式,设定易变行为防止了继承行为方式,导致无关行为污染子类。完成了策略封装和可替换性。6 工厂模式应用场景:工厂方式创建类的实例,多与proxy模式配合,创建可替换代理类。优势:易于替换,面向抽象编程,application只与抽象工厂和易变类的共性抽象类发生调用关系。敏捷原则:DIP依赖倒置原则实例:项目部署环境中依赖多个不同类型的数据库时,需要使用工厂配合proxy完成易用性替换注意事项:项目初期,软件结构和需求都没有稳定下来时,不建议使用此模式,因为其劣势也很明显,增加了代码的复杂度,增加了调用层次,增加了内存负担。所以要注意防止模式的滥用。更多:iOS面试题大全

April 10, 2019 · 1 min · jiezi

2 RAC解析 自定义KVO

知识点概述1.KVO实现原理2.runtime使用目的给NSObject添加一个Category,用于给实例对象添加观察者,当该实例对象的某个属性发生变化的时候通知观察者。大体思路添加观察者的方法中- (void)SQ_addObserver:(NSObject *)observer forKeyPath:(NSString *)keyPath options:(NSKeyValueObservingOptions)options context:(nullable void *)context;会用runtime的方式手动创建一个其子类,并且将该对象变为该子类。该子类会复写观察方法中keyPath的setter方法,使这个setter被调用时利用runtime去调用observer的回调方法-(void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary<NSKeyValueChangeKey,id> )change context:(void )context;实现这里只做KVO的基本功能,当被观察者改变属性的时候通知观察者,所以定义如下方法NSObject+SQKVO.h/ 添加观察者 @param observer 观察者 @param keyPath 被观察的属性名 */- (void)SQ_addObserver:(NSObject )observer forKeyPath:(NSString )keyPath;/ 当被观察的观察属性改变的时候的回调函数 @param keyPath 所观察被观察者的属性名 @param object 被观察者 @param value 被观察的属性的新值 */- (void)SQ_observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object changeValue:(id)value;@end因为这里要用到runtime所以需要添加runtime的头文件#import <objc/message.h>而且因为用到objc_msgSend所以要改变一下工程的环境变量一.动态生成子类在被观察者调用- SQ_addObserver:forKeyPath:时首先动态生成一个其子类。 // 1.生成子类 // 1.1获取名称 Class selfClass = [self class]; NSString *className = NSStringFromClass(selfClass); NSString *KVOClassName = [className stringByAppendingString:@"_SQKVO"]; const char *KVOClassNameChar = [KVOClassName UTF8String]; // 1.2创建子类 Class KVOClass = objc_allocateClassPair(selfClass, KVOClassNameChar, 0); // 1.3注册 objc_registerClassPair(KVOClass);这里可以看到,我们将子类的类名命名为“类名”+“SQKVO”,譬如类名为“Person”,这个子类是“Person_SQKVO”。这里有个注意点,一般为动态创建的类名应尽量复杂一些避免重复。最好加上“”。二.根据KeyPath动态添加对应的setter1 确定setter的名字举个例子,如果用户给的keyPath是name,应该动态添加一个-setName:的方法。而这个setter的名字是 “set” + “把keyPath变为首字母大写” + “:“所以可以得出NSString *setterString = [NSString stringWithFormat:@“set%@:”, [keyPath capitalizedString]]; SEL setter = NSSelectorFromString(setterString);2 利用class_addMethod()给子类动态添加方法BOOL class_addMethod(Class cls, SEL name, IMP imp, const char *types);cls: 给哪个类添加方法。即新生成的子类,上面生成的 KVOClass。name:所添加方法的名称。即上一步生成的字符串 setterString。imp:所添加方法的实现。即这个方法的C语言实现,首先在下面先写一个C语言的方法。稍后会讲具体实现。void setValue(id self, SEL _cmd, id newVale) {}types:所添加方法的编码类型。setter的返回值是void,参数是一个对象(id)。void用"v"表示,返回值和参数之间用“@:”隔开,对象用”@“表示。最后我们可以得出结果"v@:@"。具体其他的编码类型可以参考苹果文档。ps: 这里说下为什么返回值和参数之间用“@:”隔开。“:”代表字符串,所有的OC方法都有两个隐藏参数在参数列表的最前面,“发起者”和 “方法描述符”,“@”就是这个发起者,“:”是方法描述符。而这个types其实是imp返回值和参数的编码。因为OC方法中返回值和参数之间必然有“发起者”和“SEL”隔着,所以“@:”自然而然就成了返回值和参数之间的分隔符。当然我们还可以用@encode来得到我们想要的编码类型NSString *encodeString =[NSString stringWithFormat:@"%s%s%s%s”,@encode(void), @encode(id), @encode(SEL), @encode(id)];3 将当前对象的类变为我们所创建的子类的类型,即更改isa指针object_setClass(self, KVOClass);4 将keyPath和观察者关联(associate)到我们的对象上用下面这个函数可以很方便的将一个对象用键值对的方式绑定到一个目标对象上。*如果想了解跟多可以查找《Effective Objective-C》的第10条void objc_setAssociatedObject(id object, const void *key, id value, objc_AssociationPolicy policy);object目标对象key绑定对象的键,相当于NSDictionary的key这里的key一般采用下面的方式声明:static const void *SQKVOObserverKey = &SQKVOObserverKey;static const void *SQKVOKeyPathKey = &SQKVOKeyPathKey;这样做是因为若想令两个键匹配到同一个值,则两者必须是完全相同的指针才行。value绑定对象,相当于NSDictionary的valuepolicy绑定对象的缓存策略@property (nonatomic, weak) :OBJC_ASSOCIATION_ASSIGN@property (nonatomic, strong) :OBJC_ASSOCIATION_RETAIN_NONATOMIC@property (nonatomic, copy) :OBJC_ASSOCIATION_COPY_NONATOMIC@property (atomic, strong) :OBJC_ASSOCIATION_RETAIN@property (atomic, weak) :OBJC_ASSOCIATION_COPY最后关联的代码:objc_setAssociatedObject(self, SQKVOObserverKey, observer, OBJC_ASSOCIATION_RETAIN_NONATOMIC);objc_setAssociatedObject(self, SQKVOKeyPathKey, keyPath, OBJC_ASSOCIATION_COPY_NONATOMIC);三.setValue()的实现这个函数的目的主要是:1.利用objc_msgSend触发原先类的setter2.利用objc_msgSend触发观察者的回调方法1. 触发原先的setter方法 // 保存子类 Class KVOClass = [self class]; // 变回原先的类型,去触发setter object_setClass(self, class_getSuperclass(KVOClass)); NSString *keyPath = objc_getAssociatedObject(self, SQKVOKeyPathKey); NSString *setterString = [NSString stringWithFormat:@“set%@:”, [keyPath capitalizedString]]; SEL setter = NSSelectorFromString(setterString); objc_msgSend(self, setter, newVale);2. 调用观察者的回调方法id observer = objc_getAssociatedObject(self, SQKVOObserverKey); objc_msgSend(observer, @selector(SQ_observeValueForKeyPath:ofObject:changeValue:), keyPath, self, newVale);3.改回KVO类object_setClass(self, KVOClass);四.实现空的回调方法- (void)SQ_observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object changeValue:(id)value { }五.调用自定义的KVO恭喜你看到这里,并且恭喜你已经成功了!- (void)viewDidLoad { [super viewDidLoad]; // Do any additional setup after loading the view. self.name = @“A”; [self SQ_addObserver:self forKeyPath:@“name”]; self.name = @“B”;}- (void)SQ_observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object changeValue:(id)value { NSLog(@”%@.%@=%@", object, keyPath, value);}六.代码代码下载地址 ...

April 8, 2019 · 2 min · jiezi

缅怀那些正渐行渐远的编程语言

现代编程语言的祖先 (1801)Joseph Marie Jacquard 用打孔卡为一台织布机编写指令,在挂毯上织出了“hello, world”字样。当时的reddit网友对这项工作的反响并不热烈,因为它既缺少尾递归调用,又不支持并发,甚至都没有注意在拼写时恰当地区分大小写。这套机械编制技巧后来被改良成纸卷钢琴录音,也激发IBM创建者Herman Hollerith使用打洞卡来记录数据和做计算机程序设计。IBM为纪念纺织工业,后来在1994年也将其操作系统命名为OS/2 Warp(warp即是纺织布上的经线)。Ada Lovelace (1824)1842 年拜伦之女 Ada Lovelace 写了世界上第一个程序。她的努力只遇到了一点点小小的麻烦,那就是:实际上并没有任何计算机能够用来运行她的程序。后来的企业架构师们重新吸收了她的这个技能,用来学习如何更好地使用UML进行编程。Ada Lovelace为Charles Babbage的分析机写了一个计算伯努利数的算法实现,因此被后世公认为是世界上第一个程序员。实际上,由于分析机设计思想过于先进,在当时根本没有被制造出来。(Babbage的分析机一般被认为是现代电子通用计算机的先驱。)讽刺现在的某些“软件架构师”顶多只会纸上谈兵地画画UML。Fortran (1957)John Backus 和 IBM 发明了 Fortran(FORmula TRANslator)语言。它是世界上最早出现的计算机高级程序设计语言,广泛应用于科学和工程计算领域。FORTRAN语言以其特有的功能在数值、科学和工程计算领域发挥着重要作用。Fortran 90之前的版本是人们所知晓的FORTRAN(全部字母大写),从Fortran 90以及以后的版本都写成Fortran(仅有第一个字母大写)。关于IBM或Fortran并没有什么特别的地方,除了写 Fortran程序的时候不系蓝领带将被编译器视作是一个 syntax error。蓝领带、白衬衫、深色西装似乎是IBM公司20世纪经典的dress code。早期Fortran(Fortran 77)对程序书写格式的要求那是相当严格。(例如,固定格式缩进)Cobol (1959)在输掉了和 L. Ron Hubbard 之间的一场打赌之后,Grace Hopper 和其他几个发明了所谓的“面向Boilerplate的全大写化语言”(Capitalization Of Boilerplate Oriented Language,Cobol)Cobol(CommonBusinessOrientedLanguage)是数据处理领域最为广泛的程序设计语言,是第一个广泛使用的高级编程语言。在企业管理中,数值计算并不复杂,但数据处理信息量却很大。为专门解决经企管理问题,美国的一些计算机用户于1959年组织设计了专用于商务处理的计算机语言COBOL,并于1961年美国数据系统语言协会公布。COBOL语言以代码极其冗长和通篇大写字母的书写风格而闻名。Pascal (1970)1970年 Niklaus Wirth 创造了Pascal,一个过程式的语言。尽管Pascal非常流行(然而在八十到九十年代时比21世纪更加流行),依据维尔特的对这种语言的定义来构建Pascal,使它不适合在非教学的场合使用,这遭到了广泛的批评。 推广了C语言的Brian Kernighan早在1981年就在他的论文《Why Pascal Is Not My Favourite Programming Language》对Pascal提出了严厉的抨击。Smalltalk (1980)Alan Kay 创造了Smalltalk并发明了“面向对象”这个词。当被问到它的含义时,他回答道:“Smalltalk 程序本身就是对象。”当被问到对象是由什么组成时,他回答到:“对象。”当再一次被问到这个问题时,他说“看,它从里到外都是对象。直到你抽出一只乌龟。”90年代的许多软件开发思想得利于Smalltalk,例如Design Patterns, Extreme Programming(XP)和Refactoring等。20世纪70年代到80年代前期,美国施乐公司的帕洛阿尔托研究中心(PARC)开发了Smalltalk编程语言。从Smalltalk-72、Smalltalk-78到Smalltalk-80,他们开发完成了整个Smalltalk系列,Smalltalk编程语言对近代面向对象编程语言影响很大,所以称之为“面向对象编程之母”。Smalltalk的设计从很大程度上受到了Logo的影响。这门语言在70年代初期就诞生了,但公开可用的第一版是Smalltalk-80 Version 1。Ada (1983)为了纪念伟大的先辈程序员 Ada Lovelace 那能够写出永远也无法被执行的代码的彪悍技能,Jean Ichbiah和美国国防部创造了Ada语言。Ada不仅体现了许多现代软件的开发原理,而且将这些原理付诸实现。同时,Ada语言的使用可大大改善软件系统的清晰性、可靠性、有效性、可维护性。Ada是现有的语言中无与伦比的一种大型通用程序设计语言,它是现代计算机语言的成功代表,集中反映了程序语言研究的成果。Ada的出现,标志着软件工程成功地进入了国家和国际的规模。尽管缺乏证据显示有任何重要的Ada程序曾经被完成过,历史学家仍然确信Ada是个成功的公益项目,它让数以千计的国防承包商免于沦落为与黑帮为伍。Ada 曾经是美国国防部指定的嵌入式计算机系统唯一开发语言,在其研发上耗资巨大。(国防承包商们于是不用靠贩卖军火给黑帮来维持生计了。)以上语言虽然都在不同程度上渐渐不再为现代的多数开发者所用,但每一次的创新与创造,都将关乎未来。近年来年度编程语言排行(来源:TIOBE)语言会迭代升级、有兴衰起落。历年来编程语言排行榜的名次也都是在不断变化,不论是否流行 ,都值得被尊重。仅以此,向正在努力着的开发者们致敬! ...

April 8, 2019 · 1 min · jiezi

1.RAC解析 - 自定义链式编程

目的模仿Masonry连续运用点语法的操作[self.view mas_makeConstraints:^(MASConstraintMaker *make) { make.top.equalTo(@10).offset(1); }];写出一个连加的操作 make.add(10).add(10);想看结果的请直接跳到“最终结果”分析一.定义SQMath类SQMath.h#import <Foundation/Foundation.h>@interface SQMath : NSObject- (NSInteger)result;- (void)add:(int)number;@endSQMath.m#import “SQMath.h”@interface SQMath ()@property (nonatomic, assign) NSInteger number;@end@implementation SQMath- (NSInteger)result { return self.number;}- (void)add:(int)number { self.number += number;}@end使用这个SQMath的add方法 SQMath *math = [[SQMath alloc] init]; [math add:10]; [math add:20]; NSLog(@"%ld", [math result]);二.将函数调用改为点语法如果要用点语法,需要让-add从一个方法变成一个add的属性。但是这样就没有办法传参了- (NSInteger)add;但是如果返回值是一个 NSInteger (^)(NSInteger) 类型的block就可以了。math.add返回的是这个block,这个block是需要一个NSInteger为参数(加数),返回值是NSInteger(结果)。SQMath.m- (NSInteger (^)(NSInteger count))add { __weak typeof(self) weakSelf=self; NSInteger (^addBlock)(NSInteger) = ^(NSInteger addCount){ __strong typeof(weakSelf) strongSelf = weakSelf; strongSelf.number += addCount; return strongSelf.number; }; return addBlock;}或者- (NSInteger (^)(NSInteger count))add { __weak typeof(self) weakSelf=self; return ^(NSInteger addCount) { __strong typeof(weakSelf) strongSelf = weakSelf; strongSelf.number += addCount; return strongSelf.number; };}使用这个SQMath的add方法 SQMath math = [[SQMath alloc] init]; NSLog(@"%ld", math.add(10)); NSLog(@"%ld", math.add(20)); NSLog(@"%ld", math.add(30));三.连续使用点语法只要将Block的返回值更改为self。这样每次add返回的则变成了SQMath的实例对象,这样就可以实现连续点语法的效果了。- (SQMath (^)(NSInteger count))add { __weak typeof(self) weakSelf=self; return ^(NSInteger addCount) { __strong typeof(weakSelf) strongSelf = weakSelf; strongSelf.number += addCount; return self; };}使用这个SQMath的add方法SQMath *math = [[SQMath alloc] init];NSLog(@"%ld", math.add(10).add(20).add(30).result) ;四.将这个改为NSNumber的CategoryNSNumber+SQMath.h#import <Foundation/Foundation.h>#import “SQMath.h”@interface NSNumber (Math)- (NSInteger)sq_add:(void(^)(SQMath *make))block;@endNSNumber+SQMath.m#import “NSNumber+SQMath.h”@implementation NSNumber (SQMath)- (NSInteger)sq_add:(void(^)(SQMath *))block { SQMath *math = [[SQMath alloc] init]; block(math); return math.result;}@endNSNumber+SQMath 使用 NSInteger result = [@10 sq_add:^(SQMath * make) { make.add(10).add(20); }]; NSLog(@"%ld", result);最终结果SQChainProgrammingps:链式编程什么时候用我还真不太清楚,但我知道面试的时候肯定有用 哈哈。 ...

April 7, 2019 · 1 min · jiezi

如何为Xcode添加漂亮的主题

由来作为一名iOS开发者,Xcode是我们每天必须使用的开发工具,但是时间久了,相信很多人每天对着代码就已经很乏味了。时间长了,对我们自己的眼睛也不太好,一个漂亮的主题,既能减少我们写代码、看代码时候的乏味度,还能保护我们的眼睛。主题选择好了,还能看上去高大上,提升我们的逼格,让iOS开发在外人眼里成为艺术的创作。为Xcode添加主题如果你对Xcode自带的主题换了又换,还是感觉不满意;或者你想提升Xcode的逼格,让自己看起来更像艺术创作者,那么接着往下看。下面就向你展示添加Xcode新主题,提升逼格的秘籍。下面请跟着我的招式走:首先下载主题解压压缩包。将压缩包中的dvtcolortheme文件,拷贝到~/Library/Developer/Xcode/UserData/FontAndColorThemes目录下。重启Xcode,在Xcode的主题选项里已经有了有了你新添加的主题,选择预览,选择自己喜欢的即可。附上图解:根据上图所示,通过前往文件夹,在弹出框中输入~/Library/Developer/Xcode/UserData/FontAndColorThemes把下载主题压缩包里V1&V2文件夹中的内容进行拷贝到FontAndColorThemes目录下,重启Xcode。重启Xcode之后,即可选择自己喜欢的主题。主题源码该主题由WWDC在2016年提供,并且开源了源码。源代码名称:WWDC2016-Xcode-Color-Scheme。源代码网址WWDC2016-Xcode-Color-Scheme源代码文档

April 5, 2019 · 1 min · jiezi

为什么微服务一定要有网关?

一、什么是服务网关服务网关 = 路由转发 + 过滤器1、路由转发:接收一切外界请求,转发到后端的微服务上去;2、过滤器:在服务网关中可以完成一系列的横切功能,例如权限校验、限流以及监控等,这些都可以通过过滤器完成(其实路由转发也是通过过滤器实现的)。二、为什么需要服务网关上述所说的横切功能(以权限校验为例)可以写在三个位置:每个服务自己实现一遍写到一个公共的服务中,然后其他所有服务都依赖这个服务写到服务网关的前置过滤器中,所有请求过来进行权限校验第一种,缺点太明显,基本不用;第二种,相较于第一点好很多,代码开发不会冗余,但是有两个缺点:由于每个服务引入了这个公共服务,那么相当于在每个服务中都引入了相同的权限校验的代码,使得每个服务的jar包大小无故增加了一些,尤其是对于使用docker镜像进行部署的场景,jar越小越好;由于每个服务都引入了这个公共服务,那么我们后续升级这个服务可能就比较困难,而且公共服务的功能越多,升级就越难,而且假设我们改变了公共服务中的权限校验的方式,想让所有的服务都去使用新的权限校验方式,我们就需要将之前所有的服务都重新引包,编译部署。而服务网关恰好可以解决这样的问题:将权限校验的逻辑写在网关的过滤器中,后端服务不需要关注权限校验的代码,所以服务的jar包中也不会引入权限校验的逻辑,不会增加jar包大小;如果想修改权限校验的逻辑,只需要修改网关中的权限校验过滤器即可,而不需要升级所有已存在的微服务。所以,需要服务网关!!!三、服务网关技术选型引入服务网关后的微服务架构如上,总体包含三部分:服务网关、open-service和service。1、总体流程:服务网关、open-service和service启动时注册到注册中心上去;用户请求时直接请求网关,网关做智能路由转发(包括服务发现,负载均衡)到open-service,这其中包含权限校验、监控、限流等操作open-service聚合内部service响应,返回给网关,网关再返回给用户2、引入网关的注意点增加了网关,多了一层转发(原本用户请求直接访问open-service即可),性能会下降一些(但是下降不大,通常,网关机器性能会很好,而且网关与open-service的访问通常是内网访问,速度很快);网关的单点问题:在整个网络调用过程中,一定会有一个单点,可能是网关、nginx、dns服务器等。防止网关单点,可以在网关层前边再挂一台nginx,nginx的性能极高,基本不会挂,这样之后,网关服务就可以不断的添加机器。但是这样一个请求就转发了两次,所以最好的方式是网关单点服务部署在一台牛逼的机器上(通过压测来估算机器的配置),而且nginx与zuul的性能比较,根据国外的一个哥们儿做的实验来看,其实相差不大,zuul是netflix开源的一个用来做网关的开源框架;网关要尽量轻。3、服务网关基本功能智能路由:接收外部一切请求,并转发到后端的对外服务open-service上去;注意:我们只转发外部请求,服务之间的请求不走网关,这就表示全链路追踪、内部服务API监控、内部服务之间调用的容错、智能路由不能在网关完成;当然,也可以将所有的服务调用都走网关,那么几乎所有的功能都可以集成到网关中,但是这样的话,网关的压力会很大,不堪重负。权限校验:只校验用户向open-service服务的请求,不校验服务内部的请求。服务内部的请求有必要校验吗?API监控:只监控经过网关的请求,以及网关本身的一些性能指标(例如,gc等);限流:与监控配合,进行限流操作;API日志统一收集:类似于一个aspect切面,记录接口的进入和出去时的相关日志。上述功能是网关的基本功能,网关还可以实现以下功能:A|B测试:A|B测试时一块比较大的东西,包含后台实验配置、数据埋点(看转化率)以及分流引擎,在服务网关中,可以实现分流引擎,但是实际上分流引擎会调用内部服务,所以如果是按照上图的架构,分流引擎最好做在open-service中,不要做在服务网关中。4、技术选型技术选型参考如下:开发语言:java + groovy,groovy的好处是网关服务不需要重启就可以动态的添加filter来实现一些功能;微服务基础框架:springboot;网关基础组件:netflix zuul;服务注册中心:consul;权限校验:jwt;API监控:prometheus + grafana;API统一日志收集:logback + ELK;压力测试:Jmeter;

April 3, 2019 · 1 min · jiezi

iOS开发现在该如何选择方向?

跳槽,面试,进阶,加薪;这些字眼,相信每位程序员都不陌生!但是方向的选择,却不知如何抉择!其实最好的方向,已经在各个企业面试需求中完美的体现出来了;本文展示了2份面试需求,以及方向的总结,希望对读者有所帮助2份面试需求熟练组件化架构,有较强的解耦能力,深刻理解设计模式熟悉常用的网络通信协议,如http、tcp、udp等,了解socket通信机制熟练使用Objective-C,熟悉iOS的内存管理机制和多线程开发,或具有良好的C/C++语言基础,了解内存和指针概念,对于优化程序的性能有一定经验;有较强的英语能力,拥有良好的代码规范有研读源码的能力:objc runloop dispacth …有较强的三方能力,知其然而知其所以然有OpenGL,OpenGLES,FFmpeg 底层音视频开发者优先有逆向开发经验优先考虑了解算法,数据结构熟练组件化架构,MVP,MVVM,MVC,ROUTER有较强的理解;熟练掌握Objective-C语言,理解面向对象编程思想,具有较强的设计能力;熟练掌握APNS、UI布局、数据库、网络等开发技术;深入理解Objective-C Runtime、RunLoop等基础原理;对App提高用户体验、性能调优、防崩溃、节省流量等的方法有深入了解;良好的编程习惯,逻辑清晰,认真细致,良好的沟通能力,主动的沟通意识;较强的自学能力、自我驱动力、强烈的探索欲。总结:需要掌握了解的技术点架构模式,编程思想,设计模式底层进阶,深层理解三方框架要知其然,而知其所以然多线程与网络内存管理,性能优化数据结构和算法音视频方向逆向方向相信看完,也许发现这些技术领域自己可能都知道,却没行动学习起来,或者没坚持下来!!正如“大道理都懂,但是依然过不好这一生”最后说一句:请合理利用自己每一分每一秒的时间来学习提升自己,不要再用"没有时间"来掩饰自己思想上的懒惰!趁年轻,使劲拼,给未来的自己一个交代!如果想有个学习交流的圈子,可以加iOS高级交流群:624212887;请教的问题,会的都会解答,欢迎入驻推荐文集* BAT—最新iOS面试题总结

April 3, 2019 · 1 min · jiezi

React Native 跳转到 APP 推送页面并获取推送状态

产品为了提高推送送达率,提了一个需求:在 APP 推送关闭的情况下显示一个小 TIP,点击 TIP 跳转到 APP 消息设置界面。我们的 APP 是基于 React Native 开发的,这些功能 Facebook 官方没有提供,需要我们开发对应的原生模块。因为开发原生模块属于比较深入的内容了,写这篇文章时我就默认阅读者已经具有一定的 Objective-C 和 Java 开发能力,下面就直接贴代码说思路了。开发一个原生模块的基础知识可以直接看官方文档,写的很详细,我这里就不多重复了。React Native 开发 Android 原生模块React Native 开发 iOS 原生模块下面开始分析实现。第一步:获取 APP 推送状态这里我主要参考极光推送。因为公司内部有统一的推送 SDK(主要整合了市面上多家推送服务公司和手机厂商的推送服务),一些极光推送很方便的功能暂时用不了,只能自己参考实现。在我的实现里,获取 APP 推送状态主要做了两件事:兼容多个系统版本(这部分都是极光推送开发者的功劳);以 Promise 的形式进行封装(极光推送是基于 callback 的)getSystemNoticeStatus() 这个函数,在 APP 推送开启的情况下返回 true,未开启情况返回 false。iOS 代码如下:参考链接:https://github.com/jpush/jpus…RCT_EXPORT_METHOD( getSystemNoticeStatus: (RCTPromiseResolveBlock) resolverejecter: (RCTPromiseRejectBlock) reject ){ dispatch_async( dispatch_get_main_queue(), ^{ float systemVersion = [[UIDevice currentDevice].systemVersion floatValue]; if ( systemVersion >= 8.0 ) { UIUserNotificationSettings settings = [[UIApplication sharedApplication] currentUserNotificationSettings]; UIUserNotificationType type = settings.types; if ( type == UIUserNotificationTypeNone ) { return(resolve (@NO) ); }else { return(resolve (@YES) ); } }else if ( systemVersion >= 10.0 ) { [[UNUserNotificationCenter currentNotificationCenter] getNotificationSettingsWithCompletionHandler: ^ (UNNotificationSettings * _Nonnull settings) { switch ( settings.authorizationStatus ) { case UNAuthorizationStatusDenied: case UNAuthorizationStatusNotDetermined: return(resolve (@NO) ); break; case UNAuthorizationStatusAuthorized: return(resolve (@YES) ); break; } }]; } } );}Android 代码如下:参考链接:https://github.com/jpush/jpus…/* * 获取 APP 系统通知状态 * * /@ReactMethodpublic void getSystemNoticeStatus(Promise promise) { promise.resolve(hasPermission(“OP_POST_NOTIFICATION”));}private boolean hasPermission(String appOpsServiceId) { Context context = getReactApplicationContext(); if (Build.VERSION.SDK_INT >= 24) { NotificationManager mNotificationManager = (NotificationManager) context.getSystemService( Context.NOTIFICATION_SERVICE); return mNotificationManager.areNotificationsEnabled(); }else if(Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT){ AppOpsManager mAppOps = (AppOpsManager) context.getSystemService(Context.APP_OPS_SERVICE); ApplicationInfo appInfo = context.getApplicationInfo(); String pkg = context.getPackageName(); int uid = appInfo.uid; Class appOpsClazz; try { appOpsClazz = Class.forName(AppOpsManager.class.getName()); Method checkOpNoThrowMethod = appOpsClazz.getMethod(“checkOpNoThrow”, Integer.TYPE, Integer.TYPE, String.class); Field opValue = appOpsClazz.getDeclaredField(appOpsServiceId); int value = opValue.getInt(Integer.class); Object result = checkOpNoThrowMethod.invoke(mAppOps, value, uid, pkg); return Integer.parseInt(result.toString()) == AppOpsManager.MODE_ALLOWED; } catch (InvocationTargetException e) { e.printStackTrace(); } catch (IllegalAccessException e) { e.printStackTrace(); } catch (NoSuchMethodException e) { e.printStackTrace(); } catch (NoSuchFieldException e) { e.printStackTrace(); } catch (ClassNotFoundException e) { e.printStackTrace(); } } return false;}然后我们在 JavaScript侧直接引用就可import { Platform, NativeModules,} from ‘react-native’;function getSystemNoticeStatus() { NativeModules.appName.getSystemNoticeStatus().then((isOpen) => { console.log(‘getSystemNotice’, isOpen) // }).catch((e) => { console.log(‘getSystemNoticeStatus error’, e) });}第二步:跳转到 APP 设置界面跳转到 APP 设置界面也要考虑不同系统版本的兼容。比如说 iOS11+ 现在只允许跳转到系统设置首页/该应用的设置界面,Android 还要考虑不同厂商对 APP 设置页面的魔改,很是头疼。首先 iOS 适配,我们直接跳转到该应用的设置首页,就是下图:这个开发比较简单,直接在 React Native 中引用 Linking.openURL(‘app-settings:’) 就行;Android 就要多些一些代码了,具体的适配可以看注释:/* * * 跳转到系统通知设置界面 * this.appContext 表示文件/应用的上下文环境 * /@ReactMethodpublic void openSystemNoticeView(){ try { // 跳转到通知设置界面 Intent intent = new Intent(); intent.setAction(Settings.ACTION_APP_NOTIFICATION_SETTINGS); //这种方案适用于 API 26, 即8.0(含8.0)以上可以用 intent.putExtra(EXTRA_APP_PACKAGE, this.appContext.getPackageName()); intent.putExtra(EXTRA_CHANNEL_ID, this.appContext.getApplicationInfo().uid); //这种方案适用于 API21——25,即 5.0——7.1 之间的版本可以使用 intent.putExtra(“app_package”, this.appContext.getPackageName()); intent.putExtra(“app_uid”, this.appContext.getApplicationInfo().uid); intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK); this.appContext.startActivity(intent); } catch (Exception e) { e.printStackTrace(); // 出现异常则跳转到应用设置界面:锤子 Intent intent = new Intent(); //下面这种方案是直接跳转到当前应用的设置界面。 //https://blog.csdn.net/ysy950803/article/details/71910806 intent.setAction(Settings.ACTION_APPLICATION_DETAILS_SETTINGS); Uri uri = Uri.fromParts(“package”, this.appContext.getPackageName(), null); intent.setData(uri); intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK); this.appContext.startActivity(intent); }}然后我们在 JavaScript 侧做一些兼容处理:import { Linking, Platform,} from ‘react-native’;/* * 跳转到 APP 消息设置页面 * */export function openSystemNoticeSetting() { if (Platform.OS === “android”) { NativeModules.appName.openSystemNoticeView(); } else { Linking.openURL(‘app-settings:’) .catch(err => console.log(‘openSystemSetting error’, err)); }}需要跳转时,我们直接用openSystemNoticeSetting() 这个函数就行了。上面就是开发中遇到的两个难点,如果此篇文章你认为对你有用,可以点个赞表示对我的鼓励,谢谢。 ...

April 3, 2019 · 2 min · jiezi

看完就懂的无痕埋点

无痕埋点的设计与实现在移动互联网时代,对于每个公司、企业来说,用户的数据非常重要。重要到什么程度,用户在这个页面停留多久、点击了什么按钮、浏览了什么内容、什么手机、什么网络环境、App什么版本等都需要清清楚楚。甚至一些大厂的蛮多业务成果都是靠基于用户操作行为和记录的推荐转换二次。那么有了上述的诉求,那么技术人员如何满足这些需求?引出来了一个技术点-“埋点”埋点手段业界中对于代码埋点主要有3种主流的方案:代码手动埋点、可视化埋点、无痕埋点。简单说说这几种埋点方案。代码手动埋点:根据业务需求(运营、产品、开发多个角度出发)在需要埋点地方手动调用埋点接口,上传埋点数据。可视化埋点:通过可视化配置工具完成采集节点,在前端自动解析配置并上报埋点数据,从而实现可视化“无痕埋点”无痕埋点:通过技术手段,完成对用户行为数据无差别的统计上传的工作。后期数据分析处理的时候通过技术手段筛选出合适的数据进行统计分析。技术选型代码手动埋点该方案情况下,如果需要埋点,则需要在工程代码中,写埋点相关代码。因为侵入了业务代码,对业务代码产生了污染,显而易见的缺点是埋点的成本较高、且违背了单一原则。例1:假如你需要知道用户在点击“购买按钮”时的相关信息(手机型号、App版本、页面路径、停留时间、动作等等),那么就需要在按钮的点击事件里面去写埋点统计的代码。这样明显的弊端就是在之前业务逻辑的代码上面又多出了埋点的代码。由于埋点代码分散、埋点的工作量很大、代码维护成本较高、后期重构很头痛。例2:假如 App 采用了 Hybrid 架构,当 App 的第一版本发布的时候 H5 的关键业务逻辑统计是由 Native 定义好关键逻辑(比如H5调起了Native的分享功能,那么存在一个分享的埋点事件)的桥接。假如某天增加了一个扫一扫功能,未定义扫一扫的埋点桥接,那么 H5 页面变动的时候,Native 埋点代码不去更新的话,变动的 H5 的业务就未被精确统计。优点:产品、运营工作量少,对照业务映射表就可以还原出相关业务场景、数据精细无须大量的加工和处理缺点:开发工作量大、前期需要和运营、产品指定的好业务标识,以便产品和运营进行数据统计分析可视化埋点可视化埋点的出现,是为解决代码埋点流程复杂、成本高、新开发的页面(H5、或者服务端下发的 json 去生成相应页面)不能及时拥有埋点能力前端在「埋点编辑模式」下,以“可视化”的方式去配置、绑定关键业务模块的路径到前端可以唯一确定到view的xpath过程。用户每次操作的控件,都生成一个 xpath 字符串,然后通过接口将 xpath 字符串(view在前端系统中的唯一定位。以 iOS 为例,App名称、控制器名称、一层层view、同类型view的序号:“GoodCell.21.RetailTableView.GoodsViewController.*baoApp”)到真正的业务模块(“宝App-商城控制器-分销商品列表-第21个商品被点击了”)的映射关系上传到服务端。xpath 具体是什么在下文会有介绍。之后操作 App 就生成对应的 xpath 和埋点数据(开发者通过技术手段将从服务端获取的关键数据塞到前端的 UI 控件上。 iOS 端为例, UIView 的 accessibilityIdentifier 属性可以设置我们从服务端获取的埋点数据)上传到服务端。优点:数据量相对准确、后期数据分析成本低缺点:前期控件的唯一识别、定位都需要额外开发;可视化平台的开发成本较高;对于额外需求的分析可能会比较困难无痕埋点通过技术手段无差别地记录用户在前端页面上的行为。可以正确的获取 PV、UV、IP、Action、Time 等信息。缺点:前期开发统计基础信息的技术产品成本较高、后期数据分析数据量很大、分析成本较高(大量数据传统的关系型数据库压力大)优点:开发人员工作量小、数据全面、无遗漏、产品和运营按需分析、支持动态页面的统计分析如何选择结合上述优缺点,我们选择了无痕埋点+可视化埋点结合的技术方案。怎么说呢?对于关键的业务开发结束上线后、通过可视化方案(类似于一个界面,想想看 Dreamwaver,你在界面上拖拖控件,简单编辑下就可以生成对应的 HTML 代码)点击一下绑定对应关系到服务端。那么这个对应关系是什么?我们需要唯一定位一个前端元素,那么想到的办法就是不管 Native 和 Web 前端,控件或者元素来说就是一个树形层级,DOM tree 或者 UI tree,所以我们通过技术手段定位到这个元素,以 Native iOS 为例子假如我点击商品详情页的加入购物车按钮会根据 UI 层级结构生成一个唯一标识 “addCartButton.GoodsViewController.GoodsView.*BaoApp” 。但是用户在使用 App 的时候,上传的是这串东西的 MD5到服务端。这么做有2个原因:服务端数据库存储这串很长的东西不是很好;埋点数据被劫持的话直接看到明文不太好。所以 MD5 再上传。操刀就干数据的收集实现方案由以下几个关键指标:现有代码改动少、尽量不要侵入业务代码去实现拦截系统事件全量收集如何唯一标识一个控件元素不侵入业务代码拦截系统事件以 iOS 为例。我们会想到 AOP(Aspect Oriented Programming)面向切面编程思想。动态地在函数调用前后插入相应的代码,在 Objective-C 中我们可以利用 Runtime 特性,用 Method Swizzling 来 hook 相应的函数为了给所有类方便地 hook,我们可以给 NSObject 添加个 Category,名字叫做 NSObject+MethodSwizzling+ (void)swizzleMethod:(SEL)originalSelector swizzledSelector:(SEL)swizzledSelector{ Class class = [self class]; //原有方法 Method originalMethod = class_getInstanceMethod(class, originalSelector); //替换原有方法的新方法 Method swizzledMethod = class_getInstanceMethod(class, swizzledSelector); //先尝试給源SEL添加IMP,这里是为了避免源SEL没有实现IMP的情况 BOOL didAddMethod = class_addMethod(class,originalSelector, method_getImplementation(swizzledMethod), method_getTypeEncoding(swizzledMethod)); if (didAddMethod) {//添加成功:表明源SEL没有实现IMP,将源SEL的IMP替换到交换SEL的IMP class_replaceMethod(class,swizzledSelector, method_getImplementation(originalMethod), method_getTypeEncoding(originalMethod)); } else {//添加失败:表明源SEL已经有IMP,直接将两个SEL的IMP交换即可 method_exchangeImplementations(originalMethod, swizzledMethod); }}+ (void)swizzleClassMethod:(SEL)originalSelector swizzledSelector:(SEL)swizzledSelector{ Class class = [self class]; //原有方法 Method originalMethod = class_getClassMethod(class, originalSelector); //替换原有方法的新方法 Method swizzledMethod = class_getClassMethod(class, swizzledSelector); //先尝试給源SEL添加IMP,这里是为了避免源SEL没有实现IMP的情况 BOOL didAddMethod = class_addMethod(class,originalSelector, method_getImplementation(swizzledMethod), method_getTypeEncoding(swizzledMethod)); if (didAddMethod) {//添加成功:表明源SEL没有实现IMP,将源SEL的IMP替换到交换SEL的IMP method_exchangeImplementations(originalMethod, swizzledMethod); } else {//添加失败:表明SEL已经有IMP,直接将两个SEL的IMP交换即可 method_exchangeImplementations(originalMethod, swizzledMethod); }}全量收集我们会想到 hook AppDelegate 代理方法、UIViewController 生命周期方法、按钮点击事件、手势事件、各种系统控件的点击回调方法、应用状态切换等等。动作事件App 状态的切换给 Appdelegate 添加分类,hook 生命周期UIViewController 生命周期函数给 UIViewController 添加分类,hook 生命周期UIButton 等的点击UIButton 添加分类,hook 点击事件UICollectionView、UITableView 等的在对应的 Cell 添加分类,hook 点击事件手势事件 UITapGestureRecognizer、UIControl、UIResponder相应系统事件以统计页面的打开时间和统计页面的打开、关闭的需求为例,我们对 UIViewController 进行 hookstatic char *viewController_open_time = “viewController_open_time”;static char *viewController_close_time = “viewController_close_time”;// load 方法里面添加 dispatch_once 是为了防止手动调用 load 方法。+ (void)load{ static dispatch_once_t onceToken; dispatch_once(&onceToken, ^{ @autoreleasepool { [[self class] swizzleMethod:@selector(viewWillAppear:) swizzledSelector:@selector(viewWillAppear:)]; [[self class] swizzleMethod:@selector(viewWillDisappear:) swizzledSelector:@selector(viewWillDisappear:)]; } });}#pragma mark - add prop- (void)setOpenTime:(NSDate *)openTime{ objc_setAssociatedObject(self,&viewController_open_time, openTime, OBJC_ASSOCIATION_RETAIN_NONATOMIC);}- (NSDate *)getOpenTime{ return objc_getAssociatedObject(self, &viewController_open_time);}- (void)setCloseTime:(NSDate *)closeTime{ objc_setAssociatedObject(self,&viewController_close_time, closeTime, OBJC_ASSOCIATION_RETAIN_NONATOMIC);}- (NSDate *)getCloseTime{ return objc_getAssociatedObject(self, &viewController_close_time);}- (void)viewWillAppear:(BOOL)animated{ NSString *className = NSStringFromClass([self class]); NSString *refer = [NSString string]; if ([self getPageUrl:className]) { //设置打开时间 [self setOpenTime:[NSDate dateWithTimeIntervalSinceNow:0]]; if (self.navigationController) { if (self.navigationController.viewControllers.count >=2) { //获取当前vc 栈中 上一个VC UIViewController referVC = self.navigationController.viewControllers[self.navigationController.viewControllers.count-2]; refer = [self getPageUrl:NSStringFromClass([referVC class])]; } } if (!refer || refer.length == 0) { refer = @“unknown”; } [SDGDataCenter openPage:[self getPageUrl:className] fromPage:refer]; } [self viewWillAppear:animated];}- (void)viewWillDisappear:(BOOL)animated{ NSString className = NSStringFromClass([self class]); if ([self getPageUrl:className]) { [self setCloseTime:[NSDate dateWithTimeIntervalSinceNow:0]]; [SDGDataCenter leavePage:[self getPageUrl:className] spendTime:[self p_calculationTimeSpend]]; } [self viewWillDisappear:animated];}#pragma mark - private method- (NSString )p_calculationTimeSpend{ if (![self getOpenTime] || ![self getCloseTime]) { return @“unknown”; } NSTimeInterval aTimer = [[self getCloseTime] timeIntervalSinceDate:[self getOpenTime]]; int hour = (int)(aTimer/3600); int minute = (int)(aTimer - hour3600)/60; int second = aTimer - hour3600 - minute60; return [NSString stringWithFormat:@"%d",second];}@end如何唯一标识一个控件元素xpath 是移动端定义可操作区域的唯一标识。既然想通过一个字符串标识前端系统中可操作的控件,那么 xpath 需要2个指标:唯一性:在同一系统中不存在不同控件有着相同的 xpath稳定性:不同版本的系统中,在页面结构没有变动的情况下,不同版本的相同页面,相同的控件的 xpath 需要保持一致。我们想到 Naive、H5 页面等系统渲染的时候都是以树形结构去绘制和渲染,所以我们以当前的 View 到系统的根元素之间的所有关键点(UIViewController、UIView、UIView容器(UITableView、UICollectionView等)、UIButton…)串联起来这样就唯一定位了控件元素。为了精确定位元素节点,参看下图假设一个 UIView 中有三个子 view,先后顺序是:label、button1、button2,那么深度依次为: 0、1、2。假如用户做了某些操作将 label1 从父 view 中被移除了。此时 UIView 只有 2 个子view:button1、button2,而且深度变为了:0、1。可以看出仅仅由于其中某个子 view 的改变,却导致其它子 view 的深度都发生了变化。因此,在设计的时候需要注意,在新增/移除某一 view 时,尽量减少对已有 view 的深度的影响,调整了对节点的深度的计算方式:采用当前 view 位于其父 view 中的所有 与当前 view 同类型 子view 中的索引值。我们再看一下上面的这个例子,最初 label、button1、button2 的深度依次是:0、0、1。在 label 被移除后,button1、button2 的深度依次为:0、1。可以看出,在这个例子中,label 的移除并未对 button1、button2 的深度造成影响,这种调整后的计算方式在一定程度上增强了 xpath 的抗干扰性。另外,调整后的深度的计算方式是依赖于各节点的类型的,因此,此时必须要将各节点的名称放到viewPath中,而不再是仅仅为了增加可读性。在标识控件元素的层级时,需要知道「当前 view 位于其父 view 中的所有 与当前 view 同类型 子view 中的索引值」。参看上图,如果不是同类型的话,则唯一性得不到保证。有个问题,比如我们点击的元素是 UITableViewCell,那么它虽然可以定位到类似于这个标示 xxApp.GoodsViewController.GoodsTableView.GoodsCell,同类型的 Cell 有多个,所以单凭借这个字符串是没有办法定位具体的那个 Cell 被点击了。有2个解决方案利用系统提供的 accessibilityIdentifier 官方给出的解释是标识用户界面元素的字符串找出当前元素在父层同类型元素中的索引。根据当前的元素遍历当前元素的父级元素的子元素,如果出现相同的元素,则需要判断当前元素是所在层级的第几个元素/A string that identifies the user interface element.default == nil/@property(nullable, nonatomic, copy) NSString *accessibilityIdentifier NS_AVAILABLE_IOS(5_0);服务端下发唯一标识接口获取的数据,里面有当前元素的唯一标识。比如在 UITableView 的界面去请求接口拿到数据,那么在在获取到的数据源里面会有一个字段,专门用来存储动态化的经常变动的数据。cell.accessibilityIdentifier = [[[SDGGoodsCategoryServices sharedInstance].categories[indexPath.section] children][indexPath.row].spmContent yy_modelToJSONString];判断在同层级、同类型的控件元素里面的序号对当前的控件元素的父视图的全部子视图进行遍历,如果存在和当前的控件元素同类型的控件,那么需要判断当前控件元素在同类型控件元素中的所处的位置,那么则可以唯一定位。举例:GoodsCell-3.GoodsTableView.GoodsViewController.xxApp//UIResponder分类{ // if (self.xq_identifier_ka == nil) { if ([self isKindOfClass:[UIView class]]) { UIView *view = (id)self; NSString *sameViewTreeNode = [view obtainSameSuperViewSameClassViewTreeIndexPath]; NSMutableString *str = [NSMutableString string]; //特殊的 加减购 因为带有spm但是要区分加减 需要带TreeNode NSString *className = [NSString stringWithUTF8String:object_getClassName(view)]; if (!view.accessibilityIdentifier || [className isEqualToString:@“XQButton”]) { [str appendString:sameViewTreeNode]; [str appendString:@","]; } while (view.nextResponder) { [str appendFormat:@"%@,", NSStringFromClass(view.class)]; if ([view.class isSubclassOfClass:[UIViewController class]]) { break; } view = (id)view.nextResponder; } self.xq_identifier_ka = [self md5String:[NSString stringWithFormat:@"%@",str]]; // self.xq_identifier_ka = [NSString stringWithFormat:@"%@",str]; }// } return self.xq_identifier_ka;}// UIView 分类(NSString *)obtainSameSuperViewSameClassViewTreeIndexPat{ NSString *classStr = NSStringFromClass([self class]); //cell的子view //UITableView 特殊的superview (UITableViewContentView) //UICollectionViewCell BOOL shouldUseSuperView = ([classStr isEqualToString:@“UITableViewCellContentView”]) || ([[self.superview class] isKindOfClass:[UITableViewCell class]])|| ([[self.superview class] isKindOfClass:[UICollectionViewCell class]]); if (shouldUseSuperView) { return [self obtainIndexPathByView:self.superview]; }else { return [self obtainIndexPathByView:self]; }}(NSString )obtainIndexPathByView:(UIView )view{ NSInteger viewTreeNodeDepth = NSIntegerMin; NSInteger sameViewTreeNodeDepth = NSIntegerMin; NSString *classStr = NSStringFromClass([view class]); NSMutableArray *sameClassArr = [[NSMutableArray alloc]init]; //所处父view的全部subviews根节点深度 for (NSInteger index =0; index < view.superview.subviews.count; index ++) { //同类型 if ([classStr isEqualToString:NSStringFromClass([view.superview.subviews[index] class])]){ [sameClassArr addObject:view.superview.subviews[index]]; } if (view == view.superview.subviews[index]) { viewTreeNodeDepth = index; break; } } //所处父view的同类型subviews根节点深度 for (NSInteger index =0; index < sameClassArr.count; index ++) { if (view == sameClassArr[index]) { sameViewTreeNodeDepth = index; break; } } return [NSString stringWithFormat:@"%ld",sameViewTreeNodeDepth]; }## 数据的上传数据通过上面的办法收集完了,那么如何及时、高效的上传到后端,给运营分析、处理呢?App 运行期间用户会点击非常多的数据,如果实时上传的话对于网络的利用率较低,所以需要考虑一个机制去控制用户产生的埋点数据的上传。思路是这样的。对外部暴露出一个接口,用来将产生的数据往数据中心存储。用户产生的数据会先保存到 AppMonitor 的内存中去,设置一个临界值(memoryEventMax = 50),如果存储的值达到设置的临界值 memoryEventMax,那么将内存中的数据写入文件系统,以 zip 的形式保存下来,然后上传到埋点系统。如果没有达到临界值但是存在一些 App 状态切换的情况,这时候需要及时保存数据到持久化。当下次打开 App 就去从本地持久化的地方读取是否有未上传的数据,如果有就上传日志信息,成功后删除本地的日志压缩包。App 应用状态的切换策略如下:- didFinishLaunchWithOptions:内存日志信息写入硬盘- didBecomeActive:上传- willTerimate:内存日志信息写入硬盘- didEnterBackground:内存日志信息写入硬盘// 将App日志信息写入到内存中。当内存中的数量到达一定规模(超过设置的内存中存储的数量)的时候就将内存中的日志存储到文件信息中(void)joinEvent:(NSDictionary *)dictionary{if (dictionary) { NSDictionary *tmp = [self createDicWithEvent:dictionary]; if (!s_memoryArray) { s_memoryArray = [NSMutableArray array]; } [s_memoryArray addObject:tmp]; if ([s_memoryArray count] >= s_flushNum) { [self writeEventLogsInFilesCompletion:^{ [self startUploadLogFile]; }]; }}}// 外界调用的数据传递入口(App埋点统计)(void)traceEvent:(AMStatisticEvent *)event{// 线程锁,防止多处调用产生并发问题@synchronized (self) { if (event && event.userInfo) { [self joinEvent:event.userInfo]; }}}// 将内存中的数据写入到文件中,持久化存储(void)writeEventLogsInFilesCompletion:(void(^)(void))completionBlock{NSArray *tmp = nil;@synchronized (self) { tmp = s_memoryArray; s_memoryArray = nil;}if (tmp) { __weak typeof(self) weakSelf = self; dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{ NSString *jsonFilePath = [weakSelf createTraceJsonFile]; if ([weakSelf writeArr:tmp toFilePath:jsonFilePath]) { NSString *zipedFilePath = [weakSelf zipJsonFile:jsonFilePath]; if (zipedFilePath) { [AppMonotior clearCacheFile:jsonFilePath]; if (completionBlock) { completionBlock(); } } } });}}// 从App埋点统计压缩包文件夹中的每个压缩包文件上传服务端,成功后就删除本地的日志压缩包(void)startUploadLogFile{NSArray *fList = [self listFilesAtPath:[self eventJsonPath]];if (!fList || [fList count] == 0) { return;}[fList enumerateObjectsUsingBlock:^(id obj, NSUInteger idx, BOOL *stop) { if (![obj hasSuffix:@".zip"]) { return; } NSString *zipedPath = obj; unsigned long long fileSize = [[[NSFileManager defaultManager] attributesOfItemAtPath:zipedPath error:nil] fileSize]; if (!fileSize || fileSize < 1) { return; } [self uploadZipFileWithPath:zipedPath completion:^(NSString *completionResult) { if ([completionResult isEqual:@“OK”]) { [AppMonotior clearCacheFile:zipedPath]; } }];}];}总结下来关键步骤:1. hook 系统的各种事件(UIResponder、UITableView、UICollectionView代理事件、UIControl事件、UITapGestureRecognizers)、hook 应用程序、控制器生命周期。在做本来的逻辑之前添加额外的监控代码2. 对于点击的元素按照视图树生成对应的唯一标识(addCartButton.GoodsView.GoodsViewController) 的 md5 值3. 在业务开发完毕,进入埋点的编辑模式,将 md5 和关键的页面的关键事件(运营、产品想统计的关键模块:App层级、业务模块、关键页面、关键操作)给绑定起来。比如 addCartButton.GoodsView.GoodsViewController.tbApp 对应了 tbApp-商城模块-商品详情页-加入购物车功能。4. 将所需要的数据存储下来5. 设计机制等到合适的时机去上传数据 ...

April 2, 2019 · 4 min · jiezi

你会如何存储用户的一些敏感信息,如登录的token

使用keychain来存储,也就是钥匙串,使用keychain需要导入Security框架iOS的keychain服务提供了一种安全的保存私密信息(密码,序列号,证书等)的方式,每个ios程序都有一个独立的keychain存储。相对于 NSUserDefaults、文件保存等一般方式,keychain保存更为安全,而且keychain里保存的信息不会因App被删除而丢失,所以在 重装App后,keychain里的数据还能使用。从ios 3。0开始,跨程序分享keychain变得可行。如何需要在应用里使 用使用keyChain,我们需要导入Security.framework ,keychain的操作接口声明在头文件SecItem.h里。直接使用SecItem.h里方法操作keychain,需要写的代码较为复杂,为减轻 咱们程序员的开发,我们可以使用一些已经封装好了的工具类,下面我会简单介绍下我用过的两个工具类:KeychainItemWrapper和 SFHFKeychainUtils。自定义一个keychain的类CSKeyChain.h@interface CSKeyChain : NSObject+ (NSMutableDictionary *)getKeychainQuery:(NSString *)service;+ (void)save:(NSString *)service data:(id)data;+ (id)load:(NSString *)service;+ (void)delete:(NSString *)service;@endCSKeyChain.m#import “CSKeyChain.h”#import<Security/Security.h>@implementation CSKeyChain+ (NSMutableDictionary *)getKeychainQuery:(NSString *)service { return [NSMutableDictionary dictionaryWithObjectsAndKeys: (__bridge_transfer id)kSecClassGenericPassword,(__bridge_transfer id)kSecClass, service, (__bridge_transfer id)kSecAttrService, service, (__bridge_transfer id)kSecAttrAccount, (__bridge_transfer id)kSecAttrAccessibleAfterFirstUnlock,(__bridge_transfer id)kSecAttrAccessible, nil];}+ (void)save:(NSString *)service data:(id)data { // 获得搜索字典 NSMutableDictionary *keychainQuery = [self getKeychainQuery:service]; // 添加新的删除旧的 SecItemDelete((__bridge_retained CFDictionaryRef)keychainQuery); // 添加新的对象到字符串 [keychainQuery setObject:[NSKeyedArchiver archivedDataWithRootObject:data] forKey:(__bridge_transfer id)kSecValueData]; // 查询钥匙串 SecItemAdd((__bridge_retained CFDictionaryRef)keychainQuery, NULL);}+ (id)load:(NSString *)service { id ret = nil; NSMutableDictionary *keychainQuery = [self getKeychainQuery:service]; // 配置搜索设置 [keychainQuery setObject:(id)kCFBooleanTrue forKey:(__bridge_transfer id)kSecReturnData]; [keychainQuery setObject:(__bridge_transfer id)kSecMatchLimitOne forKey:(__bridge_transfer id)kSecMatchLimit]; CFDataRef keyData = NULL; if (SecItemCopyMatching((__bridge_retained CFDictionaryRef)keychainQuery, (CFTypeRef *)&keyData) == noErr) { @try { ret = [NSKeyedUnarchiver unarchiveObjectWithData:(__bridge_transfer NSData *)keyData]; } @catch (NSException *e) { NSLog(@“Unarchive of %@ failed: %@”, service, e); } @finally { } } return ret;}+ (void)delete:(NSString *)service { NSMutableDictionary *keychainQuery = [self getKeychainQuery:service]; SecItemDelete((__bridge_retained CFDictionaryRef)keychainQuery);}@end在别的类实现存储,加载,删除敏感信息方法// 用来标识这个钥匙串static NSString * const KEY_IN_KEYCHAIN = @“com.cs.app.allinfo”;// 用来标识密码static NSString * const KEY_PASSWORD = @“com.cs.app.password”;+ (void)savePassWord:(NSString *)password { NSMutableDictionary *passwordDict = [NSMutableDictionary dictionary]; [passwordDict setObject:password forKey:KEY_PASSWORD]; [CSKeyChain save:KEY_IN_KEYCHAIN data:passwordDict];}+ (id)readPassWord { NSMutableDictionary *passwordDict = (NSMutableDictionary *)[CSKeyChain load:KEY_IN_KEYCHAIN]; return [passwordDict objectForKey:KEY_PASSWORD];}+ (void)deletePassWord { [CSKeyChain delete:KEY_IN_KEYCHAIN];}原文更多:iOS面试题大全 ...

April 1, 2019 · 1 min · jiezi

Deep/Shallow Copy

原文链接拷贝是我们在开发中经常使用的技巧,这里指的不是到Github上去复制粘贴代码,而是对内存中对象的操作 (逃深拷贝与浅拷贝的区别 ? 深拷贝是指我们拷贝出来的对象拥有自己单独的内存地址,修改新对象不影响源对象,浅拷贝指的是在copy指针的引用,修改新对象会影响到源对象在ObjC里面主要有两个方法对对象进行拷贝- (id)copy;- (id)mutableCopy;要对象能够使用这两个方法需要遵守协议 NSCopying, NSMutableCopying那么,该何时使用这两种方法呢, 先说结论,只有不可变对象调用copy方法的时候才是浅拷贝,其他情况均为深拷贝新建一个工程验证一下吧Xcode -> New -> MacOS -> CommandLine -> main.m由于NSString 同时实现了 NSCopying, NSMutableCopying 两个协议,我们就用他来做实验NSString *str1 = @“str1”;NSString *str2 = str1.copy;NSString *str3 = str1.mutableCopy;NSLog(@"%p %p %p",str1, str2, str3);运行之后可以看到如下输出0x1000020b8 0x1000020b8 0x100508e00由此可以得出结论,不可变对象使用 mutableCopy 为深拷贝 ,copy 为浅拷贝下面验证一下可变对象 NSMutableStringNSMutableString *str1 = [NSMutableString stringWithFormat:@“str1”];NSString *str2 = str1.copy;NSString *str3 = str1.mutableCopy;NSLog(@"%p %p %p",str1, str2, str3);0x100505260 0x50602036b4ec25f 0x100505290得出结论,可变对象无论使用 copy 还是 mutableCopy ,均为深拷贝这些是非集合类型的拷贝结论,那么对于集合类型来说呢新建Person类Person.h#import <Foundation/Foundation.h>NS_ASSUME_NONNULL_BEGIN@interface Person : NSObject@property (nonatomic ,copy) NSString *name;@property (nonatomic ,assign) NSUInteger age;@property (nonatomic ,copy) NSArray<Person *> *friends;@endNS_ASSUME_NONNULL_ENDPerson.m#import “Person.h”@implementation Person- (instancetype)init { self = [super init]; if (self) { _name = @""; _age = 0; _friends = @[]; } return self;}// 重写以便打印对象的属性- (NSString *)description { return [NSString stringWithFormat:@"- name: %@, age: %ld, friends: %@",self.name, self.age, self.friends];}@end如果正常按照上面的来想,集合类型应该也是一样 只有不可变对象调用copy方法的时候才是浅拷贝,其他情况均为深拷贝下面创建Array来验证想法Person *Alice = Person.new;Alice.name = @“Alice”;Alice.age = 18;NSArray *arr1 = @[Alice];NSArray *arr2 = arr1.copy;NSArray *arr3 = arr1.mutableCopy;NSLog(@"%p %p %p",arr1, arr2, arr3);0x1030064c0 0x1030064c0 0x103004a90NSArray吻合我们上面的结论,下面看下NSMutableArrayPerson *Alice = Person.new;Alice.name = @“Alice”;Alice.age = 18;NSMutableArray *arr1 = [NSMutableArray arrayWithArray:@[Alice]];NSArray *arr2 = arr1.copy;NSArray *arr3 = arr1.mutableCopy;NSLog(@"%p %p %p",arr1, arr2, arr3);0x1030051f0 0x103006a00 0x1030054a0也温和上面的结论,但是稍等NSMutableArray *arr1 = [NSMutableArray arrayWithArray:@[Alice]];NSArray arr2 = arr1.copy;NSLog(@"%p %p",arr1.firstObject, arr2.firstObject);我们可以打印arr1 和 arr2 的第一个元素的地址,发现他们还是指向同一个地址!查看Apple关于DeepCopy的文档我们发现,原来要对一个集合类型实现真正的深拷贝需要用这种方法, 而上面这种官方名称为【单层深拷贝】NSArray trueDeepCopyArray = [NSKeyedUnarchiver unarchiveObjectWithData:[NSKeyedArchiver archivedDataWithRootObject:oldArray]];好,那么我们来改造代码Person *Alice = Person.new;Alice.name = @“Alice”;Alice.age = 18;NSError *err;NSMutableArray *arr1 = [NSMutableArray arrayWithArray:@[Alice]];NSArray *arr2 = [NSKeyedUnarchiver unarchivedObjectOfClass:[NSArray class] fromData:[NSKeyedArchiver archivedDataWithRootObject:arr1 requiringSecureCoding:false error:&err] error:&err];if (err) { NSLog(@"%@",err);}NSLog(@"%p %p",arr1.firstObject, arr2.firstObject);奇怪的是输出arr2为nil,看一下控制台输出-[Person encodeWithCoder:]: unrecognized selector sent to instance 0x100706ac0原来是我们的Person没有遵守协议 NSCoding , 除此之外我们还需要遵守 NSSecureCoding 协议改造后的Person.m#import “Person.h”@interface Person () <NSCoding, NSSecureCoding>@end@implementation Person+ (BOOL)supportsSecureCoding { return true;}- (void)encodeWithCoder:(NSCoder *)aCoder { [aCoder encodeObject:_name forKey:@“name”]; [aCoder encodeInteger:_age forKey:@“age”];}- (instancetype)initWithCoder:(NSCoder *)aDecoder { self = [super init]; if (self) { self.name = [aDecoder decodeObjectForKey:@“name”]; self.age = [aDecoder decodeIntegerForKey:@“age”]; } return self;}- (instancetype)init { self = [super init]; if (self) { _name = @""; _age = 0; _friends = @[]; } return self;}现在我们打开main.m试一下Person *Alice = Person.new;Alice.name = @“Alice”;Alice.age = 18;NSError *err;NSMutableArray *arr1 = [NSMutableArray arrayWithArray:@[Alice]];NSArray *arr2 = [NSKeyedUnarchiver unarchivedObjectOfClasses:[NSSet setWithObjects:[NSArray class],[Person class], nil] fromData:[NSKeyedArchiver archivedDataWithRootObject:arr1 requiringSecureCoding:true error:&err] error:&err];if (err) { NSLog(@"%@",err);}NSLog(@"%p %p",arr1.firstObject, arr2.firstObject);0x100709880 0x100705c60我们发现数组里面的对象地址也全都不一样了,这才是集合类型的真正深拷贝thanks. ...

March 31, 2019 · 2 min · jiezi