关于ios:iOS-view生命周期

2019/01/07 22:11 一、view生命周期 1、loadViewloadView办法负责创立UIViewController的view,每次拜访UIViewController的view,比方controller.view、self.view,且view为nil,就会调用loadView办法。 对于[super loadView]中的默认实现:(1)首先去查找与UIViewController相关联的xib文件,通过加载xib文件来创立UIViewController的view;(2)如果没有找到关联的xib文件,就会创立一个空白的UIView,而后赋值给UIViewController的view属性。 2、viewDidLoadloadView实现view创立后,会调用此办法,一般来说,咱们会在此处进行初始化的相干操作。 3、viewWillAppear试图行将呈现。 4、viewWillLayoutSubviews倡议布局试图在此处进行,这个办法会对所有试图进行真正的布局,包含self.view。 大部分状况下试图的布局代码写在viewDidLoad中没有问题,因为最终也是到这里进行布局操作的;然而多数状况下,这会导致一些奇怪的问题,尤其对于XIB而言,因为在 viewDidLoad 和 viewWillAppear 中,试图还是默认XIB的大小,在此办法中才会布局正确。 5、viewDidLayoutSubviews试图布局实现。 6、viewDidAppear试图曾经呈现。 7、viewWillDisappear试图行将隐没。 8、viewDidDisappear试图曾经隐没。 9、viewDidUnload这个比拟非凡。 设施的内存是无限的,如果应用程序占用的内存过多,零碎就会对应用程序收回内存正告,UIViewController就会收到didReceiveMemoryWarning音讯。 didReceiveMemoryWarning办法的默认实现是:如果以后UIViewController的view不在应用程序的视图层次结构(View Hierarchy)中,即view的superview为nil的时候,就会将view开释,并且调用viewDidUnload办法。 所以个别在开释资源,次要是开释界面元素相干的资源,将相干的实例都赋值为nil - (void)viewDidUnload { [super viewDidUnload]; self.name = nil; self.pwd = nil; }二、xib1、initWithCoder:将援用和内容文件从xib文件中加载到内存中 2、setValue:forKey:将outlet中制订的各个新属性 3、addTarget:action:forControlEvents:将xib中定制的手势交互等办法进行解决 4、bind:toObject:withKeyPath:options:连贯对象 5、awakeFromNib加载结束。

July 27, 2020 · 1 min · jiezi

关于ios:P3色域图片crash问题

问题形容:电脑上间接给手机装的包所有OK,然而App Store商店下载的包在9.0.2和9.2零碎上一点就闪退,要么进了利用后轻易点点就闪退. 起因排查:因为Release版的包敞开了日志,在控制台难以看到有用的信息.所以,只能去Xcode看iTunes统计的crash日志. 关上Xcode->Window->Organizer->Crashes,右边App Store抉择对应版本,期待日志下载实现. 找到crash的中央.能够看到crash的用户的零碎iOS9.2和型号iPhone 6s Plus. 选中crash那行,如果不是公有API的话左边会有一个箭头,点击箭头会跳转到代码中crash的中央,这样就能够找到问题所在了. 但如果是公有API就没有这箭头了,这时候点击左边的Open in Project,而后抉择对应的我的项目,这时候能够在我的项目中看到crash中央的堆栈调用; -[CUIStrucTuredThemeStore renditionWithKey:usingKeySignature:] 这是个苹果公有API,查寻材料发现此API报错指向图片色域问题. 解决方案:从iTunes下载我的项目的 ipa 并解压, 找到 Payload 中的 .app 文件, 显示包内容. 找到 Assets.car 文件, copy 到 Work 门路下,在终端执行命令 sudo xcrun --sdk iphoneos assetutil --info ./Assets.car > asset.json JerodMac:~ jerod$ cd /Users/jerod/Documents/Work JerodMac:Work jerod$ lsAssets.carJerodMac:Work jerod$ sudo xcrun --sdk iphoneos assetutil --info ./Assets.car > asset.jsonPassword:JerodMac:Work jerod$ 明码是电脑的登录明码. 执行实现后 Work 文件下会生成 asset.json 文件, 抉择文本形式或Sublime形式关上, 查问"DisplayGamut" : "P3" ...

July 27, 2020 · 1 min · jiezi

关于ios:ipa重签名

文章写于2016/07/13 19:34,搬家到思否。将一个ipa文件解压后,显示包内容,能够看到app文件目录下蕴含了这2个文件:_CodeSignature(ipa的签订文件)和embedded.mobileprovision(证书配置文件),另外还有一个本人配置的entitlements.plist受权文件,它们就是重签名的要害。 为了不便形容,先定义一些文件名(请依据本人的状况批改): 假如你的证书名为 myInhouse. mobileprovision 假如你的inhouse签订名为 iPhone Distribution: ABCD technology co., LTD. 假如你的ipa包名为 efg.ipa 一、查看证书信息、配置权限首先,查看myInhouse. mobileprovision的信息,在终端输出命令: security cms -D -i myInhouse.mobileprovision执行后输入信息中的内容,失去一个 XML 格局信息,找到Entitlements字段; 而后,创立一个 entitlements.plist 文件,依照上图中Entitlements的信息配置,如下图: 二、编写resign.sh脚本编写脚本,命名为 resign.sh #!/bin/shif ! ([ -f "$1" ]); thenecho ----- \"${1}\"文件不存在exitfiipaName=${1%.ipa}if [ "$ipaName" = "$1" ]; thenecho ----- \"${1}\"error 不是ipa文件exitfi## step 1 解压ipaunzip ${ipaName}.ipa## step 2 删除旧签名文件 rm -rf Payload/*.app/_CodeSignature/## step 3 拷贝证书配置和权限文件cp myInhouse.mobileprovision Payload/*.app/embedded.mobileprovisioncp entitlements.plist Payload/*.app/## step 4 重签名(/usr/bin/codesign -f -s "iPhone Distribution: ABCD technology co., LTD." --entitlements Payload/*.app/entitlements.plist Payload/*.app/) || { rm -rf Payload/ rm -rf __MACOSX/exit }## step 5 打包zip -r ${ipaName}_resign.ipa Payload/rm -rf Payload/rm -rf __MACOSX/三、签名脚本写完后,将 entitlements.plist 、myInhouse.mobileprovision 、resign.sh 、efg.ipa 放到同一目录ipaResign下, ...

July 27, 2020 · 1 min · jiezi

关于ios:ipa重签名

文章写于2016/07/13 19:34,搬家到思否。将一个ipa文件解压后,显示包内容,能够看到app文件目录下蕴含了这2个文件:_CodeSignature(ipa的签订文件)和embedded.mobileprovision(证书配置文件),另外还有一个本人配置的entitlements.plist受权文件,它们就是重签名的要害。 为了不便形容,先定义一些文件名(请依据本人的状况批改): 假如你的证书名为 myInhouse. mobileprovision 假如你的inhouse签订名为 iPhone Distribution: ABCD technology co., LTD. 假如你的ipa包名为 efg.ipa 一、查看证书信息、配置权限首先,查看myInhouse. mobileprovision的信息,在终端输出命令: security cms -D -i myInhouse.mobileprovision执行后输入信息中的内容,失去一个 XML 格局信息,找到Entitlements字段; 而后,创立一个 entitlements.plist 文件,依照上图中Entitlements的信息配置,如下图: 二、编写resign.sh脚本编写脚本,命名为 resign.sh #!/bin/shif ! ([ -f "$1" ]); thenecho ----- \"${1}\"文件不存在exitfiipaName=${1%.ipa}if [ "$ipaName" = "$1" ]; thenecho ----- \"${1}\"error 不是ipa文件exitfi## step 1 解压ipaunzip ${ipaName}.ipa## step 2 删除旧签名文件 rm -rf Payload/*.app/_CodeSignature/## step 3 拷贝证书配置和权限文件cp myInhouse.mobileprovision Payload/*.app/embedded.mobileprovisioncp entitlements.plist Payload/*.app/## step 4 重签名(/usr/bin/codesign -f -s "iPhone Distribution: ABCD technology co., LTD." --entitlements Payload/*.app/entitlements.plist Payload/*.app/) || { rm -rf Payload/ rm -rf __MACOSX/exit }## step 5 打包zip -r ${ipaName}_resign.ipa Payload/rm -rf Payload/rm -rf __MACOSX/三、签名脚本写完后,将 entitlements.plist 、myInhouse.mobileprovision 、resign.sh 、efg.ipa 放到同一目录ipaResign下, ...

July 27, 2020 · 1 min · jiezi

关于ios:移动App入侵与逆向破解技术-iOS篇

转载于2016/07/25,搬迁到思否。如果您有急躁看完这篇文章,您将懂得如何着手进行app的剖析、追踪、注入等实用的破解技术,另外,通过“入侵”,将帮忙您了解如何躲避常见的安全漏洞,文章纲要: 简略介绍ios二进制文件构造与入侵的原理介绍入侵罕用的工具和办法,包含pc端和手机端解说黑客技术中的动态剖析和动静分析法通过一个简略的实例,来介绍如何综合使用砸壳、寻找注入点、lldb近程调试、追踪、反汇编技术来进行黑客实战解说越狱破解补丁和不需越狱的破解补丁制作方法和差异黑客的素养敏锐的嗅觉 有时候通过一个函数名,一个类名,就能大抵的判断出它的作用,这就是嗅觉;功力已臻化境时,甚至能够应用第六感判断出一些注入点面对失败的勇气 破解有时候很耗时,和程序开发正好相同,它耗时不是耗在写代码上,而是耗在寻找注入点和逆向工程上,有可能你花了3天工夫去找程序的漏洞,然而最终的破解代码可能就2行,不到一分钟就搞定了;然而你也须要做好面对失败的筹备,如果路选错了,有可能你这3天齐全是在节约脑细胞洪荒之力 洪荒之力-即入侵过程中须要借助的各种工具,工欲善其事,必先利其器,工具都是前人智慧的结晶,能用工具解决的,绝不要手动去搞iOS黑客关键字iOS的入侵离不开越狱开发,所有的破解、入侵都是建设在越狱的根底上的,如果没有拿到零碎级权限,所有的想法都是空谈了,当然,市面上存在免越狱的破解补丁,然而它的开发过程,也是基于越狱环境的 tweak在iOS的黑客界,要做破解或越狱开发,就必须理解tweak,它是各种破解补丁的统称,在google上,如果你想搜寻一些越狱开发材料或者开源的破解补丁代码,它是最好的关键字。 iOS的tweak大抵分为两种: 第一种是在cydia上公布的,须要越狱能力装置,大部分是deb格局的安装包,iOS在越狱后,会默认装置一个名叫mobilesubstrate的动静库,它的作用是提供一个零碎级的入侵管道,所有的tweak都能够依赖它来进行开发,目前支流的开发工具有theos和iOSOpenDev,前者是采纳makefile的一个编译框架,后者提供了一套xcode我的项目模版,能够间接应用xcode开发可调试,然而这个我的项目曾经进行更新了,对高版本的xcode反对不好,大家酌情抉择(本文中的例子全副采纳theos)第二种是间接打包成ipa安装包,并应用本人的开发证书或者企业证书签名,不需越狱也能够装置,可间接放到本人的网站上,可实现在线装置;对于没有越狱的手机,因为权限的限度,咱们是没有方法写零碎级的tweak的,例如springboard的补丁是没法运行的,这种tweak大多是针对某个app,把指标app进行批改注入解决,再从新签名和公布,有点相似于windows软件的xxx破解版、xxx免注册版没有越狱的机器因为零碎中没有mobilesubstrate这个库,咱们有二个抉择,第一个是间接把这个库打包进ipa当中,应用它的api实现注入,第二个是间接批改汇编代码;第一个实用于较为简单的破解行为,而且越狱tweak代码能够复用,第二种实用于破解一些if…else…之类的条件语句 Mobilesubstrate上面的图展现的就是oc届驰名的method swizzling技术,他就是iOS的注入原理,相似于windows的钩子,所以咱们注入也称为hook Mobilesubstrate为了不便tweak开发,提供了三个重要的模块: MobileHooker 就是用来做下面所说的这件事的,它定义一系列的宏和函数,底层调用objc-runtime和fishhook来替换零碎或者指标利用的函数MobileLoader 用来在目标程序启动时依据规定把指定目录的第三方的动静库加载进去,第三方的动静库也就是咱们写的破解程序,他的原理上面会简略解说一下Safe mode 相似于windows的平安模式,比方咱们写的一些零碎级的hook代码产生crash时,mobilesubstrate会主动进入平安模式,平安模式下,会禁用所有的第三方动静库app注入原理下面讲到了mobileloader,他是怎么做到把第三方的lib注入进目标程序的呢?这个咱们要从二进制文件的构造说起,从上面的图来看,Mach-O文件的数据主体可分为三大部分,别离是头部(Header)、加载命令(Load commands)、和最终的数据(Data)。mobileloader会在目标程序启动时,会依据指定的规定查看指定目录是否存在第三方库,如果有,则会通过批改二进制的loadCommands,来把本人注入进所有的app当中,而后加载第三方库。 为了让大家看的更分明,上面我用machoview来关上一个实在的二进制文件给大家看看,能够看出,二进制当中所有援用到的动静库都放在Load commands段当中,所以,通过给这个段减少记录,就能够注入咱们本人写的动静库了 那么问题来了,在这里插入咱们本人的动静库有什么用?咱们本人写的代码没有执行的入口,咱们一样没发干坏事,嗯,祝贺你问到点子上了,咱们还须要一个"main"函数来执行咱们本人的代码,这个"main"函数在oc外面称为构造函数,只有在函数前申明 “attribute((constructor)) static” 即可,有了它咱们就能够施展想象力,进行偷天换日干点好事了: #import <CaptainHook/CaptainHook.h>CHDeclareClass(AnAppClass);CHMethod(1, void, AnAppClass, say, id, arg1){ NSString* tmp=@"Hello, iOS!"; CHSuper(1, AnAppClass, say, tmp);}__attribute__((constructor)) static void entry(){ NSLog(@"Hello, Ice And Fire!"); CHLoadLateClass(AnAppClass); CHClassHook(1, AnAppClass,say);} 到这里为止,咱们曾经晓得了怎么在目标程序注入本人的代码,那么咱们怎么晓得须要hook哪些办法?怎么找到关键点进行理论的破解呢?上面讲一下常见的app入侵分析方法 iOS逆向分析方法逆向剖析最罕用的有三种办法: 网络分析 通过剖析和篡改接口数据,能够无效的破解通过接口数据来管制客户端行为的app,罕用的抓包工具备Tcpdump, WireShark, Charles等,windows平台有fidller动态剖析 通过砸壳、反汇编、classdump头文件等技术来剖析app行为,通过这种形式能够无效的剖析出app实用的一些第三方库,甚至剖析出app的架构等内容,罕用的工具有dumpdecrypted(砸壳)、hopper disassembler(反汇编)、class_dump(导头文件)动态分析 有静就有动,万物都是相生相克的,动态分析指的是通过剖析app的运行时数据,来定位注入点或者获取要害数据,罕用的工具有cycript(运行时控制台)、 lldb+debugserver(近程断点调试)、logify(追踪)demo:微信抢红包插件下面讲了很多原理性的货色,置信大家曾经看的不耐烦了,上面咱们一起动点真格的,咱们从头开始,一步一步的做一个微信的主动抢红包插件,当然,网上可能曾经有相干的开源代码了,然而我这里要讲的是,这些代码是怎么得进去的,我么重点讲一讲剖析过程 工欲善其事,必先利其器一台越狱的手机,并装有以下软件 cycriptdumpdecrypteddebug serveropenssh一台苹果电脑,并装有以下软件 class_dumpTheosHopper Disassembler v3xcodeinsert_dylibpp助手寻找注入点砸壳首先咱们要做的就是把微信的壳砸掉,砸壳其实是为了把它的头文件classdump进去,因为从appstore下载的app二进制都是通过加密的,间接进行classdump操作是啥也看不出来的 ...

July 27, 2020 · 2 min · jiezi

关于ios:iOS封装framework

文章写于2016/04/08,搬家到此处第一篇 iOS封装Framework如果咱们心愿与他人共享某些函数,却又不违心裸露实现的细节,怎么办呢?这时候能够将咱们的代码封装成framework,对外提供接口而不裸露实现;不仅如此,将代码整合成framework还有很多其余的益处,这里就不一一列举。 上面就来看看如何打包成 framework 吧。 1、 创立工程 通过OS X > Framework&Library > Bundle 创立工程: 2、增加 Headers 在 Build Phases 中增加 Headers: Headers 开展后是这样的: 3、增加我的项目代码 将须要封装打包的文件退出我的项目中,这里最好用 copy 的形式退出: 4、合并头文件 为了不便他人应用 framework,最好创立一个头文件,并且在头文件下蕴含你所有想要公开的类,如此,当他人应用你的 framework 时只须要导入这一个头文件就能够了。 5、公开类 当代码退出了我的项目后,所有的 .h 文件都会主动呈现在 Headers 的 Project 上面,而后,咱们将须要公开的类拖到 Public 上面。 这里须要留神:Private 依然是公开的而不是公有的,不要被它的名字误导了,公有类保留在 Project 下就好。 6、更改一些设置: 6.1 info.plist > Bundle OS Type code = FMWK 6.2 Build Settings > Base SDK = Latest iOS,抉择最新的 ...

July 27, 2020 · 3 min · jiezi

关于ios:iOS个人证书与企业证书

文章写于2017.05.11,搬家到此处一、cer证书与mobileprovision文件开发iOS须要cer证书和mobileprovision形容文件 1、.cer证书 开发者的信赖证书(相当于你的身份证)。 2、.mobileprovision文件 蕴含了cer证书、利用包名(Bundle Identifier)、设施ID。 只有装置了这个文件Xcode能力调试(留神:这里的mobileprovision如果是distribution(上传AppStore的证书)那么它是不能在真机上测试的,只有上传到AppStore通过了审核能力从appstore下载安装到iOS设施外面)。 一般来讲只有有了下面的这证书文件,就能够在真机上调试了。 3、.p12 如果须要将证书给他人应用,能够从本地钥匙串里抉择相应的证书导出为.p12证书发给他人。p12蕴含了下面的.cer证书与.mobileprovision文件。 (留神developer的证书只能用于测试;distribution证书只能用来上传AppStore,没上线之前不能装置到iOS设施;inhouse证书须要设施信赖)。 二、iOS二种证书2.1 $99这种账号能够用来上传App Store提审并公布。 这种账号有集体和公司的区别: 集体账号:在上架App Store后,开发者间接显示申请人姓名,集体应用,每一种Apple产品,均有各类设施各100台测试权限。iPhone、iPad、Mac等。 公司账号:上架App Store的App开发者显示公司,公司账号下,能够增加多个测试子账号,反对Xcode在真机测试,然而子账号没有上传App Store权限。与集体账号权限相似,均有各类设施各100台测试机权限,iPhone、iPad、Mac等。 2.2 $299这种账号只能用于企业外部应用,测试设施有限,然而不能用来上传app store, 也就是常说的in-house证书(用这种证书打进去的包能在任何iOS设施上运行,不须要苹果的验证、签名)。 2.3 异同99美元的能够配置Ad-Hoc证书、公布证书(提审AppStore的证书); 299美元的能够配置Ad-Hoc证书、In-House证书,不能配置提审AppStore的证书。不要误会了这种账号即能上传AppStore又能In-House,这是两种不同账号的性能。 2.3.1 开发/调试证书(Development) 1、不能公布到Apple Store进行销售。 2、不须要Apple评审。 3、能够应用任何已知的公有API。 4、能够装置到任何苹果的设施上,无需任何签名和认证。 5、用户装置只须要一个ipa文件,无需证书和签名文件。 2.3.2 公布证书(Distribution) App Store - $99 公布到AppStore; 其实就是咱们常说的公布证书:distribution证书,用此证书打的包能够上传到AppStore提审,审核通过后就能够在AppStore下面公布,而后所有人就能下载安装应用了。 须要留神的是,在AppStore公布你的app之前,任何非越狱ios设施都不能装置此证书的包,只有在AppStore公布后,能力让所有的设施装置。 Ad Hoc - $99, $299 公布到指定设施; 公布进去的包须要通过iTunes装置。 100台,因为苹果的限度,在开发者网站上只能增加100台设施; In House - $299 公布到公司外部; 明确几个概念 1、企业版IDP:即iOS Development Enterprise Program。留神是$299/Year那种。 2、In House: 是只企业外部公布,仅限企业内部人员应用。 In-House形式特点 ...

July 27, 2020 · 1 min · jiezi

关于ios:iOS个人证书与企业证书

文章写于2017.05.11,搬家到此处一、cer证书与mobileprovision文件开发iOS须要cer证书和mobileprovision形容文件 1、.cer证书 开发者的信赖证书(相当于你的身份证)。 2、.mobileprovision文件 蕴含了cer证书、利用包名(Bundle Identifier)、设施ID。 只有装置了这个文件Xcode能力调试(留神:这里的mobileprovision如果是distribution(上传AppStore的证书)那么它是不能在真机上测试的,只有上传到AppStore通过了审核能力从appstore下载安装到iOS设施外面)。 一般来讲只有有了下面的这证书文件,就能够在真机上调试了。 3、.p12 如果须要将证书给他人应用,能够从本地钥匙串里抉择相应的证书导出为.p12证书发给他人。p12蕴含了下面的.cer证书与.mobileprovision文件。 (留神developer的证书只能用于测试;distribution证书只能用来上传AppStore,没上线之前不能装置到iOS设施;inhouse证书须要设施信赖)。 二、iOS二种证书2.1 $99这种账号能够用来上传App Store提审并公布。 这种账号有集体和公司的区别: 集体账号:在上架App Store后,开发者间接显示申请人姓名,集体应用,每一种Apple产品,均有各类设施各100台测试权限。iPhone、iPad、Mac等。 公司账号:上架App Store的App开发者显示公司,公司账号下,能够增加多个测试子账号,反对Xcode在真机测试,然而子账号没有上传App Store权限。与集体账号权限相似,均有各类设施各100台测试机权限,iPhone、iPad、Mac等。 2.2 $299这种账号只能用于企业外部应用,测试设施有限,然而不能用来上传app store, 也就是常说的in-house证书(用这种证书打进去的包能在任何iOS设施上运行,不须要苹果的验证、签名)。 2.3 异同99美元的能够配置Ad-Hoc证书、公布证书(提审AppStore的证书); 299美元的能够配置Ad-Hoc证书、In-House证书,不能配置提审AppStore的证书。不要误会了这种账号即能上传AppStore又能In-House,这是两种不同账号的性能。 2.3.1 开发/调试证书(Development) 1、不能公布到Apple Store进行销售。 2、不须要Apple评审。 3、能够应用任何已知的公有API。 4、能够装置到任何苹果的设施上,无需任何签名和认证。 5、用户装置只须要一个ipa文件,无需证书和签名文件。 2.3.2 公布证书(Distribution) App Store - $99 公布到AppStore; 其实就是咱们常说的公布证书:distribution证书,用此证书打的包能够上传到AppStore提审,审核通过后就能够在AppStore下面公布,而后所有人就能下载安装应用了。 须要留神的是,在AppStore公布你的app之前,任何非越狱ios设施都不能装置此证书的包,只有在AppStore公布后,能力让所有的设施装置。 Ad Hoc - $99, $299 公布到指定设施; 公布进去的包须要通过iTunes装置。 100台,因为苹果的限度,在开发者网站上只能增加100台设施; In House - $299 公布到公司外部; 明确几个概念 1、企业版IDP:即iOS Development Enterprise Program。留神是$299/Year那种。 2、In House: 是只企业外部公布,仅限企业内部人员应用。 In-House形式特点 ...

July 27, 2020 · 1 min · jiezi

关于ios:前端面试每日-31-第468天

明天的知识点 (2020.07.27) —— 第468天 (我也要出题)[html] 如何敞开HTML页面在IOS下的键盘首字母主动大写?[css] 在css中哪个属性会影响DOM读取文档流的程序?[js] 应用canvas画一个小球自由落体的成果[软技能] 个别与git服务器连贯有http/ssh等,你用的是哪种形式?为什么?《论语》,曾子曰:“吾日三省吾身”(我每天屡次检查本人)。前端面试每日3+1题,以面试题来驱动学习,每天提高一点!让致力成为一种习惯,让奋斗成为一种享受!置信 保持 的力量!!!欢送在 Issues 和敌人们一起探讨学习! 我的项目地址:前端面试每日3+1【举荐】欢送跟 jsliang 一起折腾前端,零碎整顿前端常识,目前正在折腾 LeetCode,打算买通算法与数据结构的任督二脉。GitHub 地址 微信公众号欢送大家前来探讨,如果感觉对你的学习有肯定的帮忙,欢送点个Star, 同时欢送微信扫码关注 前端剑解 公众号,并退出 “前端学习每日3+1” 微信群互相交换(点击公众号的菜单:交换)。 学习不打烊,充电加油只为遇到更好的本人,365天无节假日,每天早上5点纯手工公布面试题(死磕本人,愉悦大家)。心愿大家在这虚夸的前端圈里,放弃沉着,保持每天花20分钟来学习与思考。在这变幻无穷,类库层出不穷的前端,倡议大家不要等到找工作时,才狂刷题,提倡每日学习!(不忘初心,html、css、javascript才是基石!)欢送大家到Issues交换,激励PR,感激Star,大家有啥好的倡议能够加我微信一起交换探讨!心愿大家每日去学习与思考,这才达到来这里的目标!!!(不要为了谁而来,要为本人而来!)交换探讨欢送大家前来探讨,如果感觉对你的学习有肯定的帮忙,欢送点个[Star]

July 27, 2020 · 1 min · jiezi

关于ios:iOS今日头条第3轮面试回忆

今日头条的iOS高级开发岗第三面,上面记录这次面试的回顾以作日后温习。一、自我介绍简略介绍一下你本人吧解析:简略介绍下本人的名字,教育背景,当初的工作,做过的我的项目二、自我介绍衍生的口头问题讲讲下你在你我的项目中做过的优化或者技术难点解析:介绍了本人封装的一个集picker,文本域的灵便开展的表视图。这个视图的数据源是json,怎么转成模型数组的?这个cell有哪些类型?展现的怎么辨别这些cell?这外面有用过复用机制吗?这些cell有实现过多重继承吗?题外话:这种问题最好各人本人找问题讲讲,不多,提前准备一个你我的项目中十分善于并相熟的点,即可。三、编程题:实现以下性能1) 编写一个自定义类:Person,父类为NSObject解析:头文件这样写 @interface Person:NSObject2) 该类有两个属性,内部只读的属性name,还有一个属性age解析:name的修饰符nonatomic,strong,readonly。age的修饰符nonatomic,copy。3) 为该类编写一个初始化办法 initWithName:(NSString *)nameStr,并根据该办法参数初始化name属性。解析:头文件申明该办法,实现文件实现该办法4) 如果两个Person类的name相等,则认为两个Person相等解析:重写isEqual,这外面波及到了哈希函数在iOS中的利用。四、由编程题衍生的口头题目4.1题目: 怎么实现内部只读的属性,让它不被内部篡改解析: 头文件用readonly润饰并申明该属性。失常状况下,属性默认是readwrite,可读写,如果咱们设置了只读属性,就表明不能应用setter办法。在.m文件中不能应用self.ivar = @"aa"; 只能应用实例变量_ivar = @"aa";,而外界想要批改只读属性的值,须要用到kvc赋值[object setValue:@"mm" forKey:@"ivar"];。实现文件外面申明公有属性,并在头文件在protocol外面规定该属性就能够了,内部通过protocol获取,这样还能够达到暗藏成员的成果。4.2题目: nonatomic是非原子操作符,为什么要这样,atomic为什么不行?有人说能atomic耗内存,你感觉呢?保读写平安吗,能保障线程平安吗?有的人说atomic并不能保障线程平安,你感觉他们的出发点是什么,你认同这个说法吗?对于为什么用nonatomic如果该对象无需思考多线程的状况,请退出这个属性润饰,这样会让编译器少生成一些互斥加锁代码,能够提高效率。 而atomic这个属性是为了保障程序在多线程状况下,编译器会主动生成一些互斥加锁代码,防止该变量的读写不同步问题。 atomic 和 nonatomic 的区别在于,零碎主动生成的 getter/setter 办法不一样。如果你本人写 getter/setter,那 atomic/nonatomic/retain/assign/copy 这些关键字只起提醒作用,写不写都一样。 对于atomic语nonatomic的实现苹果的官网文档 有解释,上面咱们举例子解释一下背地的原理。至于 nonatomic 的实现//@property(nonatomic, retain) UITextField *userName;//系统生成的代码如下:- (UITextField *) userName { return userName;}- (void) setUserName:(UITextField *)userName_ { [userName_ retain]; [userName release]; userName = userName_;}而 atomic 版本的要简单一些://@property(retain) UITextField *userName;//系统生成的代码如下:- (UITextField *) userName { UITextField *retval = nil; @synchronized(self) { retval = [[userName retain] autorelease]; } return retval;}- (void) setUserName:(UITextField *)userName_ { @synchronized(self) { [userName release]; userName = [userName_ retain]; }}简略来说,就是 atomic 会加一个锁来保障多线程的读写平安,并且援用计数会 +1,来向调用者保障这个对象会始终存在。如果不这样做,如有另一个线程调 setter,可能会呈现线程竞态,导致援用计数降到0,原来那个对象就开释掉了。 ...

July 24, 2020 · 1 min · jiezi

关于ios:厚积薄发开发期资源管理的策略选择

1)开发期资源管理的策略抉择 2)iOS14启动就Crash 3)IL2CPP加密:global-metadata.dat在iOS下的解密问题 4)如何实现可程序控制的3D动作 5)Unity Editor内Screen的Width和Height 这是第212篇UWA技术常识分享的推送。明天咱们持续为大家精选了若干和开发、优化相干的问题,倡议浏览工夫10分钟,认真读完必有播种。 UWA 问答社区:answer.uwa4d.com UWA QQ群2:793972859(原群已满员) ResourceQ1:因为打包须要用AssetBundle,然而开发中用AssetBundle不太敌对,以上两种加载都会有各自的问题: Resources:须要放在Resources文件夹下,打包不好解决。 AssetDatabase:没有异步加载办法,开发中没方法模仿一些须要异步加载的状况。 目前我的项目用的Resources,打包机分了两个我的项目,打APK的我的项目阉割了Resources,写了独自版本治理更新命令,然而仍然挺麻烦。大部分公司都是用的AssetDatabase,请问是否有一些办法能够模仿异步加载? 另外以上两种办法都无奈模仿实在的卸载状况,导致组员不合理操作时,打AssetBundle后会有内存透露或者失落援用的状况,能够说一些大家公司开发时用的资源管理计划吗? A1:用AssetDatabase能够加一些随机延时模仿异步,资源加载的接口能够封装一下,做到依据配置来决定走AssetDatabase还是走AssetBundle。咱们打包机会把打好的AssetBundle主动提交到SVN,能够随时更新下来,在编辑器下测AssetBundle比拟不便。 另外:新的Addressable也能够试试看。 感激littlesome@UWA问答社区提供了答复 A2:据说过一个项目组做法是:平时开发应用Resources来加载;打AssetBundle或者正式包时,按某些规定依据Resources的目录主动生成一个新工程,这样就不必保护两个工程了。感激张迪@UWA问答社区提供了答复 A3:之前是协程模仿AssetDatabase下的异步,即LoadFromFileAsync。前面尝试过一种办法,就是应用独自的美术工程解决AssetBundle,而后程序工程不负责任何资源,只应用美术工程打包生成的AssetBundle。初听下来流程比较复杂,一点点小的改变即须要操作2个Unity工程。然而其长处也是十分多的。 代码上能够保留AssetDatabase的加载局部用于调试,当然能在日常开发中应用AssetBundle大部分时候能把AssetBundle的Bug裸露在Editor端,而不是上了真机才出问题。 感激cloud@UWA问答社区提供了答复 A4:以前是用Resources来加载,只不过会把资源放到Editor/Resources目录下来防止被打进包内。而后要公布的时候切换Flag,并读取Editor/Resources目录下资源进行打AssetBundle包。不过当初全副交给Addressables来治理了。 感激黄程@UWA问答社区提供了答复 iOSQ:在iOS 14公布之后,就呈现闪退问题了。和机型没关,只有是iOS 14就必闪退的那种,iOS 13就失常。用的Unity版本是2018.2.3。求教有人遇到过吗? A:咱们iOS14也出了情况,有两个体现: 启动就闪退,多起几次能够过来。启动后到某个阶段之间卡死(必卡跳不过)。XCode里看,始终是GfxDriver报错,咱们渠道方发现,XCode里【BuildSetting - Packaging - Product Name】不能含有中文,有中文就出这个问题,改掉就好了。 根本原因尚不明确,集体猜想Product Name会影响Header Folder Path,可能是代码加载门路中呈现中文会出问题(相似晚期Unity的情况)。心愿对你有帮忙。 感激Walker@UWA问答社区提供了答复 iOSQ1:为了加大破解难度,须要对global-metadata.dat文件进行加密。 实现形式是: 1. 加密在导出Android和Xcode工程后进行,对global-metadata.dat按字节加密。 2. 批改: Android:\Editor\Data\il2cpp\libil2cpp\vm\MetadataLoader.cpp Mac:Unity.app\Contents\il2cpp\libil2cpp\vm\MetadataLoader.cpp 文件中的LoadMetadataFile函数,对从文件中读取进去的内容按字节解密。 当初呈现的问题是:在Android上没有问题,能够失常加密和运行游戏。然而在Mac上,貌似解密函数没有失效,导致启动游戏解体。 求教一下各位大佬:在Mac上,global-metadata.dat的读取是不是不是通过这个函数。我看到Mac上Unity装置目录还有一个PlaybackEngines文件夹,外面也有IL2CPP相干文件,例如MetadataLoader.h,但这个文件是只读的,无奈批改,而且也没有找到MetadataLoader.cpp。Mac上解密不失效和这个文件无关吗? A:iOS上是间接链接预编译好的PlaybackEngines\iOSSupport\Trampoline\Libraries\libil2cpp.a 可能须要本人把.a从工程里移除,把IL2CPP源码引入到工程里。Q2:工程是指iOS Support/Tramponline文件夹下的Xcode工程吗?要把Unity.app\Contents\il2cpp\libil2cpp中的文件全副退出到这个工程外面是吗? A:是领导出的我的项目工程。具体哪些文件可能须要本人试试了。能够在Windows上导个IL2CPP的VS工程对照一下文件和编译参数。Q3:咱们最近遇到这个iOS加密Metadata的问题,请问最初有找到解决方案吗?https://forum.unity.com/threa... 看这个帖子,仿佛是须要Unity源码才能够? A:IL2CPP的代码间接拖到XCode工程里应该就能够了。感激littlesome@UWA问答社区提供了答复 AnimationQ:咱们的需要是心愿用程序实现灵便的拍掌动作,如下图: 两头一个大模型,左右两手,两手掌要做拍掌动作,蓝色点都可作为拍掌点,就是说要让程序控制拍到哪里。目前想到的计划是: 1. 做很多个拍掌动画,而后在Blend Tree里用二维混合,但那样不够准确。 2. 用Final IK,管制两手掌静止,但那样做的动作不天然。 ...

July 24, 2020 · 1 min · jiezi

关于ios:Apple-Widget下一个顶级流量入口

0x00 前言2020 年 6 月 22 日,苹果召开了第一次线上的开发者大会 - WWDC20。这堪称是一次能够载入史册的发布会,发表了 ARM 架构 Mac 芯片、软硬件的生态大对立、iOS 14 零碎界面大改等一系列激动人心的音讯。 当然,最让我感兴趣的就是让 iOS 界面大改的 Widget 了。过来几年,iOS 的桌面交互体验堪称是一言难尽,Widget 的退出无疑是一次比拟大的破局。在看发布会的时候,我的脑海里就浮现出一个问题:“这会是下一个互联网公司竞争的流量入口吗?” 先不抛论断,让咱们先看一下 WWDC20 介绍了哪些对于 Widget 的新货色。 ( WWDC 2020精彩内容思否专栏:https://segmentfault.com/blog...   本篇内容来自于阿里巴巴淘系技术部,高级无线开发工程师柘剑。 更多精彩内容可关注【淘系技术】公众号。) 0x01 什么是 Widget?Widget 不是一个小型的 App,它是一种新的桌面内容展示模式,次要是用于补救主应用程序无奈及时展现用户所关怀的数据。如下图所示: 一个优良的 Widget 须要有三个特点:简单明了(Glanceable)、失当展现(Relevant)、个性化定制(Personalized) 简单明了(Glanceable)Widget 不是一个小型的 App,这句话被重复提起。个别用户每天进入主屏幕的次数超过 90 次,但停留的总时长不过几分钟。通常来说用户只会在主屏幕上停留片刻工夫,就会跳转到其余中央,所以并不需要任何简单的交互设计来加强 Widget 的作用,也不须要简单的款式来丰盛 Widget 的内容,简单明了的内容才是 Widget 的要害。 和安卓的 Widget 不太一样,苹果设计的 Widget 并不反对任何交互行为,也不倡议大家设计过于简单的款式来出现内容,这也十分合乎苹果对于主屏幕的改良始终放弃克服的特点。 失当展现(Relevant)苹果冀望 Widget 能够和正在执行或者思考的事件严密的联合。比方,早上起床,用户最关怀天气怎么样,Widget 能够展现一下天气情况;起床后,用户就要理解一下一天的行程,Widget 能够展现一下 Reminders 中的内容;等到一天忙完了,筹备睡觉的时候,能够用 Widget 关上音乐略微放松一下。为此,苹果零碎提供了一个叫智能叠放(Smart Stacks)的性能,智能叠放是一个 Widgets 的汇合。零碎会依据每个人的习惯,借助端智能的能力,主动的显示精确的 Widget 在最顶部。 ...

July 23, 2020 · 3 min · jiezi

关于ios:Metal新特性大幅度提升iOS端性能

前言Metal 是一个和 OpenGL ES 相似的面向底层的图形编程接口,通过应用相干的 api 能够间接操作 GPU ,最早在 2014 年的 WWDC 的时候公布。Metal 是 iOS 平台独有的,意味着它不能像 OpenGL ES 那样反对跨平台,然而它能最大的开掘苹果挪动设施的 GPU 能力,进行简单的运算,像 Unity 等游戏引擎都通过 Metal 对 3D 能力进行了优化, App Store 还有相应的使用 Metal 技术的游戏专题。 阿里巴巴淘系技术部的闲鱼团队是比拟早在客户端侧抉择Flutter计划的技术团队,以后的闲鱼工程里也是一个较为简单的Native-Flutter混合工程。作为一个2C的利用,性能和用户体验始终是闲鱼技术团队在开发中比拟关注的点。而Metal这样的间接操作GPU的底层接口无疑会给闲鱼技术团队突破性能瓶颈提供一些新的思路。 上面会具体论述一下这次大会Metal相干的新个性,以及对于闲鱼技术和整个淘系技术来说,这些新个性带来了哪些技术启发与思考。 ( WWDC 2020精彩内容思否专栏:https://segmentfault.com/blog...   本篇内容来自于阿里巴巴淘系技术部,无线开发工程师岑彧。 更多精彩内容可关注【淘系技术】公众号。) Metal相干新个性1.Harness Apple GPUs with Metal这一章其实次要介绍的是Apple GPU的在图形渲染上的原理和工作流,是一些比拟底层的硬件原理。当咱们应用Metal进行App或者是游戏的构建的时候,Metal会利用GPU的tile-based deferred rendering (TBDR)架构给利用和游戏带来十分可观的性能晋升。这一章次要就是介绍GPU的的架构和能力,以及TBDR架构进行图像渲染的原理和流程。总之就是号召开发者们应用Metal来构建利用和游戏。因为这个session没有波及到下层的软件开发,就不对视频的具体内容进行赘述了。详情可见:Harness Apple GPUs with Metal 2.Optimize Metal apps and games with GPU counters这一章次要介绍了Xcode中的GPU性能剖析工具Instrument,这个工具当初曾经反对了GPU的性能剖析。而后从多个方面剖析了GPU的性能瓶颈,以及性能瓶颈呈现时的优化点。总体来说就是通过性能剖析工具来优化咱们的App或者游戏,让整个画面更加晦涩。整个章节次要分为五个局部: 1.总体介绍这个环节次要是疾速回顾了一下Apple的GPU的架构和渲染流程。而后因为很多渲染工作都须要在不同的硬件单元上进行,例如ALU和TPU。他们对不同的吞吐量有着不同的度量。有很多GPU的性能指标须要被思考,所以推出了GPU性能计数器。这个计数器可能测量到GPU的利用率,过高和过低都会造成咱们的渲染性能瓶颈。对于计数器的具体应用,参考官网的video成果会更好:Optimize Metal apps and games with GPU counters(6:37~9:57),次要应用了Instrument工具,对于工具的全面具体的应用能够参考WWDC19的session videoGetting Started with Instruments ...

July 20, 2020 · 1 min · jiezi

关于ios:苹果开发者账号申请政府组织类2020

博客搬迁至此2020-01-15一、筹备资料1. 申请机构工作人员的根本信息(该工作人员作为申请人或者被授权人,必须是组织机构中的工作人员):姓名,工作职位(英文),电子邮箱,手机号码,生日。 2. 证实人或者授权人的根本信息(为组织机构中,有权威代表组织机构,个别填写科长或者副科长):姓名,工作职位(英文),电子邮箱,手机号码,生日。 3. 申请机构的根本信息:机构名称(英文),地址(英文),官网网址,联系电话。 4. 反对VISA或者万事达的信用卡(这里因为波及平安码等问题,付费问题也能够由客户自行付费) 二、注册Apple ID如果您曾经领有Apple ID,跳过此步骤。 1. 注册地址: https://appleid.apple.com/account#!&page=create 填写好信息,确认无误后点击“持续; 舒适提醒:请记好明码和窃密问题。 2. 填入发送到邮箱的验证码,而后“持续”; 3. 填入发送到手机的验证码,而后“持续”; 4. Apple ID 注册实现 5. 为Apple ID开启双重认证 任何 Apple ID 帐户只有至多领有一台装有最新版 iOS、iPadOS 或者 macOS 的设施,都能够应用双重认证。进一步理解。 您能够在 iPhone、iPad 或 iPod touch 上登录Apple ID后依照以下步骤来开启双重认证。 a) 在“设置”中开启双重认证 如果应用的是 iOS 10.3 或更高版本: 返回“设置”>“[您的姓名]”>“明码与安全性”。 轻点“开启双重认证”。 轻点“持续”。 如果应用的是 iOS 10.2 或更低版本: 返回“设置”>“iCloud”。 轻点您的 Apple ID>“明码与安全性”。 轻点“开启双重认证”。 轻点“持续”。 零碎可能会要求您答复 Apple ID 平安提醒问题。 ...

July 19, 2020 · 2 min · jiezi

iOS-涨薪-Run-Loop-面试题

Run Loop 运行循环 app 程序只有不停地运行, 能力一直响应用户的操作Run Loop 两大性能: 睡眠中,期待音讯解决音讯从睡眠中 -> 解决音讯, 须要一个唤醒的过程 1、 讲讲 RunLoop, 我的项目中有用到吗? RunLoop 的根本作用: 放弃程序的继续运行 节俭 CPU 的资源,进步程序的性能 ( 没有事件,就请休眠,不要功耗。有事件,就解决) 2、 RunLoop 外部实现逻辑? Core Foundation 中对于 RunLoop 的 5 个类:CFRunLoopRef CFRunLoopModeRef CFRunLoopSourceRef CFRunLoopTimerRef CFRunLoopObserverRef __CFRunLoop 的数据结构struct __CFRunLoop { CFRuntimeBase _base; pthread_mutex_t _lock; /* locked for accessing mode list */ __CFPort _wakeUpPort; // used for CFRunLoopWakeUp Boolean _unused; volatile _per_run_data *_perRunData; // reset for runs of the run loop pthread_t _pthread; uint32_t _winthread; CFMutableSetRef _commonModes; CFMutableSetRef _commonModeItems; CFRunLoopModeRef _currentMode; // 这里有一个汇合 CFMutableSetRef _modes; struct _block_item *_blocks_head; struct _block_item *_blocks_tail; CFAbsoluteTime _runTime; CFAbsoluteTime _sleepTime; CFTypeRef _counterpart;};3、 RunLoop 和线程的关系? ...

July 15, 2020 · 2 min · jiezi

LinkedIn-秘密收集用户剪贴板信息或面临集体诉讼

技术编辑:芒果果丨发自 思否编辑部SegmentFault 思否报道丨公众号:SegmentFault 互联网时代,智能手机中简直所有的应用程序都会收集用户的个人信息。当初咱们要防的除了应用程序自身,居然还有剪贴板。 尽管有些 App 的拜访权限的并不是很必要,但在收集用户的信息前它起码进行了告知,容许用户自行抉择。但最近却频频有利用机密读取用户剪贴板的状况被爆出。 近日,微软公司的 LinkedIn 就因为读取用户剪贴板信息并转移敏感内容的事件被 iPhone 用户告上了法庭。根据加州法律,该诉讼将以涉嫌违反法律或社会规为由,成为个体诉讼。 应用程序频频被爆收集剪贴板信息几天前刚刚有降级了 iOS 14 的用户发现手机内的 TikTok 会“拜访”剪贴板的内容。对此,TikTok 的所属公司字节跳动也做出了解释,示意是解决违规信息的性能产生了误触,并已删除了此性能。此事刚刚平息,LinkedIn 就被爆出了雷同状况。 自 6 月 23 日苹果在 WWDC 上公布了 iOS 14 后,进行了系统升级的用户就能够在信息被其余利用读取时收到正告。上次 TikTok 被发现读取用户剪贴板信息也是因为大量用户发现了零碎收回的“拜访正告”。 苹果 iOS 14 的开发人员和测试人员发现,LinkedIn 在 iPhone 和 iPad 上“机密地”大量读取了用户的剪贴板。 被纽约的用户起诉后,LinkedIn 的一位高管在 Twitter 上示意,“公司已公布了新版本的应用程序”。 LinkedIn 称已更新应用程序iOS 零碎通常容许用户在苹果的一个设施上复制文本、图像、照片和视频,而后将内容粘贴到另一个苹果设施上。但 LinkedIn 是在未告诉用户的状况下读取了剪贴板信息。 依据投诉,LinkedIn 不仅在监督用户,而且还在监督其左近的计算机和其余设施,并且始终在躲避苹果的 Universal Clipboard(iOS 和 Mac 设施之间的通用性剪贴板)。 目前,除了一位高管在 Twitter 上示意已更新了应用程序外,LinkedIn 尚未对此事作出回应。 装置一个新的 App 时,会跳出是否容许拜访通讯录、相册、地位等内容的询问,大多数状况下咱们都会抉择容许,因为“不容许”很可能会导致应用程序无奈应用。 但越是个人信息被大程度公开的互联网时代,越要增强隐衷爱护,这须要应用程序开发者和用户集体独特的致力。 技术能力不体现在通过何种伎俩收集了更多用户信息,而在于其性能失去用户的宽泛认可。 ...

July 13, 2020 · 1 min · jiezi

iOS-Rendering-渲染解析问题解答

1、 CPU 和 GPU 的设计目标别离是什么? cpu是用于简单逻辑计算和管制 领有四个局部 control cache alu dram 其中cache占比比拟大 串行计算 低延时设计。 gpu 图形处理核心 能够进行多线程 高并发简略计算 大吞吐量 2、 CPU 和 GPU 哪个的 Cache、ALU、Control unit 的比例更高? cpu cache高 gpu alu 比例高 计算单元 Arithmetic Logic Unit 3、 计算机图像渲染流水线的大抵流程是什么? 图元-顶点着色器-线条着色器-几何着色器-光栅化-片段着色器-测试与混合 图像在利用中被解决的阶段,此时还处于 CPU 负责的期间。在这个阶段利用可能会对图像进行一系列的操作或者扭转,最终将新的图像信息传给下一阶段。这部分信息被叫做图元(primitives),通常是三角形、线段、顶点等 4、 Framebuffer 帧缓冲器的作用是什么? 存储通过gpu解决后的bitmap 5、 Screen Tearing 屏幕撕裂是怎么造成的? 当显示器电子束从新开始下一帧的扫描时帧缓冲器里的bitmap还没有ready此时扫描进去的是上一帧的内容而后帧处理器的bitmap被替换也就是扫了一半就变成了新的bitmap这样展现的内容就是上一帧和以后帧的拼接 6、 如何解决屏幕撕裂的问题? vsync 垂直同步信号,每次电子束完结,当晚( woqu )屏幕的扫描会发送一个vsync给frame buffer 而后frame buffer才会读取新的frame 7、 掉帧是怎么产生的? 电子束完结以后frame的display然而 gpu还没有解决完 没有新的bitmap 所以还会展现之前的frame ...

July 13, 2020 · 1 min · jiezi

跳槽季iOS开发救救自己别再这样写简历了

 下篇:‘跳槽季’ iOS开发者,写一份胜利的简历?金三银四跳槽季,转瞬已渐入序幕,我作为部门的面试官,在此期间也播种了不少简历。但惋惜的是,收到的简历数量虽多,但令人中意的却是凤毛菱角,一些应聘者倒不是因为本身能力有余而无奈进入面试环节,而是简历自身就没有很好的展现出本人的能力,因而与面试的时机擦肩而过。 为了防止相似的「喜剧」重复呈现,我分享一些我在简历制作上的一些心得和倡议,心愿能帮忙到有需要的读者在下次的求职中更加顺利。 在上面的篇幅中,我将讲述我最看重简历的三个局部,心愿能对各位读者有所启发,这三个局部别离是:  1.技术能力;2.我的项目经验;3.整体印象;就我的教训而言,可能将这三局部依照肯定准则写好的简历,是没有理由无奈取得一次面试机会的,那么话不多说,让咱们开始吧 ????。 1. 技术能力 =========== 通常,「技术能力」这个局部将紧接着您的个人简介之后,放在简历的外围版面。这样设计是有情理的,因为它可能帮忙雇主更快的判断您的技能是否与需要相吻合。 因而在制作这一部分内容时,您应该思考以下两点: (1)写什么?应聘者在技术能力的形容上通常会犯两个谬误:要么无用的货色写的太多,要么有用的货色写的太少。这里的多和少是绝对于「雇主的招聘需要」而言的。 我倡议每个应聘者在撰写简历的这部分时,都可能精心设计所需展现的技能,将本人熟练掌握的技能中与雇主需要重合的局部放在醒目的地位,如果居然还有充裕,那当然能够自豪的在其后展现。 我倡议每个应聘者在撰写简历的这部分时,都可能精心设计所需展现的技能,将本人熟练掌握的技能中与雇主需要重合的局部放在醒目的地位,如果居然还有充裕,那当然能够自豪的在其后展现。 (2)怎么写?当咱们晓得该写什么技能之后,咱们还须要晓得如何失当的形容这些技能,通常咱们会以:「理解,相熟,熟练掌握,精通」这几个形容词来形容技能的熟练程度,让我从面试官的角度来与您分享一下我认为这几个词背地的含意: 1.理解:示意您据说过这个概念,甚至理解与此概念无关的基本原理; 2.相熟:示意您通过 Demo 的模式实际过某个技术,或做过一两个与该技术无关的我的项目,但不足积淀; 3.熟练掌握:示意您在工业级环境下,通过数个我的项目的实际曾经把握了某种技术的外围原理,并可能灵便的利用在开发中; 精通:示意您通过很屡次的我的项目实际和潜心研究,曾经对某种技术的原理和利用把握到近乎尽如人意的水平; 您应该意识到您须要主观,诚恳地评判本人的技术水平,既不要蓄意的夸张,也不应该不可一世。在撰写该局部内容时,我建议您依照技能的熟练程度自高向低的排列,同时对于雇主明确示意须要的技能给予更高的优先级。 2. 我的项目经验讲完了技术能力,接下来将与您分享简历中最重要的局部 -- 「我的项目经验」。如果一份简历满分是 100 分,我的项目经验所占的分数应该是 50 分以上。所以务必请您分外注意。 让我形容一下我常常看到的一类形容: 我在该我的项目中实现了 XXX,YYY 需要,使用了 a,b,c 技术。我至今不明确,这种不足意义的形容为什么会如此经久不衰的呈现在各式各样的简历中,又为什么有这么多求职者对于这个模版的使用如此的乐此不疲。 这种形容形式的弊病在于,它除了通知我求职者的确有在工作之外,再没有其余有用的信息。 3. 简历印象局部 ============= 说完了技能和我的项目经验,最初让咱们谈谈撰写简历时须要恪守的一些准则。当一份简历投递雇主手中时,雇主通常会大略看一下这个简历,凭教训和直觉来判断是否持续浏览这份简历,而接下来我想与您分享的,便是我认为一份好简历应该具备的「好滋味」。 (1)撰写简历三大准则:清晰,简短,必要;正如题目所出现的,一份好简历应该满足以下三个特色: 1.清晰:这表明简历的内容应该是没有歧义,易于了解的,同时简历整体还要富裕逻辑; 2.简短:无论是生存还是工作中,咱们都须要领有一种「抓住重点」的能力,因而优良的求职者应该尽可能在简历中就展示这一点,而展示的形式就是,尽量写出不超过一页的简历,同时让它充斥引诱; 3.必要:招聘自身是一个互相匹配的过程,彼此展现必要的信息,可能帮忙彼此最大化的节约工夫,晋升效率。确保简历中呈现的内容都是雇主冀望理解的很容易就能赢得雇主的好感。 心愿您能在了解这三准则后从新扫视并优化您的简历,确保十拿九稳后,咱们就能够进入下一个重要的环节:简历投放。没错,这里我也有话要说。 (2)为什么您应该进行海投?我的最初一个倡议是:不要海投,要对症下药。 我当然了解面对求职压力,海投所耗费的老本及其低廉,但请留神,绝对的,海投带来的收益也近乎微不足道(更别提海投失败更容易给人带来挫败感,使人陷入一种负面情绪的恶性循环)。 其实「海投」和「精准投放」之间的差距并没有特地迥异,有时候只须要您一点点额定的致力,就能带来微小的收益。 我建议您将本人的所有信息先整合在一个文档内,而后每天抉择 10 家您向往的雇主企业,仔细阅读对方的招聘需要,并依据对方的招聘需要在本人的文档中摘出与之匹配的局部组合在一起。 之后就能够痛快的投递进来了。猜猜看接下来会产生什么?因为您的技能形容完满符合了雇主的须要,并且我的项目经验的形容因为使用了 STAR 法令,雇主可能更充沛的理解到您各方面的能力。 毫无疑问的,您将大大晋升您进入面试环节的几率! 这可是胜利的第一步! 4. 结尾 ========= 至此,文章终于到了序幕。总结一下,咱们议论了简历制作过程中须要留神的以下三个局部,并别离给出了一些倡议: 1.技术能力:先写岗位所需能力,再写加分能力,不要写无关能力;2.我的项目经验:只写明星我的项目,形容遵循 STAR 法令;3.简历印象:简历遵循三大准则:清晰,简短,必要,要对症下药,不要海投;心愿我所分享的教训能对您有所帮忙,也心愿您终能如愿以偿进入心仪的企业工作。 感谢您读到这里,真是不容易! 5.结交人脉作为一个开发者,有一个学习的气氛跟一个交换圈子特地重要,这是一个我的iOS交换群:1001906160 ,进群明码000,不论你是小白还是大牛欢送入驻 ,分享BAT,阿里面试题、面试教训,探讨技术, 大家一起交流学习成长!

July 10, 2020 · 1 min · jiezi

嘿iOS开发你准备好何时跳槽了吗

序言 我置信很多人都在说,iOS行业不好了,iOS当初行情越来越难了,就业的人比找工作的人还要多。就业即相当于转行,跳槽即相当于升高本人的身价。那么做iOS开发的你,你是否在时刻筹备着跳槽或者转行了。 咱们先看一下当初iOS行业,iOS程序员在现在竞争强烈的市场环境下,你本人还值多少钱,上面是按年限,按要求提出的工作及薪资待遇。 一年以内,一至三年,本科: iOS市场,薪资待遇 iOS市场,薪资待遇 iOS市场,薪资待遇 那么你处于哪一个阶段!,你拿的薪资待遇怎么样,你的工作怎么样,是不是天天加班,有改不完的bug,没有工夫陪本人的小孩,爱人,家人。面对这样的一个现实情况,你本人是否还在保持,保持本人的岗位,是否去想过转行,跳槽。面对现实的生存,就业的你,或者想跳槽的你,你真的做好筹备了吗? 接下来小编会来浅谈一下iOS开发中有哪些方向和职业规划,同时小编也欢送大家退出小编的iOS交换群761407670,明码‘000‘,群里会提供相干面试材料,书籍欢送大家入驻!iOS开发的你正处于哪一个技能阶段和年限,学好这些,把握这些,你会在如此定义你本人方向和职业规划: ** 1、架构师 2、平安攻防 3、逆向编程 4、iOS进阶 5、底层开发 6、swift4.0开发** 小编为大家整顿一下,不论做iOS开发几年的都能够看一下,畅通与总结这几年本人的ios编程之路,程序员之路,兴许是辞别你的编程之路,兴许是再次点燃你心田的星星之火。 一、iOS架构师应该理解把握的 iOS架构师应该去理解把握“UML建模”、“软件工程架构与设计模式”、“第三方库” UML建模 软件工程架构与设计模式 第三方库 二、iOS平安攻防应该理解与把握的 iOS平安攻防在“攻”与“防”中会有哪些了,须要懂哪些? ** 1、 “攻”应该包含这些在类** ** (1)iOS逆向工程介绍** ** (2)逆向工具与实践** ** (3)我的项目实际** ** (4)构建防护** OS逆向工程介绍 逆向工具与实践 我的项目实际,构建防护 2“防”应该包含这些在类 **(1)加密与取证**** (2)反取证** ** (3)运行时库平安** 加密与取证 反取证,运行时库平安 三、iOS进阶学习应该须要理解并把握的 **(1)多线程与网络进阶**** (2)iOS底层进阶** ** (3)iOS主动打包** 多线程 ...

July 9, 2020 · 1 min · jiezi

我的-2020-iOS-BAT面试心得Bigo字节快手伴鱼百度微博等

ps:后面按照自己面试的时间顺序来写,记录的面试题是我印象比较深刻的,并不一定很全,暂时先提供面试题,后面考虑给出相应的题解。面试我面试了大大小小的各种公司,BAT、bigo、字节、快手、伴鱼等,因为一些原因,也拒面了一些公司,拿了几家的offer。 伴鱼伴鱼是我准备后参加的第一轮面试,有很多自己准备得不是很全,也没有完全进入面试状态,面试结果不是很好,一面就挂了。 一面算法题:判断平衡二叉树(easy)代码阅读题:(问输出)TestObject *object1 = [[TestObject alloc] init];__block TestObject *object2 = [[TestObject alloc] init];object1.name = @"Mike";object2.name = @"Sean";__block int vi = 1;void (^handler)(NSString *) = ^(NSString *name) { object1.name = name; object2.name = name; vi = 2;}handler(@"Lucy");NSLog(object1.name);NSLog(object2.name);NSLog(@"%i", vi);引申: 如果__block int vi = 1; 这句改成int vi = 1会怎样,为什么代码中的block是什么block,为什么 weak的实现原理weak弱引用表是可变的么还是不可变的weak是在什么时候置nil的,如果同时有很多对象对性能影响大怎么办UIView 和 CALayer的关系和区别UIView 和 CALayer在动画上的区别frame和bounds在什么情况下是不相等的bounds x,y 一定是0,0么,为什么bounds 改成 (50, 50, width, height)会发生什么,view本身,子View?5858我面了很多次,一开始面的基础研发部门,后来给我转到了企业工具研发,中间时间拖得有点长,直接拒面了。 一面说下你在开发过程中遇到过的内存泄漏NSTimer 怎么处理内存泄漏Delegate什么情况下会出现内存泄漏,怎么解决Delegate和Notification的区别多线程相关 iOS中有哪些多线程技术如果有两个同步任务嵌套会怎样常见的锁,为什么要加锁C依赖AB任务执行完才能执行,你怎么设计读写锁底层怎么实现JavaScriptCore相关 什么是JavaScriptCore,JS和Native是怎么进行通信的你知道hybrid么,说说你平常怎么使用的(因为没怎么接触过直接说的不会)后面就是聊天了,中间穿插问了下动态库和静态库的却别二面(终面)58这个部门的面试就两轮,二面是群面(几个人轮流面你),第一次接触这种面试形式,压力还是有点的。 对我的项目表感兴趣,前面聊了不少项目的内容,问了下项目的背景,做了啥以及有哪些收益了解业内性能优化是怎么做的么你项目中是怎么做性能优化的ReactNative相关 RN的原理RN和flutter的区别你知道RN拆包么,RN为什么要拆包JS是单线程的是怎么和native多线程进行交互的(这个问题有点奇葩)JS和native通信的数据结构是什么你们公司对于线上JSError做了哪些事情是怎么处理的你有什么想问的么一个创业公司这个创业公司全程都是在聊天,后面问了些和iOS没多大关系的问题,然后就发了口头offer。 聊天:在公司中学到了啥,为啥要来北京等有一个10个G的文件里面每一行都有数字,对这些数字进行排序(两种方法)怎么将彩色的图片专程黑白的Web渲染和Native渲染有什么异同点拼多多拼多多应该是自己面的一个相对较大的公司,面试过程中和面试官有了点小分歧,后面问我源码在哪个文件哪一行,后面问得问题也基本上是我之前没怎么接触过的。 ...

July 7, 2020 · 1 min · jiezi

App-Clips

该文章属于<简书 — 刘小壮>原创,转载请注明:<简书 — 刘小壮> 前两天leader让我调研一下App Clips,我简单调研了一下,这是我调研的一些总结,大家可以看看,有问题欢迎评论区讨论。 是什么类似微信的小程序,不需要下载App,可以直接打开一个小程序,并且不需要显式的去App Store里下载。APP clips可以在不打开主App的情况下,单独进行使用,交互操作和主App无异,例如登录、列表视图、支付等。 怎么用通过Safari Banner或iMessage,点击URL链接下载和打开App,此时会弹出下面的问询窗口,告知用户是否要打开APP clips,点击open即可打开。如果已经安装主App,则打开的就是主App,所以主App和APP clips都需要支持这个URL。 下面是打开主App的Safari Banner,APP clips的应该也这个形式差不多。 配置image、subtitle等,应该是直接在Apple Developer开发者中心配置,之前一些App外的信息也都是在上面配置的。 开发TipsAPP clips由于是小程序,所以安装包大小被限制在10MB以内。并且一个主App,只能有一个APP clips。如果想开发APP clips的话,可以下载最新的Xcode12 Bate版开发,当然也需要运行在最新的iOS14系统上。APP clips的bundle id有命名要求,需要以{主App Bundle id}+Clip的格式命名。由于APP clips只支持iOS14,所以可以直接使用SwiftUI进行开发,还是比较nice的。通过Xcode12的App Clip选项即可开发自己的App Clips,开发完成后在Apple Developer配置一些展示信息即可。

July 6, 2020 · 1 min · jiezi

NSURLSession最全攻略

该文章属于<简书 — 刘小壮>原创,转载请注明:<简书 — 刘小壮> NSURLSessionNSURLSession在iOS7中推出,NSURLSession的推出旨在替换之前的NSURLConnection,NSURLSession的使用相对于之前的NSURLConnection更简单,而且不用处理Runloop相关的东西。 2015年RFC 7540标准发布了http 2.0版本,http 2.0版本中包含很多新的特性,在传输速度上也有很明显的提升。NSURLSession从iOS9.0开始,对http 2.0提供了支持。 NSURLSession由三部分构成: NSURLSession:请求会话对象,可以用系统提供的单例对象,也可以自己创建。NSURLSessionConfiguration:对session会话进行配置,一般都采用default。NSURLSessionTask:负责执行具体请求的task,由session创建。NSURLSession有三种方式创建: sharedSession系统维护的一个单例对象,可以和其他使用这个session的task共享连接和请求信息。 sessionWithConfiguration:在NSURLSession初始化时传入一个NSURLSessionConfiguration,这样可以自定义请求头、cookie等信息。 sessionWithConfiguration:delegate:delegateQueue:如果想更好的控制请求过程以及回调线程,需要上面的方法进行初始化操作,并传入delegate来设置回调对象和回调的线程。 通过NSURLSession发起一个网络请求也比较简单。 创建一个NSURLSessionConfiguration配置请求。通过Configuration创建NSURLSession对象。通过session对象发起网络请求,并获取task对象。调用[task resume]方法发起网络请求。NSURLSessionConfiguration *config = [NSURLSessionConfiguration defaultSessionConfiguration];NSURLSession *session = [NSURLSession sessionWithConfiguration:config delegate:self delegateQueue:[NSOperationQueue mainQueue]];NSURLSessionDataTask *task = [session dataTaskWithURL:[NSURL URLWithString:@"http://www.baidu.com"]];[task resume];NSURLSessionTask通过NSURLSession发起的每个请求,都会被封装为一个NSURLSessionTask任务,但一般不会直接是NSURLSessionTask类,而是基于不同任务类型,被封装为其对应的子类。 NSURLSessionDataTask:处理普通的Get、Post请求。NSURLSessionUploadTask:处理上传请求,可以传入对应的上传文件或路径。NSURLSessionDownloadTask:处理下载地址,提供断点续传功能的cancel方法。主要方法都定义在父类NSURLSessionTask中,下面是一些关键方法或属性。 currentRequest当前正在执行的任务,一般和originalRequest是一样的,除非发生重定向才会有所区别。originalRequest主要用于重定向操作,用来记录重定向前的请求。taskIdentifier当前session下,task的唯一标示,多个session之间可能存在相同的标识。prioritytask中可以设置优先级,但这个属性并不代表请求的优先级,而是一个标示。官方已经说明,NSURLSession并没有提供API可以改变请求的优先级。state当前任务的状态,可以通过KVO的方式监听状态的改变。- resume开始或继续请求,创建后的task默认是挂起的,需要手动调用resume才可以开始请求。- suspend挂起当前请求。主要是下载请求用的多一些,普通请求挂起后都会重新开始请求。下载请求挂起后,只要不超过NSURLRequest设置的timeout时间,调用resume就是继续请求。- cancel取消当前请求。任务会被标记为取消,并在未来某个时间调用URLSession:task:didCompleteWithError:方法。 NSURLSession提供有普通创建task的方式,创建后可以通过重写代理方法,获取对应的回调和参数。这种方式对于请求过程比较好控制。 - (NSURLSessionDataTask *)dataTaskWithRequest:(NSURLRequest *)request;- (NSURLSessionUploadTask *)uploadTaskWithRequest:(NSURLRequest *)request fromFile:(NSURL *)fileURL;- (NSURLSessionDownloadTask *)downloadTaskWithRequest:(NSURLRequest *)request;除此之外,NSURLSession也提供了block的方式创建task,创建方式简单如AFN,直接传入URL或NSURLRequest,即可直接在block中接收返回数据。和普通创建方式一样,block的创建方式创建后默认也是suspend的状态,需要调用resume开始任务。 completionHandler和delegate是互斥的,completionHandler的优先级大于delegate。相对于普通创建方法,block方式更偏向于面向结果的创建,可以直接在completionHandler中获取返回结果,但不能控制请求过程。 - (NSURLSessionDataTask *)dataTaskWithURL:(NSURL *)url completionHandler:(void (^)(NSData * _Nullable data, NSURLResponse * _Nullable response, NSError * _Nullable error))completionHandler;- (NSURLSessionUploadTask *)uploadTaskWithRequest:(NSURLRequest *)request fromData:(nullable NSData *)bodyData completionHandler:(void (^)(NSData * _Nullable data, NSURLResponse * _Nullable response, NSError * _Nullable error))completionHandler;- (NSURLSessionDownloadTask *)downloadTaskWithURL:(NSURL *)url completionHandler:(void (^)(NSURL * _Nullable location, NSURLResponse * _Nullable response, NSError * _Nullable error))completionHandler;可以通过下面的两个方法,获取当前session对应的所有task,方法区别在于回调的参数不同。以getTasksWithCompletionHandler为例,在AFN中的应用是用来获取当前session的task,并将AFURLSessionManagerTaskDelegate的回调都置为nil,以防止崩溃。 ...

July 6, 2020 · 6 min · jiezi

探秘AFNetworking

该文章属于<简书 — 刘小壮>原创,转载请注明:<简书 — 刘小壮> AFNetworking源码分析AFNetworking是iOS最常用的网络框架,虽然系统也有NSURLSession,但是我们一般不会直接用它。AFNetworking经过了三个大版本,现在用的大多数都是3.x的版本。 AFNetworking经历了下面三个阶段的发展: 1.0版本 : 基于NSURLConnection的封装。2.0版本 : 两套实现,分别基于NSURLConnection和NSURLSession,是转向NSURLSession的过渡版。3.0版本 : 基于NSURLSession的封装。文件构成 AFNetworking3.X的构成很简单,主要就四部分,除此之外还有一些基于UIKit的Category,但这些并不是标配。 Manager : 负责处理网络请求的两个Manager,主要实现都在AFURLSessionManager中。Reachability : 网络状态监控。Security : 处理网络安全和HTTPS相关的。Serialization : 请求和返回数据的格式化器。AFURLSessionManager在AFN3.0中,网络请求的manager主要有AFHTTPSessionManager和AFURLSessionManager构成,二者为父子关系。这两个类职责划分很清晰,父类负责处理一些基础的网络请求代码,并且接受NSURLRequest对象,而子类则负责处理和http协议有关的逻辑。 AFN的这套设计很便于扩展,如果以后想增加FTP协议的处理,则基于AFURLSessionManager创建子类即可。子类中只需要进行很少的代码处理,创建一个NSURLRequest对象后调用父类代码,由父类去完成具体的请求操作。 创建sessionManagerAFHTTPSessionManager类的初始化方法中并没有太多实现代码,其内部调用的都是父类AFURLSessionManager的initWithSessionConfiguration方法,下面是此方法内部的一些关键代码。 在初始化方法中包含一个参数sessionConfiguration,如果没有传入的话默认是使用系统的defaultConfiguration,我们创建是一般都不会自定义configuration,所以大多数都是系统的。 if (!configuration) { configuration = [NSURLSessionConfiguration defaultSessionConfiguration];}随后是NSURLSession的初始化代码,关于NSOperationQueue后面详细进行讲解。NSURLSession的初始化方式有两种,一种是使用系统的共享session,另一种是自己创建session。AFN选择的是创建自己的session,并且每个请求都会创建一个独立的session。 可以通过NSURLSession进行连接复用,这样可以避免很多握手和挥手的过程,提高网络请求速度,苹果允许iOS设备上一个域名可以有四个连接同时存在。但是由于AFN的实现是每个请求都创建一个session,所以就不能进行连接复用。 所以可以通过在外面对AFN进行二次封装,将AFHTTPSessionManager复用为单例对象,通过复用sessionManager的方式,来进行连接的复用。但是这种方案对于不同的requestSerializer、responseSerializer等情况,还是要做特殊兼容,所以最好建立一个sessionManager池,对于同类型的sessionManager直接拿出来复用,否则就创建新的。 // 共享session连接池[NSURLSession sharedSession];// 创建新session,则不能使用共享session连接池[NSURLSession sessionWithConfiguration:self.sessionConfiguration delegate:self delegateQueue:self.operationQueue];由于当前AFURLSessionManager对象的所有sessionTask请求任务,都是共享同一个回调代理的,所以AFN为了区分每个sessionTask,通过下面的可变字典,将所有taskDelegate和task.taskIdentifier的进行了一一对应,以便于很容易的对每个请求task进行操作。 self.mutableTaskDelegatesKeyedByTaskIdentifier = [[NSMutableDictionary alloc] init];在初始化方法中,可以发现AFN在创建session后,调用了getTasksWithCompletionHandler方法来获取当前所有的task。但是现在刚创建session,理论上来说是不应该有task的。但从AFN的issues中找到了答案issues 3499。 这是因为,在completionHandler回调中,为了防止进入前台时,通过session id恢复的task导致一些崩溃问题,所以这里将之前的task进行遍历,并将回调都置nil。 [self.session getTasksWithCompletionHandler:^(NSArray *dataTasks, NSArray *uploadTasks, NSArray *downloadTasks) { for (NSURLSessionDataTask *task in dataTasks) { [self addDelegateForDataTask:task uploadProgress:nil downloadProgress:nil completionHandler:nil]; } for (NSURLSessionUploadTask *uploadTask in uploadTasks) { [self addDelegateForUploadTask:uploadTask progress:nil completionHandler:nil]; } for (NSURLSessionDownloadTask *downloadTask in downloadTasks) { [self addDelegateForDownloadTask:downloadTask progress:nil destination:nil completionHandler:nil]; }}];创建task在AFURLSessionManager中进行task的创建,task的类型总共分为三种,dataTask、uploadTask、downloadTask,AFN并没有对streamTask进行处理。 ...

July 6, 2020 · 9 min · jiezi

ScrollViewCollectionView和TableView添加UIRefreshControl实现下拉刷新

Apple在iOS 6中添加了UIRefreshControl,但只能在UITableViewController中使用,不能在UIScrollView和UICollectionView中使用。 iOS 10 新特性从iOS 10开始,UIScrollView增加了一个refreshControl属性,用于把配置好的UIRefreshControl赋值给该属性,这样UIScrollView就有了下拉刷新功能。和之前在UITableViewController中使用一样,不需要设置UIRefreshControl的frame,只需要配置UIRefreshControl。 因为UITableView和UICollectionView继承自UIScrollView,所以UITableView和UICollectionView也继承了refreshControl属性,也就是可以很方便的把刷新控件添加到滚动视图、集合视图和表视图(不再需要表视图控制器)。 截止目前,Xcode 8.2.1的Interface Builder还没有支持refreshControl属性,如果你需要在UIScrollView、UITableView和UICollectionView中使用UIRefreshControl只能通过代码添加。通过Interface Builder可以为UITableViewController 添加刷新控件。滚动视图示例这个demo使用Single View Application模板,打开storyboard,在系统创建的ViewController上添加一个UIScrollView,在UIScrollView上添加两个UILabel,并在UILabel上添加内容。想要实现的功能是,下拉刷新页面时隐藏第二个UILabel,再次刷新时显示该UILabel。 这里只对demo简单描述,如果需要查看详细代码,可以在我的GitHub中查看。另外,文章底部也会提供源码地址。创建刷新控件在UIScrollView、UITableView和UICollectionView中创建刷新控件步骤是一样的。在这个示例中,在ViewController的viewDidLoad方法中创建并配置UIRefreshControl。scrollView是连接到Interface Builder中的UIScrollView的IBOutlet属性。 - (void)viewDidLoad{ [super viewDidLoad]; // 1 先判断系统版本 if ([NSProcessInfo.processInfo isOperatingSystemAtLeastVersion:(NSOperatingSystemVersion){10,0,0}]) { // 2 初始化 UIRefreshControl *refreshControl = [[UIRefreshControl alloc] init]; // 3.1 配置刷新控件 refreshControl.tintColor = [UIColor brownColor]; NSDictionary *attributes = @{NSForegroundColorAttributeName : [UIColor redColor]}; refreshControl.attributedTitle = [[NSAttributedString alloc] initWithString:@"Pull To Refresh" attributes:attributes]; // 3.2 添加响应事件 [refreshControl addTarget:self action:@selector(refresh:) forControlEvents:UIControlEventValueChanged]; // 4 把创建的refreshControl赋值给scrollView的refreshControl属性 self.scrollView.refreshControl = refreshControl; }}注意以下几点: ...

July 1, 2020 · 1 min · jiezi

腾讯-‘iOS开发-部门3次挂了这次成功拿下岗位面试题附答案

前言最近在网上找了不少面试相关的资料学习准备面试!之前面了一个部门3次挂了.....尴尬 有记录面试题, 希望对你们有帮助~! 少走一些弯路! 请看答案在最下面!! 腾讯一面介绍你做过的项目难点?OC修饰符,追问weak,unsafe_unretained什么意思? 为什么NSString要加copy? 4.4. KVO的原理是什么? RN基础架构是什么? 做过哪些性能优化? 有没有遇到什么卡顿的情况?怎么处理的? HTTPS和HTTP区别,追问具体怎么加密,如何信任证书 HTTP2.0和HTTP1.1区别 TCP接受窗口和发送窗口,TCP 3次握手 数组和链表的区别,使用场景分别是什么 算法题:给定50个已排序数组,每个里面200个数,找出其中最小的200个数 描述思路,追问时间复杂度,追问还有没有其他方法。描述思路,继续追问复杂度,问有没其他方法。线下写完代码发送到邮箱。 算法复试4道一共有算法题,要求写出可编译代码。 反转一个链表 给定一个数组,其中有一个数只出现一次,其他数都出现两次,找到只出现一次的那个数。 实现堆排序给定一个数组array,其中array[i] != array[i+1],找到任何一个i,满足array[i] > array[i-1] 且 array[i] > array[i+1]。假设array[-1] == array[n] == 负无穷。要求O(logN)时间复杂度。6.2 二面算法题翻转k个链表 (写代码) 如何拷贝一个包含随机指针的链表 (描述思路) 问答 (因为我简历写做过RN,面试官刚好也做过) 描述一下RN渲染过程 你使用RN的时候有遇到什么问题?什么地方导致RN性能瓶颈? OC对象内存结构,isa指针有什么用,根源类是什么? _weak自动重置nil具体如何实现? MSS和MTU是什么,具体怎么确定 交叉面试你觉得熟悉iOS哪些框架? 为什么UI更新必须在主线程? 追问:具体哪些冲突? 追问:还有吗? 追问:如果强行开一个子线程,把事件处理和ui更新都放进去,是不是可以解决你说的冲突? 了解过Xcode编译过程?了解过bitcode吗? 你了解哪些设计模式?具体描述一下? 研究生什么方向?答:VR。追问:3D渲染的具体过程。 面试资料:看完文章如果你正在跳槽或者正准备跳槽不妨动动小手,添加一下咱们的交流群1012951431来获取一份详细的大厂面试资料为你的跳槽多添一份保障。

June 29, 2020 · 1 min · jiezi

教你打造一套移动端-APM-监控系统

APM 是 Application Performance Monitoring 的缩写,监视和管理软件应用程序的性能和可用性。应用性能管理对一个应用的持续稳定运行至关重要。所以这篇文章就从一个 iOS App 的性能管理的纬度谈谈如何精确监控以及数据如何上报等技术点App 的性能问题是影响用户体验的重要因素之一。性能问题主要包含:Crash、网络请求错误或者超时、UI 响应速度慢、主线程卡顿、CPU 和内存使用率高、耗电量大等等。大多数的问题原因在于开发者错误地使用了线程锁、系统函数、编程规范问题、数据结构等等。解决问题的关键在于尽早的发现和定位问题。 本篇文章着重总结了 APM 的原因以及如何收集数据。APM 数据收集后结合数据上报机制,按照一定策略上传数据到服务端。服务端消费这些信息并产出报告。请结合姊妹篇, 总结了如何打造一款灵活可配置、功能强大的数据上报组件。 一、卡顿监控卡顿问题,就是在主线程上无法响应用户交互的问题。影响着用户的直接体验,所以针对 App 的卡顿监控是 APM 里面重要的一环。 FPS(frame per second)每秒钟的帧刷新次数,iPhone 手机以 60 为最佳,iPad 某些型号是 120,也是作为卡顿监控的一项参考参数,为什么说是参考参数?因为它不准确。先说说怎么获取到 FPS。CADisplayLink 是一个系统定时器,会以帧刷新频率一样的速率来刷新视图。 [CADisplayLink displayLinkWithTarget:self selector:@selector(###:)]。至于为什么不准我们来看看下面的示例代码 _displayLink = [CADisplayLink displayLinkWithTarget:self selector:@selector(p_displayLinkTick:)];[_displayLink setPaused:YES];[_displayLink addToRunLoop:[NSRunLoop currentRunLoop] forMode:NSRunLoopCommonModes];代码所示,CADisplayLink 对象是被添加到指定的 RunLoop 的某个 Mode 下。所以还是 CPU 层面的操作,卡顿的体验是整个图像渲染的结果:CPU + GPU。请继续往下看 1. 屏幕绘制原理 讲讲老式的 CRT 显示器的原理。 CRT 电子枪按照上面方式,从上到下一行行扫描,扫面完成后显示器就呈现一帧画面,随后电子枪回到初始位置继续下一次扫描。为了把显示器的显示过程和系统的视频控制器进行同步,显示器(或者其他硬件)会用硬件时钟产生一系列的定时信号。当电子枪换到新的一行,准备进行扫描时,显示器会发出一个水平同步信号(horizonal synchronization),简称 HSync;当一帧画面绘制完成后,电子枪恢复到原位,准备画下一帧前,显示器会发出一个垂直同步信号(Vertical synchronization),简称 VSync。显示器通常以固定的频率进行刷新,这个固定的刷新频率就是 VSync 信号产生的频率。虽然现在的显示器基本都是液晶显示屏,但是原理保持不变。 ...

June 28, 2020 · 67 min · jiezi

iOS-动画-窗景篇三完结

这篇文章是系列文章的第三篇。 看过上一篇文章的朋友,已经知道标题中的“景”指代 view,“窗”指代 view.mask,窗景篇就是在梳理 mask 及 mask 动画。如果你还不熟悉 iOS 的 mask,建议先看一下第一篇。 前两篇我们介绍了 mask、mask 动画的一些用法。 这一篇作为收尾,我们来实现一个效果练练手,也借这个效果,让大家回忆起一个简单的道理:复杂的效果,可以等价于简单效果的组合。 一、效果这个效果如下面的动图所示: 我们截取比较有代表性的一帧,如下图所示: 从图中可以看到,波浪由两种颜色组成,各部分颜色不同。 这个效果看上去有点复杂,如果不熟悉 mask,可能一时半会儿没有思路。但看过前两篇的朋友,可能已经暗暗在想,是窗在动?还是景在动?会不会有多套窗景? 那么接下来,我们先通过一个简单的效果来看一下原理。 注:波浪动画的实现和本文关系不大,本文不会讲述。网上有成熟的波浪动画的教程,本文 demo 中 WaveView 类也有简要的注释。一、一个简单的效果这个效果如下图所示: 从图中可以看到,一张黑白图片上有一部分是彩色的。我们当然可以通过图像处理来实现这个效果,但在本文中,我们还是使用 mask 的方式来实现。 我们回忆一下前文中的一张图: 通过对frontView 添加一个圆 mask,就形成了图中的效果。 也许有的朋友已经想到,把上图中 backView 的图换成和 frontView 一样的黑白图片,不就是本例的效果吗,如下图所示: 也就是说,这个效果看上去是黑白图片上有一部分变成了彩色,但其实只是两张内容一样的图片重叠,黑白图片在后,彩色图片在前,而前方的彩色图片,被施加了圆形的 mask。 这个效果很简单,但能让我们意识到一件事:看上去是一张图,其实可能是多张图组合而成。 既然如此,那本文的波浪动画中,各部分颜色不同的波浪,真的只是一个波浪吗? 没错,本例中的波浪,也是多个波浪组合而成的,接下来,我们就详细的看一看。 二、多景合一和黑白、彩色图片重叠效果一样,多色波浪也是由一组重叠的波浪 view 组合而成。 每层 view 的波浪只有一种颜色,各层 view 的波浪动画都一致,对于每一帧,所有波浪都是完全重合的。 每个波浪 view 都有自己的 mask,在 mask 们 的控制下,每层波浪 view 只显示了波浪的一部分,我们看到的多色波浪,就是各层波浪 view 可见部分的组合。 ...

June 28, 2020 · 2 min · jiezi

iOS笔记-1SEL的原理与使用

概念SEL:方法名(编号)IMP:一个函数指针,保存了方法的地址@selector(方法名)获取方法的编号,结果是SEL类型。他的行为基本可以等同于C语言中的函数指针区别 C语言中,可以直接把函数名赋值给一个函数指针,而且函数指针直接保存了函数地址Objc中的类不能直接应用函数指针,只能使用@selector来获取,获取的是方法的编号方法以@selector作为索引,@selector的数据类型是SEL,对应每个方法的编号,当我们寻找方法的时候使用的是这个方法编号。类中存在一个methodLists专门用来存放方法实现IMP和SEL的映射。方法编号SEL通过Dispatch table表寻找到对应的IMP,IMP就是一个函数指针,然后执行这个方法。 struct objc_class {    struct objc_class super_class;  /*父类*/    const char *name;                 /*类名字*/    long version;                   /*版本信息*/    long info;                        /*类信息*/    long instance_size;               /*实例大小*/    struct objc_ivar_list *ivars;     /*实例参数链表*/    struct objc_method_list **methodLists;  /*方法链表*/    struct objc_cache *cache;               /*方法缓存*/    struct objc_protocol_list *protocols;   /*协议链表*/};typedef struct objc_method *Method;typedef struct objc_ method { SEL method_name; //方法名 char *method_types; //参数类型 IMP method_imp; //方法实现};相关class   返回对象的类;isKindOfClass 和 isMemberOfClass检查对象是否在指定的类继承体系中;respondsToSelector 检查对象能否相应指定的消息;conformsToProtocol 检查对象是否实现了指定协议类的方法;methodForSelector  返回指定方法实现的地址。performSelector:withObject 执行SEL 所指代的方法。具体实现在寻找IMP的地址时,runtime提供了两种方法: IMP class_getMethodImplementation(Class cls, SEL name);IMP method_getImplementation(Method m)而根据官方描述,第一种方法可能会更快一些 ...

June 26, 2020 · 1 min · jiezi

编码1蜜蜂采蜜

一朵花有Open和Close两种状态,3只蜜蜂在花Open的时候去采蜜,在花Close的时候回巢,用面向对象技术和Design Pattern方法模拟上面过程,输出如下: Flower openBee 1's eating time!Bee 2's eating time!Bee 3's eating time!Floer closeBee 1's bed time!Bee 2's bed time!Bee 3's bed time!编码实现上述情景,要求使用一种Design Pattern方法,使花和蜜蜂之间松耦合。 protocol ObserverProtocol { var id: Int {get set} func onValueChanged(_ value: Any?)}protocol ObservableProtocol: class { var observers: [ObserverProtocol] {get set} func addObserver(_ observer: ObserverProtocol) func removeObserver(_ observer: ObserverProtocol) func notifyObservers(_ observers: [ObserverProtocol])}class Observable<T>: ObservableProtocol { var value: T { didSet { self.notifyObservers(self.observers) } } internal var observers: [ObserverProtocol] = [] init(value: T) { self.value = value } func addObserver(_ observer: ObserverProtocol) { guard self.observers.contains(where: { $0.id == observer.id}) == false else { return } self.observers.append(observer) } func removeObserver(_ observer: ObserverProtocol) { guard let index = self.observers.firstIndex(where: { $0.id == observer.id}) else { return } self.observers.remove(at: index) } func notifyObservers(_ observers: [ObserverProtocol]) { observers.forEach({$0.onValueChanged(value)}) } deinit { observers.removeAll() }}enum FlowerStatus: String { case open = "Flower open" case close = "Flower close"}class Flower: Observable<FlowerStatus> { var status: FlowerStatus { get { return self.value } set { print(newValue.rawValue) self.value = newValue } }}struct Bee: ObserverProtocol { var id: Int var name: String func onValueChanged(_ value: Any?) { if let status = value as? FlowerStatus { if status == .open { print("\(name)'s eating time!") } else { print("\(name)'s beding time!") } } }}func beeTime() { let flower = Flower(value: .close) let bee1 = Bee.init(id: 1, name: "Bee 1") let bee2 = Bee.init(id: 2, name: "Bee 2") let bee3 = Bee.init(id: 3, name: "Bee 3") flower.addObserver(bee1) flower.addObserver(bee2) flower.addObserver(bee3) flower.status = .open flower.status = .close}beeTime()Observable另一种实现,注意,这种方法实现,打印是无序的 ...

June 24, 2020 · 2 min · jiezi

OC-语法快速入门

来源: https://www.runoob.com/w3cnot... 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)。 这里提供了一份意义相近的C++语法对照,如下: class MyObject : public NSObject {protected: int memberVar1; // 实体变量 void * memberVar2; public: static return_type class_method(); // 類方法 return_type instance_method1(); // 实例方法 return_type instance_method2( int p1 ); return_type instance_method3( int p1, int p2 );}Objective-C定义一个新的方法时,名称内的冒号(:)代表参数传递,不同于C语言以数学函数的括号来传递参数。Objective-C方法使得参数可以夹杂于名称中间,不必全部附缀于方法名称的尾端,可以提高程序可读性。设定颜色RGB值的方法为例: ...

June 17, 2020 · 2 min · jiezi

OCPromise用OC实现JS的Promise

OCPromise是参考的Javescript中的Promise写的一套用Objective-C实现的异步任务队列管理的链式操作工具。 写这套库的想法是源自一次面试的失败经历:之前在工作中我使用过React native进行开发,因此也写过Javascript代码并且使用过Promise语法,但是在一次面试中,面试官让我手写Promise的实现,当时我直接懵了,才发现在开发过程中很多东西实现过一次之后,后面再用到时直接复制粘贴再改一改,结果就是这些东西根本没有变成自己的知识,甚至对它的理解也很片面。 回想起来,Promise的调用方式还是很有意思的,链式的语法使用起来也很美观,因此我尝试用OC实现了Promise的功能,OCPromise提供的功能可以参考这篇关于JS-Promise的文章:理解 Javascript 中的 Promise,我在写OCPromise时也是完全参照的Promise,只不过由于语法的差异性,调用方法会略有不同。 下面我先介绍一下OCPromise的使用方法: OCPromise的创建 OCPromise *p = Promise(^(resolve _Nonnull resolve, reject _Nonnull reject) { NSLog(@"start new Promise..."); resolve(@123); }); OCPromise *multiply = function(^OCPromise * _Nullable(id _Nonnull value) { return Promise(^(resolve _Nonnull resolve, reject _Nonnull reject) { NSLog(@"calculating %ld x %ld ...", [value longValue], [value longValue]); resolve([NSNumber numberWithLong:[value longValue] * [value longValue]]); }); }); OCPromise *add = function(^OCPromise * _Nullable(id _Nonnull value) { return Promise(^(resolve _Nonnull resolve, reject _Nonnull reject) { NSLog(@"calculating %ld + %ld ...", [value longValue], [value longValue]); resolve([NSNumber numberWithLong:[value longValue] + [value longValue]]); }); });使用Promise()函数创建的Promise对象可以处理独立的任务,而使用function()函数创建Promise对象时,实际Promise对象的创建延迟到^OCPromise *(id value) {}执行时期,并且Promise的任务执行期间value可以参与内部的Promise()任务的执行的(也可以不参与)。 ...

June 16, 2020 · 4 min · jiezi

OCPromise进阶用法

OCPromise-用OC实现JS的Promise上篇文章介绍了OCPromise的基本用法,这篇里我将介绍几个扩展方法。 deliverOnMainThread上篇结尾处提到过,OCPromise的任务都是在子线程上执行的,那么我们在接收任务结果时,如果需要进行UI的操作时,还需要自己手动切到主线程上,因此我提供了一个将任务结果传递到主线程的回调方法: OCPromise *p = Promise(^(resolve _Nonnull resolve, reject _Nonnull reject) { NSLog(@"start new Promise..."); resolve(@123); }); OCPromise *multiply = function(^OCPromise * _Nullable(id _Nonnull value) { return Promise(^(resolve _Nonnull resolve, reject _Nonnull reject) { NSLog(@"calculating %ld x %ld ...", [value longValue], [value longValue]); resolve([NSNumber numberWithLong:[value longValue] * [value longValue]]); }); }); OCPromise *add = function(^OCPromise * _Nullable(id _Nonnull value) { return Promise(^(resolve _Nonnull resolve, reject _Nonnull reject) { NSLog(@"calculating %ld + %ld ...", [value longValue], [value longValue]); resolve([NSNumber numberWithLong:[value longValue] + [value longValue]]); }); }); p.then(multiply).deliverOnMainThread(^(id _Nonnull value) { NSLog(@"1 got value %@ on %@", value, [NSThread currentThread]); }).then(function(^OCPromise * _Nullable(id _Nonnull value) { NSLog(@"2 got value %@ on %@", value, [NSThread currentThread]); return Promise(^(resolve _Nonnull resolve, reject _Nonnull reject) { resolve(@([value longValue] * 10)); }); })).then(add).deliverOnMainThread(^(id _Nonnull value) { NSLog(@"3 got value %@ on %@", value, [NSThread currentThread]); }).then(function(^OCPromise * _Nullable(id _Nonnull value) { NSLog(@"4 got value %@ on %@", value, [NSThread currentThread]); return nil; }));2020-06-11 16:10:37.224503+0800 OCPromise_Example[56255:2885996] start new Promise...2020-06-11 16:10:37.224690+0800 OCPromise_Example[56255:2885996] calculating 123 x 123 ...2020-06-11 16:10:37.224909+0800 OCPromise_Example[56255:2885925] 1 got value 15129 on <NSThread: 0x6000022c85c0>{number = 1, name = main}2020-06-11 16:10:37.224920+0800 OCPromise_Example[56255:2885996] 2 got value 15129 on <NSThread: 0x60000228a300>{number = 6, name = (null)}2020-06-11 16:10:37.225055+0800 OCPromise_Example[56255:2885996] calculating 151290 + 151290 ...2020-06-11 16:10:37.225182+0800 OCPromise_Example[56255:2885996] 4 got value 302580 on <NSThread: 0x60000228a300>{number = 6, name = (null)}2020-06-11 16:10:37.225188+0800 OCPromise_Example[56255:2885925] 3 got value 302580 on <NSThread: 0x6000022c85c0>{number = 1, name = main}通过打印结果我们可以看到1、3都是在主线程执行的,并且主线程的接收并不影响子线程任务结果在任务链中的传递。deliverOnMainThread的实际调用逻辑就是在子线程接收到上一个任务的结果,然后为主线程添加一个异步任务来执行外部回调将结果传出去,并在当前线程继续将任务结果传递给下一个任务。 ...

June 16, 2020 · 3 min · jiezi

iOS无侵入埋点方案如何实现

在开发过程中,埋点可以解决两大类问题:一是了解用户使用 App 的行为,二是降低分析线上问题的难度。目前,iOS 开发中常见的埋点方式,主要包括: 代码埋点可视化埋点无埋点代码埋点 代码埋点主要就是通过手写代码的方式来埋点,能很精确的在需要埋点的代码处加上埋点的代码,可以很方便地记录当前环境的变量值,方便调试,并跟踪埋点内容,但存在开发工作量大,并且埋点代码到处都是,后期难以维护等问题。 缺点: 显而易见,你会在后期维护的时候写的怀疑人生复用性差,几乎不能移植给其他项目工作量大,而且会越写越多统计代码上线之后,如果出现问题,只能后续版本迭代如果统计项目名字改变了,原来老的APP版本依旧会统计老的页面名字优点: 如果非要写一个其他统计无法做到的优点的话,那就是可自定义程度高吧,统计代码想写到那里写到那里(其实这些也可以在后面的方案实现,只是实现上稍微麻烦一点罢了)最容易想到的方案(前期费时少,使用起来费手不费思路)可视化埋点 就是将埋点增加和修改的工作可视化了,提升了增加和维护埋点的体验。 该方案的具体步骤就是: 从后台获取需要统计的地方hook住需要统计的类的load方法来Method Swizzing要统计的方法上传统计到的事件给后台分析用UIViewController、UIControl为例子,讲解一下该方案的思路。 UIViewController PV统计,页面的统计较为简单,利用Method Swizzing hook 系统的viewDidLoad, 直接通过页面名称即可锁定页面的展示代码如下: UIControl 点击统计,主要通过hook sendAction:to:forEvent: 来实现, 其唯一标识符我们用 targetname/selector/tag来标记,具体代码如下: 缺点: 需要后台配合可拓展性不是很高,因为需要修改后台下发的统计内容来做每次的版本统计扩展优点: 相对于第一种方案,代码量少了很多。动态化从后台获取统计内容,方便线上修改无埋点 无埋点,并不是不需要埋点,而更确切地说是“全埋点”,而且埋点代码不会出现在业务代码中,容易管理和维护。它的缺点在于,埋点成本高,后期的解析也比较复杂,再加上 view_path 的不确定性。所以,这种方案并不能解决所有的埋点需求,但对于大量通用的埋点需求来说,能够节省大量的开发和维护成本。 在这其中,可视化埋点和无埋点,都属于是无侵入的埋点方案,因为它们都不需要在工程代码中写入埋点代码。所以,采用这样的无侵入埋点方案,既可以做到埋点被统一维护,又可以实现和工程代码的解耦。 接下来,我们就通过今天这篇文章,一起来分析一下无侵入埋点方案的实现问题吧。 运行时方法替换方式进行埋点 我们都知道,在 iOS 开发中最常见的三种埋点,就是对页面进入次数、页面停留时间、点击事件的埋点。对于这三种常见情况,我们都可以通过运行时方法替换技术来插入埋点代码,以实现无侵入的埋点方法。具体的实现方法是:先写一个运行时方法替换的类 ViewHook,加上替换的方法 hookClass:fromSelector:toSelector,代码如下: 这个方法利用运行时method_exchangeImplementations 接口将方法的实现进行了交换,原方法调用时就会被hook 住,从而去执行指定的方法。 页面进入次数、页面停留时间都需要对 UIViewController 生命周期进行埋点,你可以创建一个 UIViewController 的 Category,代码如下: 可以看到,Category 在+load() 方法里使用了 ViewHook 进行方法替换,在替换的方法里执行需要埋点的方法 [self insertToViewWillAppear]。 这样的话,每个UIViewController生命周期到了ViewWillAppear 时都会去执行insertToViewWillAppear 方法。 那么,我们要怎么区别不同的 UIViewController 呢?我一般采取的做法都是,使用NSStringFromClass([self class]) 方法来取类名。这样,我就能够通过类名来区别不同的 UIViewController 了。 ...

June 13, 2020 · 1 min · jiezi

中高级iOS大厂面试宝典

引言过年之后相信有一部分的人,早已磨刀霍霍向大厂。势必要大展拳脚,必将在大厂内创出一片天地。但是,想必大家都知道:最近几年的最严重的互联网寒冬来临,各位兄弟都会说“江湖再见”。耳边总是充刺着流言蜚语,这个地方裁员了,这个地方缩减HC。弄得人心慌慌。年后将是一片血雨腥风,程序界的江湖将在这一天精彩斑斓。 但我们要知道,寒冬之中,什么是最珍贵,就让鄙人告诉你:人才。只要有过硬的技术和装备,在逆风直下的情况下,咱们也能迎难而上,打他个戳手不及。不是“李云龙”大哥说:“过狭路相逢勇者胜.” 最近几天看了朋友,到处厮杀,经过一番斗争,最终夺下头筹获得多家大厂的offer。 承蒙兄弟抬爱,感情深厚。拿出《iOS中高级面试宝典》赠与小弟参悟。经过小弟我的反复参悟和整理,现在共享出来,希望与大家一起学习参悟。小弟我先说一下,面试虽然有技巧,但咋们绝不是吹嘘与伪造之辈,因先当花点时间静心闭关修炼,带到出关之日,必进大厂,薪资翻倍,岂不快哉!!! 参悟规则作为一名优秀的程序员,肯定是不会浪费时间在一个 : 一:没有晋升,没有职业发展的公司 二:也不会停留在某一个技术层面不前进的公司 三:我愿付出真心,你却不愿有待我的公司 本博客的知识点较多,花点时间一个个理解并记忆后,自然也就融会贯通,无所畏惧。面试iOS也就分分钟 本宝典为了便于记忆,快速达到应试状态,类似于复习知识大纲。知识点会尽量的精简与提炼知识脉络,并不去展开深入细节,面面俱到。有兴趣或者有疑问的兄弟可以自行谷歌下对应知识点的详细内容。 作为一个开发者,有一个学习的氛围跟一个交流圈子特别重要,这是一个我的iOS交流群:761407670 进群密码‘思否’,不管你是小白还是大牛欢迎入驻 ,分享BAT,阿里面试题、面试经验,讨论技术, 大家一起交流学习成长!1、swift和oc的区别(1) Swit没有地址指针的概念 (2)泛型 (3)类型严谨对比oc的动态绑定 2、编译连接id和instancetype的区别instancetype只能故返回值编译时判断真实类型,不符合发警告特殊情况:关联类型返回方法如类方法lloc或new开头实例方法中,以autorelease,init,retain,或self开头3、synthesize & denamic1:通过@sythesize 指令告诉编译器在编译期间产生getter/setter方法。 2:通过@dynamic指令,自己实现方法。 有些存取是在运行时动态创建的,如在CoreData的NSManagedObject类使 4、在项目开发中常用的开发工具有哪些?instrumentbeyondComparegit5、UlTableView & UlCollectionUlCollectionView是iOS6新引进的API,用于展示集合视图,布局更加灵活,其用法类似于UITableView。而UICollectionView、UlCollectionViewCell与UITableView、UITableViewCell在用法上有相似的也有不同的, 下面是一些基本的使用方法:对UITableView,仅需要UITableViewDataSource,UITableViewDelegate这两个协议使用UlCollectionView需要实现:UICollectionViewDataSource,UlCollectionViewDelegate, UlCollectionViewDelegateFlowLayout这三个协议.这是因为UlCollectionViewDelegateFlowL ayou实际上是UCollectionViewDelegate的一个子协议,它继承了 UlCollectionViewDelegate,它的作用是提供一些定义UlCollectionView布局模式的函数6、NSProxy & NSObjectNSObjetct:NSObject协议组对所有的Object -C下的objects都生效。如果objects遵从该协议,就会波看作是first -class objects (- 级类)。另外, 遵从该协议的objects的retain, release, autorelease等 方法也服从objects的管理和在 Foundation中定义的释放方法。- -些容器中的对象也可以管理这些objects,比如说NSArray和NSDictionary定义的对象。Cocoa的根类也遵循该协议,所以所有继承NSObjects的 objects都有遵循该协议的特性。NSProXY:NSProxy是一个虚基类,它为一些表现的像是其它对象替身或者并不存在的对象定义一套API。 -般 的,发送给代理的消息被转发给一个真实的对象或者代理本身load(或者将本身转换成)一个真实的对象。NSProxy的基类可以被用来透明的转发消息或者耗费巨大的对象的lazy初始化。7、Object & SwiftObejective-C复杂的语法,更加简单易用、有未来,让许多开发者心动不已.苹果宣称Swift的特点是:快速、现代、安全、互动,而且明显优于Objective-C语言 可以使用现有的Cocoa和Cocoa Touch框架 Swift取消了Objective C的指针及其他不安全访问的使用舍弃Objective C早期应用Smalltalk的语法,全面改为句点表示法提供了类似Java的名字空间(namespace)、 泛型(generic)、运算对象重载(operator overloading) Swift 被简单的形容为“没有C的Objective-C" (Objective- C without theC)为苹果开发工具带来了XcodePlaygrounds功能,该功能提供强大的互动效果,能让Swift源代码在撰写过程中实时显示出其运行结果; 基于C和Objective-C,而却没有C的一些兼容约束; 采用了安全的编程模式;界面基于Cocoa和Cocoa Touch框架;保留Smaltalk的动态特性8、传值通知&推送通知(本地&远程)传值通知:类似通知,代理,Block实现值得传递 推送通知:推送到用户手机对应的App上(主要是不再前台的情况)本地通知。 local notfication,用于基于时间行为的通知,比如有关日历或者todo列表的小应用。另外,应用如果在后台执 行,iOS允许它在受限的时间内运行,它也会发现本地通知有用。比如,一个应用,在后台运行,向应用的服 务器端获取消息,当消息到达时,比如下载更新版本的提示消息,通过本地通知机制通知用户。 ...

June 13, 2020 · 1 min · jiezi

22个iOS开发常用的开源项目

分享近期 GitHub 上比较流行的 22 个和 iOS 开发相关的开源项目。 包括开发辅助工具,异步编程库,JSON 解析,移动端数据库,图像视频处理,网络请求,UI 框架、组件,算法、数据结构等内容。 Accio 使用 Swift 编写的 iOS/tvOS/watchOS/macOS 依赖管理工具。 在当前 iOS 生态环境中,CocoaPods 和 Carthage 是最成熟的依赖管理器。 如果你不喜欢使用 Ruby 编写的 CocoaPods,或者更偏爱 Carthage 的非侵入方式,可以尝试下 Accio. Accio 改进了 Carthage 的一些问题,同时它的核心尽可能使用 SwiftPM,这样未来 Xcode 对 SwiftPM 支持更成熟时,可以很方便地迁移到 SwiftPM。 顺便提一下,Accio 读作 AH-kee-oh. SwiftLint 检查 Swift 代码风格、惯例。基于 Clang 和 SourceKit 提供的 AST 表示,因此可以提供更精准的分析结果。 idb 顾名思义,iOS 版本的 adb。这款 Facebook 开发的命令行工具可助你自动化在模拟器和真机上的调试流程。 InAppViewDebugger 供内嵌于应用的视图调试器。类似 Xcode 视图调试器,但可以在 iPad 和 iPhone 上调试视图。 MTHawkeye 美图秀秀开源的 iOS 调试优化辅助工具集。 内置插件有 LivingObjectSniffer (跟踪对象)、Allocations(跟踪实时分配内存)、UITimeProfiler(主线程耗时任务调优)、ANRTrace(捕获卡顿事件)、FPSTrace(跟踪界面 FPS 及 OpenGL 刷新绘制 FPS)、CPUTrace(跟踪 CPU 持续高使用率)、NetworkMonitor(监听记录应用内 HTTP(S) 网络请求各阶段耗时)、NetworkInspect(基于 Network Monitor 推荐可优化项,支持自定义规则)、OpengGLTrace(跟踪 OpenGL 资源内存占用)、DirectoryWatcher(跟踪沙盒文件夹大小)、FLEX(沙盒文件 AirDrop)。 ...

June 13, 2020 · 1 min · jiezi

MD5-AES-RSA对数据加密传输

网络安全面临的问题有 窃听、篡改、仿冒。这三种攻击方式,分别会破坏消息的私密性、完整性和通信双方的互信。 一 单向加密 哈希HASH散列 严格意义上不算一种加密算法MD5(Message Digest algorithm 5):消息摘要算法SHA(Secure Hash Algorithm):安全散列算法HMAC(Hash Message Authentication Code):散列消息鉴别码 给定一个密钥,对明文进行密钥拼接,并且做"两次散列",得到32位结果。CRC(Cyclical Redundancy Check):循环冗余码校验 MD5加密的特点:MD5Hash算法的特点:1:输入任意长度的信息,经过摘要处理,输出为32位字符(128位)的信息.(数字指纹)2:不同输入产生不同的结果,(唯一性)3:根据128位的输出结果不可能反推出输入的信息(不可逆) 不可逆运算对不同的数据加密的结果是定长的32位字符(不管文件多大都一样)对相同的数据加密,得到的结果是一样的(也就是复制)。抗修改性 : 信息“指纹”,对原数据进行任何改动,哪怕只修改一个字节,所得到的 MD5 值都有很大区别.弱抗碰撞 : 已知原数据和其 MD5 值,想找到一个具有相同 MD5 值的数据(即伪造数据)是非常困难的.强抗碰撞: 想找到两个不同数据,使他们具有相同的 MD5 值,是非常困难的 MD5 应用:一致性验证:MD5将整个文件当做一个大文本信息,通过不可逆的字符串变换算法,产生一个唯一的MD5信息摘要,就像每个人都有自己独一无二的指纹,MD5对任何文件产生一个独一无二的数字指纹。 SHA安全哈希算法(Secure Hash Algorithm)主要适用于数字签名标准(Digital Signature Standard DSS)里面定义的数字签名算法(Digital Signature Algorithm DSA)。对于长度小于2^64位的消息,SHA1会产生一个160位的消息摘要。当接收到消息的时候,这个消息摘要可以用来验证数据的完整性。在传输的过程中,数据很可能会发生变化,那么这时候就会产生不同的消息摘要。当让除了SHA1还有SHA256以及SHA512等。 SHA1有如下特性:不可以从消息摘要中复原信息;两个不同的消息不会产生同样的消息摘要。 MD5和SHA的比较因为二者均由 MD4 导出,SHA-1 和 MD5 彼此很相似。相应的,他们的强度和其他特性也是相似,但还有以下几点不同: 对强行攻击的安全性最显著和最重要的区别是 SHA-1 摘要比 MD5 摘要长 32 位。使用强行技术,产生任何一个报文使其摘要等于给定报摘要的难度对 MD5 是 2^128 数量级的操作,而对 SHA-1 则是 2^160 数量级的操作。这样,SHA-1 对强行攻击有更大的强度。 ...

June 10, 2020 · 1 min · jiezi

iOS本地缓存方案之YYCache源码解析

iOS持久化方案有哪些?简单列举一下,iOS的本地缓存方案有挺多,各有各的适用场景: NSUserDefault : 系统提供的最简便的key-value本地存储方案,适合比较轻量的数据存储,比如一些业务flag。主要原因还是其底层是用plist文件存储的,在数据量逐步变大后,可能会发生性能问题。 存文件,归档:无论是自己转换业务数据为二进制再writeFile,还是直接利用系统的NSKeyedArchiver接口归档成文件,都属于文件存储的方案。优势是开发简单,业务可以自行控制单文件的存储内容以避免可能发生的性能问题。 sqlite、FMDB:底层利用到数据的存储方案,比较适用数据量大,有查询,排序等需求的存储场景,缺点就是开发略复杂一些。 CoreData、其他ORM方案:CoreData感觉好像应用并不是很广泛? Key-Value接口的缓存方案:这里特指提供Key-Value形式接口的缓存库,底层缓存可能使用文件或者sqlite都有。本文讨论的YYCache底层是混合使用文件+sqlite的存储方式。基于接口简便,性能优于NSUserDefault的特性,应该适用于大多数的业务场景,但是无法适用上面数据库类似的使用场景。 聊聊YYCache的优秀设计这里其实yy大神本人在博文《YYCache 设计思路》中对其设计思路有比较详尽的介绍,建议大家可以先去读一读,本文就其相对于其他缓存库的一些优势点聊一聊。 高性能的线程安全方案首先高性能是YYCache比较核心的一个设计目标,挺多代码逻辑都是围绕性能这个点来做的。 作为对比,yy提出了TMMemoryCache方案的性能缺陷。TMMemoryCache的线程安全采用的是比较常见的通过dispatch_barrier来保障并行读,串行写的方案。该方案我在上一篇《AFNetworking源码解析与面试考点思考》中有介绍。那么TMMemoryCache存在性能问题的原因会是因为其dispatch_barrier的线程安全方案吗? 答案应该在其同步接口的设计上: - (id)objectForKey:(NSString *)key{ if (!key) return nil; __block id objectForKey = nil; dispatch_semaphore_t semaphore = dispatch_semaphore_create(0); [self objectForKey:key block:^(TMMemoryCache *cache, NSString *key, id object) { objectForKey = object; dispatch_semaphore_signal(semaphore); }]; dispatch_semaphore_wait(semaphore, DISPATCH_TIME_FOREVER); #if !OS_OBJECT_USE_OBJC dispatch_release(semaphore); #endif return objectForKey;}TMCache在同步接口里面通过信号量来阻塞当前线程,然后切换到其他线程(具体代码在其异步接口里面,是通过dispatch_async到一个并行队列来实现的)去执行读取操作。按照yy的说法主要的性能损耗应该在这个线程切换操作,同步接口没必要去切换线程执行。 yy这边的思路是通过自旋锁来保证线程安全,但仍然在当前线程去执行读操作,这样就可以节省线程切换带来的开销。(不过我在YYCache的最新代码里看到的是普通的互斥锁,并没有用自旋锁,应该是后面又做了方案上的修改?) 除了加锁串行,dispatch_sync实现同步的方案是否可行呢?除了yy提供的加锁串行方案,我们来看看前面介绍过的barrier并行读串行写方案是否也存在性能问题。如果使用该方案,同步接口可能是这样的: - (id)objectForKey:(NSString *)key{ __block id object = nil; dispatch_sync(concurrent_queue, ^{ object = cache[key]; // 读接口,不用barrier,保证读与读能够并行 }); return object;}- (void)setObject:(id)object forKey:(NSString *)key{ dispatch_barrier_sync(concurrent_queue, ^{ // 写接口,barrier保证与读互斥 cache[key] = object; });}经过demo验证,可以发现虽然是dipatch到一个concurrent_queue中执行,但是由于是sync同步派发,实际上并不会切换到新的线程执行。也就是说该方案也能做到节省线程切换的开销。 ...

June 8, 2020 · 2 min · jiezi

iOS-动画-窗景篇二

本文是系列文章的第二篇。 看过上一篇文章的同学,已经知道标题中的“景”指代 view,“窗”指代 view.mask,窗景篇就是在梳理 mask 及 mask 动画。如果你还不熟悉 iOS 的 mask,建议先看一下第一篇。 相对于景来说,窗的变化更多样一些,所以本文我们重点来看一下窗的效果。 我们从3个维度来看:窗在动吗?窗在变吗?有几个窗? 很多动画就是这3个维度的单独体现,或者组合后的效果。我们先看一下各个维度的单独效果,然后再来看一下它们的组合效果。 一、窗动前文中,我们用一个圆作为窗,先贴张图回忆一下: 我们大都做过基本的动画,因此可以想到,只要动画地改变圆 mask 的中心位置,就可以让窗动起来。 效果如下面的动图所示: 示意代码如下: /// viewDidLoad// 景frontView.frame = UIScreen.main.boundsview.addSubview(frontView)// 圆窗let mask = CircleView()mask.frame = CGRect(x: 0, y: 0, width: 200, height: 200)mask.center = CGPoint(x: 100, y: 100)self.mask = maskfrontView.mask = mask// 窗动startAnimation()/// startAnimation// 动画地改变 mask 的中心private func startAnimation() { mask.layer.removeAllAnimations() let anim = CAKeyframeAnimation(keyPath: "position") let bound = UIScreen.main.bounds anim.values = [CGPoint(x: 100, y: 250), CGPoint(x:bound.width - 100 , y: 250), CGPoint(x: bound.midX, y: 450), CGPoint(x: 100, y: 250)] anim.duration = 4 anim.repeatCount = Float.infinity mask.layer.add(anim, forKey: nil)}让窗动起来非常简单,这简单的效果也可以成为其他效果的基础。 ...

June 8, 2020 · 4 min · jiezi

事件池当然-RxSwift-做的非常好

网络请求到的数据, BehaviorSubject 作为事件仓库 store, BehaviorSubject 用户行为事件源, 保持住最新的事件。 用户行为点击,采用 PublishSubject 即时事件源,马上 fire ,作为开关,发起 BehaviorSubject 存储的事件 场景:网络请求到的数据,不是马上展示。是用户点击某个按钮后,才展示网络请求到的数据,不是马上展示,就需要用 BehaviorSubject 把这个事件 hold 住。 用户点击某个按钮后,才展示。采用 PublishSubject, 作为上面事件的开关,fire 开启事件 代码部分: struct PerViewModel { private let inputTap = PublishSubject<Bool>() private let inputNet = BehaviorSubject<Model?>(value: nil) var outputNetPop: Observable<Model?> init() { let netOK = inputNet.filter { $0 != nil } outputNetPop = netOK.sample(inputTap) } func click(){ inputTap.onNext(true) } func popUp(net data: Model){ inputNet.onNext(data) }}RxSwift 的 Operator , Sample 具有这个特性 ...

June 4, 2020 · 1 min · jiezi

pod-做了什么子-project-作为动态库三步走

pod 做了什么,子 project 作为动态库 framework,三步走 第一步,添加项目引用: 第二步: 跑一下子 project ,把其 product 作为 framework 第 3 步:把其 product 的 framework,拖入主 project 的依赖 最后,能跑通

June 4, 2020 · 1 min · jiezi

iOS-动画-窗景篇一

iOS 有一种动画,使用虽然简单,但能实现很多有趣的效果,那就是 mask 动画。 如果你还不了解 mask 动画,看完本系列文章后,你可以学会这种动画。如果你已经使用过了,本文也能帮你梳理一下,让你使用起来更方便。 本系列文章共3篇,作为系列的开篇,我们首先要搞清楚一个问题:什么是 mask。 一、什么是 maskmask 是 UIView 或 CALayer 的一个属性,它决定了 view 或 layer 的哪一部分能被我们看到。 本文为了方便讲述,主要使用 view 和它的 mask 属性。iOS 对 mask 的描述,对很多人来说不是特别直观,所以在贴出定义之前,我们先尝试直观地看一下。 首先,我们来看一下这张图: 如图所示,一张纸上有个圆洞,纸盖在了左边的图片上,图片的一部分通过这个洞透了过来,就像墙上开了一扇窗,让我们看到了一部分风景。 不严谨的说,中间的这张黑纸就是 mask,它决定了 view 的哪一部分能被我们看到。 不过这张图会误导我们,让我们感觉 mask 挡住了 view,其实并不是这样。我们来看一下这张图: 从这张图中,我们可以看到:frontView.mask 只影响了 frontView 哪部分可以被我们看到 ,对后面的 backView 没有任何影响。看上去,mask 更像是对 view 进行了裁剪。 上面的两张图并不符合 iOS 对 mask 的描述, 但通过这两张图,我们应该对 “mask 决定了 view 的哪一部分能被我们看到” 这句话,有了直观的印象。 接下来,我们就一起来看一下 iOS 对 mask 的描述。 ...

June 4, 2020 · 3 min · jiezi

赞值得收藏的-iOS-在线资料大全

来源:https://github.com/kechengsou... 发现一个不错的 iOS 资料合(dà)集( quán ),资料非常多,比我之前看到的资料合集要好,排版也比较清晰,还有pdf版可以下载,也有 html 版。 不过貌似项目刚启动(我发现的早~),觉得有用的同学可以去点个 star。 /*** * ┌─┐ ┌─┐ * ┌──┘ ┴───────┘ ┴──┐ * │ │ * │ ─── │ * │ ─┬┘ └┬─ │ * │ │ * │ ─┴─ │ * │ │ * └───┐ ┌───┘ * │ │ * │ │ * │ │ * │ └──────────────┐ * │ │ * │ ├─┐ * │ ┌─┘ * │ │ * └─┐ ┐ ┌───────┬──┐ ┌──┘ * │ ─┤ ─┤ │ ─┤ ─┤ * └──┴──┘ └──┴──┘ * 神兽保佑 * 代码无BUG! */

June 3, 2020 · 1 min · jiezi

iOS-swift-关闭包

一. 闭包表达式(Closure Expression)在Swift中,可以通过func定义一个函数,也可以通过闭包表达式定义一个函数,闭包表达式和闭包是两回事 闭包表达式的格式如下: { (参数列表) -> 返回值类型 in 函数体代码}通过func定义一个函数: func sum(_ v1: Int, _ v2: Int) -> Int { v1 + v2 }通过闭包表达式定义一个函数: //通过闭包表达式定义一个函数,然后调用var fn = { (v1: Int, v2: Int) -> Int in return v1 + v2}fn(10, 20) //闭包调用的时候默认不用写参数名称的 //通过闭包表达式定义一个函数,并且直接调用{(v1: Int, v2: Int) -> Int in return v1 + v2}(10, 20)二. 闭包表达式的简写//传入两个Int变量,返回一个函数func exec(v1: Int, v2: Int, fn: (Int, Int) -> Int) { print(fn(v1, v2))}//什么都不省略exec(v1: 10, v2: 20, fn: { (v1: Int, v2: Int) -> Int in return v1 + v2})//省略参数类型、返回值类型 (因为编译器能推断出来)exec(v1: 10, v2: 20, fn: { v1, v2 in return v1 + v2})//省略returnexec(v1: 10, v2: 20, fn: { v1, v2 in v1 + v2})//超级简写:$0代表第一个参数,$1代表第二个参数exec(v1: 10, v2: 20, fn: { $0 + $1 })//终极简写:只用一个+exec(v1: 10, v2: 20, fn: +)三. 尾随闭包如果将一个很长的闭包表达式作为函数的最后一个实参,使用尾随闭包可以增强函数的可读性。 尾随闭包是一个被书写在函数调用括号外面(后面)的闭包表达式。 ...

June 2, 2020 · 5 min · jiezi

iOS-NSRunLoop-介绍

来自:https://www.jianshu.com/p/d71... 是什么?RunLoop其实是iOS中的一种消息机制的处理模式。字面的意识就是跑圈,那就是循环了呗。对,就是循环! 理解:学过C语言的同学都知道,每个程序从开始运行到完成需要的计算后打印台打印出你需要的信息后就结束了任务。 那么对于我们的手机来说,任何应用在前台他都是在一直处于运行状态的,随时等待你的命令,对吧!那为什么他在做完你一次的命令任务后退出呢? 这个问题的核心就是RunLoop。 因为RunLoop一直在跑圈啊,一直在循环啊,一直在运行啊,你没有杀死进程啊,So...一直运行着等待你的命令。 作用:保持程序的持续运行处理App中的各种事件(手势、定时器、Selector等)节省CPU资源、提高程序性能:该做任务的时候做任务,没事干的时候休息。理解重点:RunLoop和线程是一对一的关系。比如我们创建项目的时候,刚创建项目的时候就会有个主线程,而这个时候就已经创建了RunLoop了,所以说在线程创建之初就创建了RunLoop(只限于主线程,而子线程需要我们去主动获取),因此线程存在于线程的整个生命周期。 然而好多人问子线程怎么销毁RunLoop? 首先说主线程的RunLoop不能销毁啊,你销毁了程序就退出了。我们现在基本所有应用都用是ARC,我们不需要对内存进行费时的管理,系统会在一个子线程完成的时候销毁掉这个子线程,因此子线程的RunLoop也就跟着自动销毁了。RunLoop对象 在我们开发iOS中有2套API来访问RunLoop Foundation NSRunLoopCore Foundation CFRunLoopRef首先NSRunLoop和CFRunLoopRef都是RunLoop的对象,前者是Oc,后者是C。前者是苹果公司对后者的封装,在正常的开发中NSRunLoop已经够我们使用了,想要了解更多,使用更多,研究更多就去看后者,他是开源的,不过也很难看懂。其实RunLoop底层C都是用一个结构体完成的,里面有各种指针、指针函数、bool值、变量等等等。。。。 RunLoop相关类 RunLoop结构图.png Core Foundation中关于RunLoop的5个类 CFRunLoopRefCFRunLoopModeRefCFRunLoopSourceRefCFRunLoopTimerRefCFRunLoopObserverRef这五个类按照这个结构图一一对应。 CFRunLoopRef代表RunLoop的实体类,一个RunLoop中包含若干个Mode,而每个mode又包含若干个Source/Timer/Observer。 重点: 每次运行RunLoop都必须指定其中一个mode,如果没有mode,RunLoop无法运行,而这个mode被称为当前mode。如果要切换mode,只能退出当前RunLoop,然后再重新指定个mode进入。为什么要像2这样做呢?不是很麻烦吗?苹果这样做是为了区分不同组的Source/Timer/Observer。系统默认的5个modeNSDefaultRunLoopMode 这个mode一般是主线程RunLoop的默认mode。创建线程之初RunLoop是以这种mode运行的。UITrackingRunLoopMode 这个mode是保证滑动ScrollView滑动不受影响,比如滑动tableView的时候主线程就切到这个mode上了。UITInitializationRunLoopMode 在刚启动App的时候第一次进入的mode,启动后就不进入此mode了,本人理解是为了防止App进入时选择了其他mode而运行错乱。GSEventReceiveRunLoopMode 接受系统内部的mode,通常不用。NSRunLoopCommonModes 这个mode是包换1和2的,因此解有个一个问题。定时器触发的时候滑动TableView定时器会停止。这是因为默认是在第一个mode上运行,在滑动的时候RunLoop切换到了第二个mode,所以第一个mode上的任务就被搁置了。 So,解决这个问题就直接把Timer加入到第五个mode中就完美解决了,滑动的时候也不影响定时器的触发。事件源(输入源)Source是事件源也是输入源,比如点击Button按钮、滑动TableView都是一种事件源,告诉RunLoop需要去做什么! CFRunLoopSourceRef分两种: Source0:非基于Port端口,自定义的方法函数、SelectorPerform。简单理解就是你写的就是。Source1:基于Port端口,系统提供默认的方法函数,比如UIApplicationMain。定时器这个就不说了,在上面第五个mode已经说了。 CFRunLoopObserverRefCFRunLoopObserverRef是观察者,能够监听RunLoop所有的状态改变。 可以监听的时间点有如下几种: RunLoopObserver监听的时间点.png 从上往下是: 即将进入RunLoop 即将处理Timer 即将处理Source 即将进入休眠 即将从休眠中被唤醒 即将退出RunLoop 所有状态 这是个枚举,枚举值是2的0次方,1次方,2次方...... 比如在点击按钮的这个操作过程中,RunLoop会把这些状态全部按照任务的顺序走一遍。或者你在滑动,轻扫等操作,RunLoop都会接收到并走这些状态,然后执行任务。 因此有这些状态的判断就可以在好多Oc做不到的地方搞些事情,但是NSRunLoop里面没有提供Observer的这些状态,所以需要用CoreFoundation框架来写 NSRunLoop 结构就是这样的!目前介绍完了。 应用NSRunLoop的应用: 延迟显示ImageView 用RunLoop。(performSelector: withObject: afterDelay: inModes:)这个方法NSTimer。 需要加入mode常驻线程,就是一直让线程活着。其实就是在创建线程的时候,打开当前RunLoop,然后加入mode,然后Run。这个AFNetWorking中也用到了RunLoop(其实是写了个单例创建了一个线程,然后打开RunLoop,加入了mode的SourcePort)把3加入@autoreleasepool中,在runloop睡眠的时候释放。

June 2, 2020 · 1 min · jiezi

几天用Flutter撸了个新浪微博

谷歌在2018年12月正式发布了Flutter,这是一个出色的跨平台框架,可用于移动、桌面和 Web 平台构建应用程序,发布不到一年,它的流行度就超过了React Native以及同领域的产品。从下图就可以看出: 相比其他选项,Flutter有着许多独有的优势。这些优势融入了基础语言和SDK的设计中,以解决其他技术的常见问题和缺陷,简单来说,Flutter的优势主要为以下几个方面:Flutter应用程序可编译为原生二进制文件,在性能表现和流畅的渲染方面,是真正的原生构建应用很难超越的。 除了性能表现之外,Flutter之所以获得如此广泛的使用率,另一大因素就是它提供的出色文档和可供参考的大批高质量示例。 最后,Flutter是基于出色的语言(Dart)和快速的高性能渲染引擎(Skia)从头开始设计出来的,能满足不同技能水平的开发人员基于良好的设计模式和最佳实践来构建应用。 许多开发人员都能够通过Flutter在短时间内创建出高性能的应用程序,这不,前阵子,就有人通过Flutter撸了一个新浪微博,还原微博80%的界面,总共涉及到了几十个界面和接口,用到了Flutter中的大部分组件。 想要学习Flutter的伙伴们可以做一些参考,下面是该项目实现的一些界面: 首页模块: 视频模块: 发现模块: 消息模块: 创建者还给出了相关的一些第三方库: 如果你觉得这个项目对你学习Flutter会有所帮助,就赶紧参考一下吧,最后附上项目地址:github.com/huangruiLea… 面试题持续更新记得关注我哦! 不同的圈子就有不同的学习方式 ; (qq群搜索):651612063 群密码:111 进群文件可以直接获取大厂面试题 推荐点击进群密码:111

May 30, 2020 · 1 min · jiezi

Xcode-Multiple-commands-produce-DerivedData

Multiple commands produce '/Users/.../Library/Developer/Xcode/DerivedData/...方法如下:1:打开菜单栏File,点击Workspace settings2:点击build system3:将build system改为 Legacy Build system

May 27, 2020 · 1 min · jiezi

盘古实验室报告多个-iOS-安全漏洞获苹果官方致谢临时解锁是条件竞争漏洞的重要成因

技术编辑:徐九丨发自 思否编辑部 近日,苹果发布了 iOS/iPadOS 13.5 正式版,新增了 Face ID 增加口罩探测、新冠密切接触追踪等与新冠疫情有关的功能,此外也修复了一些之前的系统 Bug。 但似乎此次的正式版系统仍然存在很多安全问题和漏洞,5 月 26 日,苹果于官网发布了一篇题为《关于 iOS 13.5 和 iPadOS 13.5 的安全报告》的文档公告,详细描述了 iOS 13.5 和 iPadOS 13.5 的安全更新。 据专注于移动互联网安全的「盘古实验室」表示,去年他们在 Black Hat USA 有个议题《Attacking iPhone XS Max》,介绍了如何利用 Unix socket bind 函数中条件竞争导致的 socket UAF 漏洞实现 iPhone XS Max 越狱,并总结到:“临时解锁”是条件竞争漏洞的一个重要成因。 在该基础上,盘古实验室扩大了研究范围,把 XNU 内核中其它 socket 模块也做了分析。基于这个特征,又发现了更多类似的安全漏洞,相继发现内存越界、整数溢出、堆溢出、UAF、多次释放、类型混淆、未初始化变量使用等各种安全漏洞。并从中挑选了十几个典型的漏洞,批量报给了苹果。 此次苹果发布的 iOS 13.5 中对其中的 6 个漏洞进行了修复。 盘古团队是由多名资深安全研究人员组成的专业安全研究团队。团队在主流操作系统和核心软件产品中发现过数百个安全漏洞,因连续多次发布 iOS 完美越狱工具而闻名,是国内首个自主实现苹果iOS完美越狱的团队,也是全球范围内第一个实现针对 iOS 8 和 iOS 9 系统完美越狱的团队。盘古越狱工具被全世界的用户下载数千万次。 ...

May 27, 2020 · 1 min · jiezi

使用-protocol-和-callAsFunction-改进-Delegate指针

2018 年 3 月的时候我写过一篇在 Swift 中如何改进 Delegate Pattern 的文章,主要思想是用遮蔽变量 (shadow variable) 声明的方式,来保证 self 变量可以被常时地标记为 weak。本文中,为了保证没有看过原文的读者能处在同一频道,我会先 (再次) 简单介绍一下这种方法。然后,结合 Swift 5.2 的新特性提出一些小的改进方式。 Delegate简单说,为了避免繁琐老式的 protocol 定义和实现,我们可能更倾向于选择提供闭包的方式完成回调。比如在一个收集用户输入的自定义 view 中,提供一个外部可以设置的函数类型变量 onConfirmInput,并在合适的时候调用它: class TextInputView: UIView { @IBOutlet weak var inputTextField: UITextField! var onConfirmInput: ((String?) -> Void)? @IBAction func confirmButtonPressed(_ sender: Any) { onConfirmInput?(inputTextField.text) } } 在 TextInputView 的 controller 中,检测 input 确定事件就不需要一堆 textInputView.delegate = self 和 textInputView(_:didConfirmText:) 之类 的麻烦事了,可以直接设置 onConfirmInput: class ViewController: UIViewController { @IBOutlet weak var textLabel: UILabel! override func viewDidLoad() { super.viewDidLoad() let inputView = TextInputView(frame: /*...*/) inputView.onConfirmInput = { text in self.textLabel.text = text } view.addSubview(inputView) } } ...

May 27, 2020 · 3 min · jiezi

SwiftUI-Instafilter-基本-UI-和图片导入

构建基本 UI项目的第一步是构建基本的用户接口,对于这个应用来说包括: 一个 NavigationView ,用以在顶部展示应用的名称一个灰色的矩形,显示 “点击以选择图片”,我们导入的图片会放在这里。一个 “强度” 滑块,用来控制应用的 Core Image 滤镜的程度,存储从 0.0 到 1.0 的数值。一个 “保存” 按钮,用来写入修改后的图片到用户的相册。一开始用户还没有选择图片,所以对于图片我们需要用可选的 @State 属性。 首先下面两个属性到 ContentView: @State private var image: Image?@State private var filterIntensity = 0.5复制代码然后修改 body 属性: NavigationView { VStack { ZStack { Rectangle() .fill(Color.secondary) // 显示图片 } .onTapGesture { // 选择图片 } HStack { Text("强度") Slider(value: self.$filterIntensity) }.padding(.vertical) HStack { Button("切换滤镜") { // 切换滤镜 } Spacer() Button("保存") { // 保存图片 } } } .padding([.horizontal, .bottom]) .navigationBarTitle("Instafilter")}复制代码这里有很多的占位符,随着工程的推进,我们逐步填充。 ...

May 26, 2020 · 3 min · jiezi

2020年大厂面试题

1、NSArray与NSSet的区别? NSArray内存中存储地址连续,而NSSet不连续NSSet效率高,内部使用hash查找;NSArray查找需要遍历NSSet通过anyObject访问元素,NSArray通过下标访问2、NSHashTable与NSMapTable? NSHashTable是NSSet的通用版本,对元素弱引用,可变类型;可以在访问成员时copyNSMapTable是NSDictionary的通用版本,对元素弱引用,可变类型;可以在访问成员时copy(注:NSHashTable与NSSet的区别:NSHashTable可以通过option设置元素弱引用/copyin,只有可变类型。但是添加对象的时候NSHashTable耗费时间是NSSet的两倍。 NSMapTable与NSDictionary的区别:同上) 3、属性关键字assign、retain、weak、copy assign:用于基本数据类型和结构体。如果修饰对象的话,当销毁时,属性值不会自动置nil,可能造成野指针。weak:对象引用计数为0时,属性值也会自动置nilretain:强引用类型,ARC下相当于strong,但block不能用retain修饰,因为等同于assign不安全。strong:强引用类型,修饰block时相当于copy。4、weak属性如何自动置nil的? Runtime会对weak属性进行内存布局,构建hash表:以weak属性对象内存地址为key,weak属性值(weak自身地址)为value。当对象引用计数为0 dealloc时,会将weak属性值自动置nil。5、Block的循环引用、内部修改外部变量、三种block block强引用self,self强引用block内部修改外部变量:block不允许修改外部变量的值,这里的外部变量指的是栈中指针的内存地址。__block的作用是只要观察到变量被block使用,就将外部变量在栈中的内存地址放到堆中。三种block:NSGlobalBlack(全局)、NSStackBlock(栈block)、NSMallocBlock(堆block)6、KVO底层实现原理?手动触发KVO?swift如何实现KVO? KVO原理:当观察一个对象时,runtime会动态创建继承自该对象的类,并重写被观察对象的setter方法,重写的setter方法会负责在调用原setter方法前后通知所有观察对象值得更改,最后会把该对象的isa指针指向这个创建的子类,对象就变成子类的实例。如何手动触发KVO:在setter方法里,手动实现NSObject两个方法:willChangeValueForKey、didChangeValueForKeyswift的kvo:继承自NSObject的类,或者直接willset/didset实现。7、categroy为什么不能添加属性?怎么实现添加?与Extension的区别?category覆盖原类方法?多个category调用顺序 Runtime初始化时categroy的内存布局已经确定,没有ivar,所以默认不能添加属性。使用runtime的关联对象,并重写setter和getter方法。Extenstion编译期创建,可以添加成员变量ivar,一般用作隐藏类的信息。必须要有类的源码才可以添加,如NSString就不能创建Extension。category方法会在runtime初始化的时候copy到原来前面,调用分类方法的时候直接返回,不再调用原类。如何保持原类也调用(https://www.jianshu.com/p/40e28c9f9da5)。多个category的调用顺序按照:Build Phases ->Complie Source 中的编译顺序。8、load方法和initialize方法的异同。——主要说一下执行时间,各自用途,没实现子类的方法会不会调用父类的? load initialize 调用时机 app启动后,runtime初始化的时候 第一个方法调用前调用 调用顺序 父类->本类->分类 父类->本类(如果有分类直接调用分类,本类不会调用) 没实现子类的方法会不会调用父类的 否 是 是否沿用父类实现 否 是 9、对 runtime 的理解。——主要是方法调用时如何查找缓存,如何找到方法,找不到方法时怎么转发,对象的内存布局 OC中向对象发送消息时,runtime会根据对象的isa指针找到对象所属的类,然后在该类的方法列表和父类的方法列表中寻找方法执行。如果在最顶层父类中没找到方法执行,就会进行消息转发:Method resoution(实现方法)、fast forwarding(转发给其他对象)、normal forwarding(完整消息转发。可以转发给多个对象) 10、runtime 中,SEL和IMP的区别? 每个类对象都有一个方法列表,方法列表存储方法名、方法实现、参数类型,SEL是方法名(编号),IMP指向方法实现的首地址 11、autoreleasepool的原理和使用场景? 若干个autoreleasepoolpage组成的双向链表的栈结构,objc_autoreleasepoolpush、objc_autoreleasepoolpop、objc_autorelease使用场景:多次创建临时变量导致内存上涨时,需要延迟释放autoreleasepoolpage的内存结构:4k存储大小 12、Autorelase对象什么时候释放? 在没有手加Autorelease Pool的情况下,Autorelease对象是在当前的runloop迭代结束时释放的,而它能够释放的原因是系统在每个runloop迭代中都加入了自动释放池Push和Pop。 13、Runloop与线程的关系?Runloop的mode? Runloop的作用?内部机制? 每一个线程都有一个runloop,主线程的runloop默认启动。mode:主要用来指定事件在运行时循环的优先级作用:保持程序的持续运行、随时处理各种事件、节省cpu资源(没事件休息释放资源)、渲染屏幕UI14、iOS中使用的锁、死锁的发生与避免 @synchronized、信号量、NSLock等死锁:多个线程同时访问同一资源,造成循环等待。GCD使用异步线程、并行队列15、NSOperation和GCD的区别 GCD底层使用C语言编写高效、NSOperation是对GCD的面向对象的封装。对于特殊需求,如取消任务、设置任务优先级、任务状态监听,NSOperation使用起来更加方便。NSOperation可以设置依赖关系,而GCD只能通过dispatch_barrier_async实现NSOperation可以通过KVO观察当前operation执行状态(执行/取消)NSOperation可以设置自身优先级(queuePriority)。GCD只能设置队列优先级(DISPATCH_QUEUE_PRIORITY_DEFAULT),无法在执行的block中设置优先级NSOperation可以自定义operation如NSInvationOperation/NSBlockOperation,而GCD执行任务可以自定义封装但没有那么高的代码复用度GCD高效,NSOperation开销相对高16、oc与js交互 拦截urlJavaScriptCore(只适用于UIWebView)WKScriptMessageHandler(只适用于WKWebView)WebViewJavaScriptBridge(第三方框架)17、swift相比OC有什么优势? 18、struct、Class的区别 class可以继承,struct不可以class是引用类型,struct是值类型struct在function里修改property时需要mutating关键字修饰19、访问控制关键字(public、open、private、filePrivate、internal) public与open:public在module内部中,class和func都可以被访问/重载/继承,外部只能访问;而open都可以private与filePrivate:private修饰class/func,表示只能在当前class源文件/func内部使用,外部不可以被继承和访问;而filePrivate表示只能在当前swift源文件内访问internal:在整个模块或者app内都可以访问,默认访问级别,可写可不写20、OC与Swift混编 OC调用swift:import "工程名-swift.h” @objcswift调用oc:桥接文件21、map、filter、reduce?map与flapmap的区别? map:数组中每个元素都经过某个方法转换,最后返回新的数组(xx.map({$0 \* $0}))flatmap:同map类似,区别在flatmap返回的数组不存在nil,并且会把optional解包;而且还可以把嵌套的数组打开变成一个([[1,2],[2,3,4],[5,6]] ->[1,2,2,3,4,5,6])filter:用户筛选元素(xxx.filter({$0 > 25}),筛选出大于25的元素组成新数组)reduce:把数组元素组合计算为一个值,并接收初始值() 22、guard与defer guard用于提前处理错误数据,else退出程序,提高代码可读性defer延迟执行,回收资源。多个defer反序执行,嵌套defer先执行外层,后执行内层23、try、try?与try! try:手动捕捉异常try?:系统帮我们处理,出现异常返回nil;没有异常返回对应的对象try!:直接告诉系统,该方法没有异常。如果出现异常程序会crash24、@autoclosure:把一个表达式自动封装成闭包 25、throws与rethrows:throws另一个throws时,将前者改为rethrows ...

May 25, 2020 · 1 min · jiezi

iOS知识梳理-异步编程-coobjc学习

这几年异步编程是个比较热门的话题。 今天我们在iOS平台下简单聊聊异步编程和coobjc。 首先要回答一个问题,我们为什么需要异步编程? 早年的时候,大家都很习惯于开一个线程去执行耗时任务,即使这个耗时任务并非CPU密集型任务,比如一个同步的IO或网络调用。但发展到今日,大家对这种场景应该使用异步而非子线程的结论应当没什么疑问。开线程本身开销相对比较大,并且多线程编程动不动要加锁,很容易出现crash或更严重的性能问题。而iOS平台,系统API有不少就是这种不科学的同步耗时调用,并且GCD的API算是很好用的线程封装,这导致iOS平台下很容易滥用多线程引发各种问题。 总而言之,原则上,网络、IO等很多不耗CPU的耗时操作都应该优先使用异步来解决。 再来看异步编程的方案,iOS平台下常用的就是delegate和block回调。delegate导致逻辑的割裂,并且使用场景比较注重于UI层,对大多数异步场景算不上好用。 而block回调语法同样有一些缺陷。最大的问题就是回调地狱: [NSURLConnection sendAsynchronousRequest:rq queue:nil completionHandler:^(NSURLResponse * _Nullable response, NSData * _Nullable data, NSError * _Nullable connectionError) { if (connectionError) { if (callback) { callback(nil, nil,connectionError); } } else{ dispatch_async(dispatch_get_global_queue(0, 0), ^{ NSDictionary *dict = [NSJSONSerialization JSONObjectWithData:data options:0 error:nil]; NSString *imageUrl = dict[@"image"]; [NSURLConnection sendAsynchronousRequest:[NSURLRequest requestWithURL:[NSURL URLWithString:imageUrl]] queue:nil completionHandler:^(NSURLResponse * _Nullable response, NSData * _Nullable data, NSError * _Nullable connectionError) { dispatch_async(dispatch_get_global_queue(0, 0), ^{ if (connectionError) { callback(nil, dict,connectionError); } else{ UIImage *image = [[UIImage alloc] initWithData:data]; if (callback) { (image, dict, nil); } } }); }]; }); } }]不过iOS开发中好像并没有觉得很痛,至少没有前端那么痛。可能是因为我们实际开发中对比较深的回调会换成用delegate或notificaiton机制。但这种混杂的使用对代码质量是个挑战,想要保证代码质量需要团队做很多约束并且有很高的执行力,这其实很困难。 ...

November 5, 2019 · 3 min · jiezi

苹果-iOS-132-改善隐私数据保护兑现承诺不主动收集用户录音

近日,苹果公司发布了最新的 iOS 13.2 更新,更新说明中指出,手机语音助手 Siri 已经改变了其处理用户隐私数据的方式,而该问题此前一直备受 iPhone 用户关注。 在这次系统更新中,与 Siri 有关的变化主要有两点:1、安装更新后,用户需要手动选择是否希望向苹果公司共享音频历史记录,且今后也可以随时修改选择。2、用户可以随时在设置界面中选择删除存储在苹果公司服务器上的那些与设备相关联的音频历史记录。 据外媒今年 7 月底的报道,苹果公司之前曾雇佣外包公司,通过人工收听录音记录的方式来鉴别 Siri 用户是进行了误唤醒操作,还是真的想要提问,以及提问后 Siri 的回答是否有帮助。这些录音记录内容可能涉及了用户的部分个人隐私。 但苹果公司在之后的一份声明中提出,只有 Siri 日活量不到 1% 的用户录音可能会被筛选出来用于分析并改进 Siri 的用户体验,这些工作均是在安全可靠的环境中完成的,且录音内容并没有与用户的 Apple ID 进行关联。 接着在 8 月底,苹果公司就在其官网站上发布了正式声明,为其在用户隐私保护方面的疏忽进行了道歉,并明确指出人工听取用户录音的行为已被叫停,另外也解释了苹果公司是如何保护用户隐私数据及改进 Siri 用户体验的。 在声明的最后,苹果公司还承诺将从三个方面做出改变,而其中的两点已经在 iOS 13.2 版本更新中有所体现,第三点则是仅限苹果员工才能有机会接触到用户录音,并会努力删除那些因为误唤醒问题而生成的录音样本。 据了解,除了苹果以外还有其他公司也采用了类似的方法来改进语音助手。究竟该如何去平衡隐私与便利呢?恐怕目前也没有固定的标准答案了。

November 4, 2019 · 1 min · jiezi

mac-打开ios模拟器

命令行输入open -a Simulatorsimulator:模拟器 关于open命令:open,使用关联的程序打开文件,例:open a.txt会用文本编辑打开a.txt,open b.jpg会使用预览打开b.jpgopen -e,强制使用文本编辑程序打开文件open -a,自行选择程序打开文件,例:open -a Preview b.jpg会使用预览打开b.jpg,另外使用此命令输入已安装的程序名可直接打开,而open则需要知道程序存放的路径才行,例:open -a Preview等同于open /Applications/Preview.app

November 4, 2019 · 1 min · jiezi

css适配iPhoneX屏幕安全区

场景iPhoneX对比起以前其他的手机,屏幕顶部变成了留海屏,底部取消了物理按键改成了小黑条,这种改动导致了web开发中页面上新的适配问题。 比如一些需要贴在底部的按钮,和呼起的tabBar和底部弹出框,在iphoneX上就会出现被小黑条遮挡内容,或者页面上出现白色空隙的问题。 先上解决代码<head><meta content="width=device-width,initial-scale=1,maximum-scale=1,user-scalable=0,viewport-fit=cover" name="viewport"/></head>body {height: 100vh;}/* 你的贴底元素↓ */.container { position: aboslute; bottom: 0; padding-bottom: constant(safe-area-inset-bottom); /* 兼容 iOS < 11.2 */ padding-bottom: env(safe-area-inset-bottom); /* 兼容 iOS >= 11.2 */ ...}几个新知识安全区域安全区域指的是一个可视窗口范围,处于安全区域的内容不受圆角(corners)、齐刘海(sensor housing)、小黑条(Home Indicator)影响,如下图蓝色区域: Apple的官方文档:adaptivity-and-layout viewport-fitiOS11 新增特性,苹果公司为了适配 iPhoneX 对现有 viewport meta 标签的一个扩展,用于设置网页在可视窗口的布局方式,可设置三个值: contain: 可视窗口完全包含网页内容(左图)cover:网页内容完全覆盖可视窗口(右图)auto:默认值,跟 contain 表现一致 <meta content="width=device-width,initial-scale=1,maximum-scale=1,user-scalable=0,viewport-fit=cover" name="viewport"/>参考文档:viewport-fit-descriptor enviOS11 新增特性,Webkit 的一个 CSS 函数,用于向 CSS 插入用户代理定义的变量设定安全区域与边界的距离,有四个预定义的变量: safe-area-inset-left:安全区域距离左边边界距离safe-area-inset-right:安全区域距离右边边界距离safe-area-inset-top:安全区域距离顶部边界距离safe-area-inset-bottom:安全区域距离底部边界距离env()必须配合 viewport-fit=cover 使用!我们最常用的就是 safe-area-inset-bottom, 这个代表着不被小黑条遮挡的安全距离。 padding-bottom: env(safe-area-inset-bottom); 参考文档:designing-websites-for-iphone-x,MDN/env() referencehttps://aotu.io/notes/2017/11...

November 2, 2019 · 1 min · jiezi

ins的更新带来的一系列问题

这个月初,ins又更新了版本,跟往常不同的是,很多人发现使用新版本的ins之后,账号很容易被锁住。这是什么情况呢?这也是我这几天一直思考的问题,对于出海产品一个比较重要的运营渠道,通过社交推广,必须有足够的关注量,所以会关注很多人,这就有了相关的周边产品。本来一切都是相安无事的,可以自从这个月开始,这种和谐被打破了,很多人发现周边产品不能用了,不知原由还以为周边产品都出问题了。经过我仔细分析,主要是和用户多次邮件交流,我发现旧版本的ins的用户没有问题,可以正常使用周边产品。所以,如果对ins周边产品有需求的同学,可以下载版本在112之前的ins使用,功能应该没多大差别,不知道怎么下载的,可以在知识星球里搜索“出海产品记”,免费的一个星球,里面我会发布链接(这里发不了)。值得注意的是,打开连接需要代理。       上面就是我这几天在研究的事情,每天忙于回复邮件和更新产品,好几天没更新了。今天就主要说说海外运营社交渠道需要注意的事情吧。做海外运营的同学,或者独立开发者想要免费推广自己产品,肯定都会接触ins,fb, twi, whats等等。其中fb最为“敏感”,动不动就禁用,真的很烦人。经过多次的申诉,终于稳定了。稳定的原因有两个,一个是上传了身份证,算是实名认证了;二是使用稳定ip的代理。twi的使用者也不少,很多名人都在用,尤其是米国的谁谁,天天在上面发牢骚。很多人就会利用名人效应评论加软文。ins上的用户主要都是在发照片和视频,不过ins开始在社交中做电商了,很多人也在上面卖东西。所以,要想在ins上涨粉丝,不仅要使用周边产品,还得发些好看的照片吸引人呀。除了社交渠道,还有油管和tiktok视频类的,也是很多产品推广的必争之地,要么砸钱,要么慢慢经营。       说到油管和tiktok,和国内差不多,也是造就了不少网红,造福了不少草根人士,如果对这方面感兴趣的,不妨当一个副业研究研究,毕竟现在副业成为一个“潮流”。最后,大家可以在知识星球搜索”出海产品记“,进入我的“出海产品记”,一起聊聊产品出海和副业的事儿!因为公众号只能留言,星球里可以提问,大家都可以回答,互动效果更好。

October 22, 2019 · 1 min · jiezi

Google-Play开发者账号注册过程中的安全问题

Google Play开发者账号注册,前提是我们要注册一个google账号,一般我们都使用gmail邮箱,所以注册google账号的同时,也注册了gmail邮箱账号。这里会有什么安全问题呢,之前的文章里提到一个干货,说的是在google账号注册的过程中,需要手机号验证。这里就是安全问题所在,为什么需要手机号验证,就是出于安全考虑。保险起见,我们在注册之后,最好添加辅助邮箱,这里说的辅助邮箱指的是开发者账号的辅助邮箱。这样,我们就可以通过手机号和辅助邮箱两种方式验证账号。    开发者账号绑定的手机号和辅助邮箱最好只跟一个开发者账号关联,同时还要代理要稳定,登录环境最好干净,防止突然被关联。被关联后基本是找不回来的,所以要牢记,谨慎关联,有问题立马换。如果发现开发者账号退出登录显示异常,排除关联封的情况。一般我们再次登录,需要我们验证,这时候手机号和辅助邮箱就发挥作用了。如果在pc端手机号验证不行,可以尝试辅助邮箱的方式,再不行,还有手机端操作,最好是使用chrome操作。如果登陆成功了,记得要在安全模块里确认异常行为。 上面这张图是点击google账号后可以看到的一个模块,这是确认完异常行为和没有安全隐患时的展示。如果有问题,蓝色的字是”确保账号安全“类的文字,点击进去就能看到有什么问题,一个个确认就可以了。如果没有办法登录成功,我们就要联系开发者支持小组了,至少我们要弄清楚具体的原因,虽然人很”霸道“,还是讲理的。 这张图就是联系开发者小组的页面,第一个是账户或者应用出问题了,申诉使用的,这个联系的是google play政策团队。第二个是及时在线聊天,可以立马和google play开发者支持小组联系上,但是要注意人家的服务时间是正常的工作时间。第三个是咨询使用过程中的问题,邮件的方式是比较慢的,是在非工作时间可以操作的方式,算是对即时聊天的补充。这几个联系方式我都使用过,很靠谱,虽然不会百分百解决问题。这个页面链接我在星球”出海产品记“里发布,有兴趣的可以关注下,这个星球是免费的,目前开始起步。 最后,再跟大家说个安全问题,使用信用卡注册开发者账号后,默认信用卡是和账号绑定的,这个时候如果我们不用开发者账号购买什么的话,建议解绑信用卡。具体见下图: 还是点击google账号进入的页面,我们看到左边的”付款和订阅“,点击后可以看到右边的内容,点击管理付款方式,就能看到绑定的信用卡了,点击删除就可以了。

October 22, 2019 · 1 min · jiezi

这可能是市面上最好用的iOS云真机

背景&&现状现在还有很多 App 在支持着 iOS9、iOS10,一旦在这些低版本的机器出现兼容性问题的时,想找一台手机来 debug,是一件非常难的事,而且 iOS 系统的分辨率也越来越多,无论是自动化还是日常的兼容性都需要有更全面的机型去做兼容性测试 在公司内部,很多时候,一些稀缺的测试设备我们是有,但无法高效地利用起来,对于其他办公区的同事用起来想快速用起来就更难了。 当然,有了统一的平台去管理设备后,我们也有一个统一的资源池,在机器空闲的时候,能提供给自动化服务使用。 我们调研了业界的解决方案后,发现大家对 iOS 端都支持得不是那么好,并且收费也相对较高,所以我们决定去尝试自研。 历经数个版本的迭代后,我们终于完成了一版比较好用的 iOS 云真机产品了,可以先感受一下效果。 亮点· 在屏幕画面方面,我们做到了高端机能有 15 帧左右的比较流畅的高画质屏幕传输,并且是秒开、超级省流量。 · 在操作方面,极低延迟 · 在用户体验方面,我们也做了更多比较方便的小功能,快捷安装 ipa、键盘输入、从电脑直接粘贴内容到手机、一键 web 调试。。。。。 · 在 Mac 主机方面,现阶段,我们是能做到一台 i5 的 mac mini 能支持同时接入 20 台手机 解决方案架构基本和 openSTF 类似,mini 作为 provider,provider 去管理手机和处理与云端服务器之间的消息,连接到云端服务器,云端服务器能够支持 N 个 provider 接入,并且自身能很容易地去做扩展,而云端服务器则负责分发消息,与做一些 websocket 服务的代理转发,前端则直接对接云端服务器。 云真机核心驱动部分这部分我们是完全自研,不基于 WDA,也不基于开源产品,现在市面上很多 iOS 云真机都会基于 WDA 或者其他的开源自动化测试框架去做的,可是这些框架的设计初衷并不是做云真机,会引入了很多我们不需要的功能模块。 我们希望云真机核心驱动部分,能尽量轻量,而且稳定。 整个核心驱动部分,最主要分为屏幕捕获、模拟控制、辅助功能接口。 屏幕捕获苦于苹果的限制,这一点是最难突破,我们也有尝试过很多方案。 例如: idevicescreenshot,帧率太低了,而且通过逆向发现 iOS 系统中的 ScreenShorter 不接受任何宽高、图片质量、图片格式的参数,这个方法在 iPhoneX 之类的高分辨率的机器,截图会更慢,只有 1-2 帧。 而比较流行的 iOS-minicap 方案,这个方案虽然能非常非常高效地实时获取到屏幕内容,但这个方案最大的缺点就是 1 个 mac 只能提供到 1 台 iPhone 的实时屏幕流,成本实在太高,我们也有尝试过破解 Mac 中 CoreMediaIO。framework 的 iOSScreenCapture 协议 (iOS-minicap 就是调用了系统提供的 iPhone 录屏接口,由 AVFoundation 与 CoreMediaIO 提供的),我们还有尝试过把整个 Mac 虚拟化去做隔离,让同一个物理机使用多个 iOS-minicap,但这些种种方案,最终都以失败告终。 我们的最终方案是,在 XCUITest 中是使用私有 API 去截图,这个私有 API 能最高能达到 15 帧左右,基本能满足我们的需求了,再把每一帧编码成视频流,从而节省流量。 可以感受一下 jpg 截图与视频流的流量大小对比: 以 XS Max 为例,每一帧都平均在 90K 左右,每一帧的数据传输截图: ...

October 16, 2019 · 1 min · jiezi

Flutter-命名路由注册-跳转传参接收数据返回数据

注册: MaterialApp( routes: <String, WidgetBuilder> { 'myrouter': (BuildContext context) => new MyRouter(), }, );带参数跳转 Navigator.of(context).pushNamed('myrouter',arguments: "这是传过去的参数");跳转界面接收参数 @override Widget build(BuildContext context) { //获取路由参数 var args=ModalRoute.of(context).settings.arguments }返回时候,传参数 Navigator.of(context).pop('这个是要返回给上一个页面的数据');返回到的上一页接收参数(修改上面的跳转方法) Navigator.of(context).pushNamed('myrouter',arguments: "这是传过去的参数").then((value){ print("value===="+value.toString()); });

October 15, 2019 · 1 min · jiezi

地图-SDK-系列教程在地图上展示指定区域

以下内容转载自iOS 工程师Genosage的文章《地图 SDK 系列教程-在地图上展示指定区域》 作者:Genosage链接:https://juejin.im/post/5d721a... 来源:掘金著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。地图SDK系列教程-在地图上展示指定区域在使用腾讯 iOS 地图 SDK 的过程中,经常会遇到需要地图展示指定区域的场景,相信大家也会遇到类似的情况,地图 SDK 提供了许多与之相关的接口,本篇文章将对这些接口进行整合,并提供示例代码来实现多个场景下展示指定区域的需求。 需要注意,本篇文章适用于地图未发生旋转与俯仰角的场景。 下载腾讯 iOS 地图 SDK 请前往:iOS 地图 SDK 指定区域包含单个坐标点在地图上显示某个固定的坐标点是地图 SDK 最为基础的功能之一。 举例来说,我们根据 SDK 的检索功能得到了天坛公园的坐标 (39.881190,116.410490),接下来,我们可以通过设置地图的中心点 centerCoordinate 来让地图显示这个坐标,同时我们还可以设置 zoomLevel 来指定缩放级别: // 设置中心点self.mapView.centerCoordinate = CLLocationCoordinate2DMake(39.881190,116.410490); // 设置缩放级别self.mapView.zoomLevel = 15;显示效果如下: 如果想展示墨卡托坐标点 QMapPoint 则需要先通过方法 QCoordinateForMapPoint(QMapPoint mapPoint) 将墨卡托坐标转换为经纬度再进行设置。 指定区域包含多个坐标点现在,假如我们想把天坛公园的搜索结果都显示在地图上,应该如何实现呢? 首先,我们通过检索功能搜索天坛公园,取搜索结果的前九个坐标点,接下来,应该使我们的地图视野包含这九个坐标点,地图 SDK 提供了方法 QBoundingCoordinateRegionWithCoordinates(CLLocationCoordinate2D *coordinates, NSUInteger count) 来计算多个经纬度坐标点的最小外接矩形 QCoordinateRegion 在得到了外接矩形之后,我们可以直接设置地图的 region 来使其显示我们想要的区域,完整代码如下: CLLocationCoordinate2D coordinates[9];// 天坛公园检索结果坐标coordinates[0] = CLLocationCoordinate2DMake(39.881190,116.410490);coordinates[1] = CLLocationCoordinate2DMake(39.883247,116.400063);coordinates[2] = CLLocationCoordinate2DMake(39.883710,116.412900);coordinates[3] = CLLocationCoordinate2DMake(39.883654,116.412863);coordinates[4] = CLLocationCoordinate2DMake(39.883320,116.400040);coordinates[5] = CLLocationCoordinate2DMake(39.876980,116.413190);coordinates[6] = CLLocationCoordinate2DMake(39.878160,116.413140);coordinates[7] = CLLocationCoordinate2DMake(39.878980,116.407080);coordinates[8] = CLLocationCoordinate2DMake(39.878560,116.413160); // 计算区域外接矩形QCoordinateRegion region = QBoundingCoordinateRegionWithCoordinates(coordinates, 9); // 设置区域self.mapView.region = region;显示效果如下: ...

October 14, 2019 · 2 min · jiezi

H5唤醒App快速直达App核心页面

在这个流量为王的互联网背景下,移动端的H5页面显然在导流上承担着重要作用,在H5页面上,我们对引流的需求有两种: 一是引导已下载用户从H5页面唤醒App并直达指定场景二是引导未下载用户从H5页面下载App,首次打开App时直达指定场景从运营角度来看,引导已下载用户打开App,能提高用户粘性和活跃度,而用户在App内的产品体验自然也比H5页面要好;引导未下载用户下载App并进入指定页面,显然能给用户更好的产品初体验。 这里其实就解释了我们做H5唤醒App并直达指定页面的必要性。 涉及哪些要素?唤醒App这件事,在不同平台要采用不同的方法,主要是这三个: URL SchemeUniversal LinkAndroid App Links1、URL Scheme URL Scheme是iOS、Android都兼容的机制,只需要原生App开发时注册Scheme即可,用户点击此类链接时,会自动唤醒App,并借助URL Router机制跳转到指定页面。 <scheme name> : <hierarchical part> [ ? <query> ] [ # <fragment> ]<scheme name>:是scheme的名称,代表着协议名称。<hierarchical part>:它包含 authority 和 path。<query>:可选项目,隔开或&隔开的键值对<key>=<value><fragmentg> :可选项目包,其它额外的标识信息 尽管URL Scheme兼容性高,但却存在许多限制,比如: 国内各个厂商浏览器差异很大,当要被唤醒的目标App未安装时,这个链接很容易出错。当注册有多个Scheme相同的时候,目前是没有办法区分的。不支持从其他App中的UIWebView中跳转到目标App。被部分主流平台禁止,微信、微博、QQ浏览器、手机百度中都已经被禁止使用。正是由于这些限制的存在,苹果和安卓都不约而同发布了自己的第二套方案:iOS的Universal Link、Android的App Links。 2、Universal Link Universal Link是iOS9后苹果推出的通用链接技术,能够方便的通过一个https链接来打开App指定页面,不需要额外的判断,如果没有安装App,可以跳转到自定义地址。 相对Scheme的优势在于,Universal Link是一个Web Link,因此少了很多麻烦: 当用户已安装该App时,不需要加载任何页面,能够立即唤醒App,用户未安装App,则跳去对应的web link(自定义页面)。Universal Links支持从其他App中的UIWebView中跳转到目标app。提供Universal Link给别的App进行App间的交流,然而对方并不能够用这个方法去检测你的App是否被安装,具有比较好的隐私性。绝大多数平台都支持Universal Link,微信7.0.5版本也解除了对Universal Link的限制,同时也能被搜索引擎索引。 3、App Links Android M以上版本可以通过App Links,让用户在点击一个链接时跳转到App的指定页面,前提是这个App已经安装并经过验证。App Links的最大的作用,就是可以避免从页面唤醒App时出现的选择浏览器选项框,前提是必须注册相应的Scheme,就可以实现直接打开关联的App。 实际上App Links和Universal Links差异不大,但相对来说有不同的限制: App links在国内的支持还不够,部分安卓浏览器并不支持跳转至App,而是直接在浏览器上打开对应页面。系统询问是否打开对应App时,假如用户选择“取消”并且选中了“记住此操作”,那么用户以后就无法再跳转App。几个方案的缺陷这几种方式无论哪种都无法解决这几个问题: 当用户未安装目标App时,无法保留用户停留的上下文,也就是说,用户下载完App后,无法在首次打开App时还原指定页面。Web目前无法监听App是否已安装,因此这几个方案都需要一些其他方法兼容唤醒App,或者跳转下载页面。那么怎样实现用户安装App后进入指定页面呢? 众所周知,苹果出于用户隐私的保护,设置了名为沙盒的机制:应用只能访问它声明可以访问的资源,但沙盒也阻碍了应用间合理的信息共享。 但也不是完全没办法,比如使用模糊匹配,尽可能收集设备的特征,将Web和App上的信息点配合算法做一个匹配是可以做到的,但准确率和成功率就取决于算法本身。如果App本身业务需求不高,那么低精度的方案也可以满足,但如果业务上需要一个能做到一对一精准匹配的方案,那么精准度不够高显然会影响业务的开展。 第三方服务如果嫌精准度不够高或者实现难度太大的话,可以交给专业的第三方去做,毕竟这几项技术是基于系统平台的,Android 及 iOS 每个系统版本的迭代后,配置方式都会有新的变化,且安卓机型众多,浏览器众多等也会导致出现兼容问题,开发者自行研发的话,资源配置以及系统更新后的维护成本是比较高的,还要考虑各种各样的跳转场景问题。 ...

October 9, 2019 · 1 min · jiezi

适用场景广表单收集类小程序开发案例复盘

今天我将以「北江纺织牛仔新时尚」小程序为例,复盘一个服装行业订单收集小程序从设计到实现的全过程。这是上篇,主要讲产品逻辑搭建和数据库设计的过程。 这个小程序的主要功能是向用户展示服装和面料商品,并提供搜索、收藏商品,加入购物车和下单等功能,是一个比较典型的订单 / 意向收集小程序。 这个实现方案也可以应用于活动意向、用户信息收集等营销类的小程序。 技术方案从技术层面(或系统架构)上说,(功能丰富的)小程序一般由前端和后端两部分组成。前端可以认为是我们所能看到的小程序,负责页面数据展示与用户操作交互;后端主要负责提供和操作数据。 在技术选型上,前端方面固然采用小程序开发技术,后台方面则选用知晓云(cloud.minapp.com)。知晓云免去了小程序开发中服务器搭建、数据接口实现等繁琐流程,可以更专注于业务逻辑的实现。 需求分析在确定好技术方案后,接下来就需要有一个明确的业务需求分析。 以纺织行业为例,该小程序的主要目的是用于商品展示和用户下单,为用户提供一个便捷快速的购物平台。因此可以主要分为三大模块:商品模块,购物车模块,个人中心模块。 每个模块根据实际业务需求可以再细分为多个功能模块,从而可以根据功能需求初步确定页面设计。 商品模块:商品模块是小程序很重要的一部分,提供商品展示,用户购买、收藏、加入购物车、下单等主要功能。 这个纺织小程序的商品主要有面料及其制作的成衣两大类,为了更直观地展示和区分面料和成衣两种不同商品,在设计上也会体现出【主页】和【面料】两部分。 购物车模块:购物车模块的功能和多数的购物商城程序一样,用于暂时存放有购买意向的商品,在页面上显示的部分为【购物袋】。个人中心模块:个人中心包含注册,登录,个人信息,收藏,订单管理等功能,一般在页面上的设计为【我的】。 页面设计经过分析完业务功能需求,初步设计首页的入口有四个,以常见的 tabbar 形式展示 主页:展示不同分类的成衣商品面料:展示面料商品列表 购物袋:展示用户有购买意向的商品 我的:管理个人信息 在确定首页入口后,会再根据每个入口设计各自功能应该展示的页面。至于每个部分的页面详细设计和实现,会在后续文章讲述。 数据库设计当然,良好的数据库设计无疑是以上功能实现的基础。制作数据库首先要确定实体的属性和实体间的关系。数据库要存储所有的商品信息和用户信息,这些信息都是商品的属性和用户的属性。 为了能更加全面考虑到要实现的功能,我们还可以再通过工作流程图作数据流程分析,进一步明确小程序所包含的实体信息有哪些。 根据实现目标和业务需求制定工作流程图如下。 「北江纺织」小程序的数据管理主要是从商品展示到用户下单支付流程的各种信息管理,其中包含商品数据、用户数据、订单数据、购物车数据、收藏数据等。 商品包含的信息比较复杂,可以分为商品信息、商品类别信息以及供应商信息等,用记录 ID 在数据表间建立联系。 其中,信息管理主要体现在添加、删除、修改、查询等功能。 数据表汇总如下: 关于上述数据表的字段设计,知晓云(cloud.minapp.com)已经提供了常用的电商数据表模版,在这个基础上,我们可以再根据小程序的功能需求对数据表增删字段。 Tip:此外,各个数据表内可以添加 status、priopity 字段,用于控制数据的是否显示以及显示顺序。 所有的准备工作做好了,接下来就可以开始小程序开发了。关于具体的技术实现,将在下篇文章详细介绍。

October 9, 2019 · 1 min · jiezi

iOS知识梳理-多线程1杂谈

目的我们使用多线程通常有两点目的: 目的1:充分利用多核优势。现在无论是终端设备还是服务器,基本上都是跑在多核CPU上的,如果想要充分利用多核性能,上层应用的线程数至少不能少于CPU核心数。 目的2:并发执行一些逻辑。最常见的就是,耗时逻辑应避开UI线程。 线程数需要注意的是,线程数并非越多越好。当活跃线程数超出CPU核数时,会带来上下文切换开销。且子线程还会带来一定的内存开销(iOS平台下,主线程占用1M内存,每个子线程会占用512K内存)。 理想情况下,线程数和CPU核心数应当大体在同一数量级。(部分平台有人推荐CPU核心数x2作为线程池大小)。iOS中,GCD的并发队列,如果不存在阻塞的话,所使用的线程数即CPU核心数(参考[Number of threads created by GCD?)。 多线程和异步印象中,早年很多程序,是开启一个子线程去发网络请求,这个子线程会阻塞等待网络请求返回并处理结果。 这种方式称为同步请求,期间这个线程是阻塞的。 现在这样做的少了,通常发出网络请求后当前线程会继续往下执行,请求回包后会通过某种事件处理机制触发回调函数进行处理。 这种方式称为异步,期间这个线程是非阻塞的。 除了网络请求,IO读写等场景也是类似的。这些不占用CPU的场景应当优先使用异步的手段,而不是开子线程处理。(iOS平台下好像基本上没有这样做的,年轻时写Java的时候倒是干过不少...) 代价多线程的好处,其实就是最开始的两点目的。但实际开发中,由于多个线程访问同一份内存数据,很容易产生冲突,往往就得加锁。忘了加锁就容易crash,锁加的不好就会有性能问题(→_→ 参考python的GIL)。线程、逻辑、数据之间的关系,最好是能在程序/模块设计之初就明确下来,但实际开发中往往没这么理想化。总的来说,多线程给程序员带来了大量的心智负担,也比较容易出问题。 非传统方案面对传统多线程编程的各种问题,业界提出了一些解决方案。主要思想是: 尽量不用多线程,部分场景可以用多进程替代。例如服务端的PHP和nodejs。一定要用多线程的话,通过通信机制共享数据而不是直接共享内存。 比如GO:Do not communicate by sharing memory; instead, share memory by communicating.比如js的web worker:Web Worker 使用教程dart的isolate机制:dart基础之异步编程其实抛弃了共享内存的多线程,已经跟传统意义的多线程没有太多关系了。比如dart的isolate机制,虽然也可以说是不提供共享内存只提供通信机制的多线程,但是dart自称单线程语言也并无不妥。

October 8, 2019 · 1 min · jiezi

使用XCODE快速发布IOS-ad-hoc测试版本

首先,你必须得有开发者账号,如何申请开发者账号,这里就不再累赘,请自行百度。 一、什么时候使用ad hoc证书 app发布之前,一般都要在真机上面进行测试,所以需要打包测试版(这不废话吗) 二、证书的创建与使用流程 在创建证书之前我们需要在我们的电脑上生成一个Certificate Signing Request即证书注册请求文件,找到mac下的“钥匙串访问”点击进入操作界面 选择“存储到磁盘”,点击存储之后我们会获得这样一个文件,请记住文件路径(笑脸)接下来我们登陆苹果开发者账号,Certificates-Development点+号新增证书 Choose file 选择刚刚下载的证书 此时你应该拥有了一个测试证书了,点击download并安装,你在钥匙串里面就能看到你的测试证书了。 三.创建app ids 点击右上角的+号 Bundle ID栏中填写的必须和你的xcode -Bundle Identifier中的内容保持一致完善表单,提交submit就可以完成这一步的操作 四、创建测试设备 点击左侧菜单的Devices下的all 来添加我们所支持的运行设备,填写设备的nama,UDID可以通过itunes查看,完善表单添加测试设备 五、创建Provisioning Profiles文件 选择我们之前创建的app id Continue 选择我们创建的调试证书 双击安装 六、xcode 如图操作点击preferences添加你的开发者账号 七、接下来我们去xcode,Team这行里选择你刚才账号中的team Name中显示的名字 设置Target-General-Signing,x-code8 有一个Automatically manage signing,此时要去掉勾选,然后配置Provisioning Profile 为刚刚创建好的 adhoc 配置文件 八.此时所有环境都配置完成 菜单选择 Product -> Archive,然后等待编译 九.还不打包? ...

October 8, 2019 · 1 min · jiezi

Kingfisher加载多个Gif图内存告警崩溃

Kingfisher加载多个Gif图,内存告警崩溃

October 8, 2019 · 1 min · jiezi

一篇文章带你从-0-到-1-开发小程序插件

知晓程序注: 有了微信小程序插件功能,小程序开发者就可以通过这个功能,强化自身小程序能力;小程序服务提供商也可以用它,为开发者、用户提供强大的小程序功能支持,进一步拓展小程序能力。 插件固然好,但如何从零开发一个插件呢?今天,知晓程序就来手把手,教你如何从零开发一款微信小程序插件。 需要注意的是,本推送为全文节选。关注「知晓程序」微信公众号,微信后台回复「微观」,了解更多行业资讯。 新建插件新建插件的操作非常简单。只需要在微信开发者工具中新建小程序项目,并选择「建立插件快速启动模板」即可,开发者工具就会自动创建插件项目。 需要注意的是,新建项目时,需要确保选择的项目目录是空目录,否则不会显示「建立插件快速启动模板」选项。 插件目录结构生成的项目结构主要分为两大块,一个是 plugin,一个是 miniprogram。plugin中放置我们插件的逻辑代码,主要分为 api 和 components 两个部分; miniprogram 中放置的是插件的使用示例或者测试示例。 插件 API 接口开发以写一个返回「hello world!」的接口为例,我们可以在 plugin/api/data.js 中写下如下代码: function sayHelloWorld() { return 'hello world!' } module.exports = { sayHelloWorld }在 plugin/index.js 中将我们需要暴露出需要给插件使用者使用的接口 var data = require('./api/data.js') module.exports = { sayHelloWorld: data.sayHelloWorld}然后在 plugin/plugin.json 的配置文件中,配置插件的入口,默认如下: { "main": "index.js"}然后在 miniprogram 中使用该接口。如在 miniprogram/pages/index/index.js 中使用: var plugin = requirePlugin("myPlugin")Page({ onLoad: function() { console.log(plugin.sayHelloWorld()) }})其中 myPlugin 为我们的插件名,微信默认配置。 插件组件开发同样,以写一个显示 「hello world!」的组件为例,在 plugin/components 下新建一个 helloWorld 文件夹,点击该文件夹,右键生成组件,与普通组件一样,生成以下四个文件。 ...

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

自定义报头协议

自定义报头协议在学习过计算机网络的课程,我们知道刚开始计算机都是单独脱机工作的,没有联网的情况下计算机的信息共享能力、运算能力都非常有限,后来诞生了计算机网络.有了就是那几网络,计算机 A 的信息和数据可以通过网络传递到计算机 B,同样计算机 A 可以获取到来自计算机 B 的数据. 但是不同计算机之间交换数据的时候就要通过网络来传输了.传输的过程中需要不同的计算机都遵循一定的规则来组装数据、传递信息,那么这样的规则就叫做协议.1. 协议计算机网络中有非常多协议,这些协议位于 OSI 的不同层中,比如 TCP/IP、UDP、SMTP、FTP 等. 协议之所以称为协议,是因为它具有约束效应,信息在端到端的传输过程中,同等层次之间通过使用同样的协议规则,这样发送方在该层次按照协议约定处理数据,接收方在该层次按照协议约定解析数据.成对存在. 2. 自定义协议在日常开发的时候处于某些原因可能需要自定义报文协议.这个协议是建立在 TCP 连接的基础上,比如,移动端在做 APM 的时候将功能拆分为2个模块,一个是 APM 监控模块、一个为了方便可拓展单独做了一个数据上报组件,具有动态下发上报策略的配置. 所以上报组件这里涉及到和服务端高效通信,所以客户端和服务端约定了一套自定义的报文协议,如下所示. PACKET 整体结构| HEAD | META | BATCH_PAYLOAD |PACKET::HEAD 结构| META_SIZE (2bytes unsigned int) | BATCH_COUNT (1 bytes unsigned int) | PAYLOAD_SIZE (4bytes unsigned int) | PAYLOAD_SIZE (4bytes unsigned int) | ... | PACKET::META 结构换行符分割的 JSON 字符串 crypto(deflate(JSONnJSON...)) PACKET::BATCH_PAYLOAD 结构JSON 体里每个字段的值都是 BASE64 编码的 ...

October 7, 2019 · 1 min · jiezi

H5跳转ReactNative打开指定页面

1、需求工作可能有这样的需求,就是手机浏览器中加载一个h5页面,点击可以打开某一个APP,比如微信等。这时候通常都是采用URL Scheme的方式进行配置跳转。那么什么是URL Scheme呢?简单说:它是一个页面跳转协议。 2、 URL Scheme协议URL Scheme是一种页面内跳转协议,是一种非常好的实现机制,通过定义自己的scheme协议,可以非常方便跳转app中的各个页面;通过scheme协议,服务器可以定制化告诉App跳转那个页面,可以通过通知栏消息定制化跳转页面,可以通过H5页面跳转页面等。 苹果手机中的APP都有一个沙盒,APP就是一个信息孤岛,相互是不可以进行通信的。但是iOS的APP可以注册自己的URL Scheme,URL Scheme是为方便app之间互相调用而设计的。 URL Scheme必须能唯一标识一个APP,如果你设置的URL Scheme与别的APP的URL Scheme冲突时,你的APP不一定会被启动起来。因为当你的APP在安装的时候,系统里面已经注册了你的URL Scheme。完整的URL Scheme格式: stars://host:8088/pageDetail?pageId=1021.stars:表示Scheme协议名称,可以自定义2.host: 表示协议的跳转地址名称,通常和APP中配置的名称是一直的3.pageDetail:表示跳转的具体页面名称4.pageId:表示传递的参数5.8088:通常表示跳转地址的端口名称 3、具体使用配置Android配置<activity ... <!--要想在别的App上能成功调起App,必须添加intent过滤器--> <intent-filter> <!--协议部分,随便设置--> <data android:host="host" android:path="/pageDetail" android:port="8088"//可以不要 android:scheme="stars"/> <!--下面这几行也必须得设置--> <!--表示该页面可以被隐式调用,必须加上该项--> <category android:name="android.intent.category.DEFAULT"/> <action android:name="android.intent.action.VIEW"/> <!--如果希望该应用可以通过浏览器的连接启动,则添加该项--> <category android:name="android.intent.category.BROWSABLE"/> </intent-filter> </activity>IOS配置只需要配置info.plist 文件,只需要配置URL Schemes就可以了,identifier是可选配置 4、注意事项正常情况下,以上配置后,就可以正常进行跳转了,但是在安卓上,还需要进行一步配置,如果你的应用被其注册过的外部 url 调起,如果要在现有的 MainActivity 中监听传入的 intent,那么需要在AndroidManifest.xml中将 MainActivity 的launchMode设置为singleTask <activity android:name=".MainActivity" android:launchMode="singleTask">

October 5, 2019 · 1 min · jiezi

苹果拟解除限制-让用户通过-Siri-调用任何应用程序

据外媒报道,未来的 iOS 更新程序将允许用户通过语音助手Siri调用他们喜欢的任何应用程序,这意味着 WhatsApp 等应用程序可以有效地取代默认的苹果应用程序。 据报道,苹果计划允许第三方应用程序取代自己的即时通讯或其他应用程序。这意味着,如果用户定期使用 WhatsApp 给特定的联系人发送消息,那么 Siri 也会自动通过该应用程序发送消息。 此前,有人批评苹果自己的应用程序统治了 App Store 应用商店,并成为了 iPhone、iPad 和 Siri 上默认的应用程序。 苹果公司在一份声明中发布了这一消息。 “我们为用户提供的体验只能通过硬件、软件和服务的集成来实现。从第一代 iPhone 开始,我们就包含了一些应用程序,可以为用户提供开机即用的体验,如打电话、播放音乐和上网等等。” “随着每一代 iPhone 推出,我们都为用户提供了先进的内置功能,提供了一些旨在实现卓越性能、长电池寿命、无缝集成以及业界领先的安全和隐私保护的默认应用程序。我们还创建了 App Store,这是用户获取应用程序的最安全的地方,可以让用户从数百万个应用程序中进行选择,以找到能够进一步增强其 iPhone 功能的应用程序。” “在苹果有自己应用程序的少数几个类别中,我们有许多成功的竞争对手。令我们感到自豪的是,在一个繁荣的数十亿美元的开发者市场中,它们的成功为美国创造了近 200 万个就业机会。” “我们的指导方针始终是为我们的用户创造最好的产品,这就是为什么 iPhone 在行业中拥有最高的用户满意度。” iOS的更新程序预计将在 2019 年晚些时候发布。不过,让用户通过 Siri 调用任何应用程序,首先需要应用程序开发人员添加 Siri 语音助手功能。 新闻来源:腾讯科技

October 3, 2019 · 1 min · jiezi

iOS编译自动升级版本号脚本

版权申明:本文原创首发于以下网站,您可以自由转载,但必须加入完整的版权声明博客园:https://www.cnblogs.com/Mogoo... csdn博客:https://blog.csdn.net/nmjkl001/ 知乎:https://www.zhihu.com/people/... 简书:https://www.jianshu.com/u/954... segmentfault:https://segmentfault.com/u/mo...使用方法Shell代码设置到xcode运行脚本里在每次成功构建后版本号会改变Shell代码#!/usr/bin/env bashecho $CONFIGURATIONif [ $CONFIGURATION == Release ]; thenecho " Bumping build number... "#!/bin/bashbuildNumber=$(/usr/libexec/PlistBuddy -c "Print CFBundleVersion" "$INFOPLIST_FILE")if [ "${buildNumber}" == "" ]; thenecho 2fivar1=${buildNumber##*.}var1=$(($var1 + 1))var2=${buildNumber%.*}buildNumber=${var2}.${var1} /usr/libexec/PlistBuddy -c "Set CFBundleVersion $buildNumber" "$INFOPLIST_FILE"echo " Bumped build number to $buildNumber " elseecho $CONFIGURATION " build - Not bumping build number. "fi我的联系方式:QQ:2161044579 邮箱:mogoostudio@outlook.com Github:https://github.com/MogooStudio

October 1, 2019 · 1 min · jiezi

Flutter九之Flutter的布局Widget

Flutter布局前言一:接下来一段时间我会陆续更新一些列Flutter文字教程更新进度: 每周至少两篇; 更新地点: 首发于公众号,第二天更新于掘金、思否、开发者头条等地方; 更多交流: 可以添加我的微信 372623326,关注我的微博:coderwhy 希望大家可以 帮忙转发,点击在看,给我更多的创作动力。 前言二:为了实现界面内组件的各种排布方式,我们需要进行布局,和其他端不同的是,Flutter中因为万物皆Widget,所以布局也是使用Widget来完成的。 Flutter中的布局组件非常多,有31个用于布局的组件,Flutter布局组件; 在学习的过程中,我们没必要一个个全部掌握,掌握最常用的,一些特殊的组件用到时去查文档即可; Flutter将布局组件分成了 单子布局组件(Single-child layout widgets) 和 多子布局组件(Multi-child layout widgets) 一. 单子布局组件单子布局组件的含义是其只有一个子组件,可以通过设置一些属性设置该子组件所在的位置信息等。比较常用的单子布局组件有:Align、Center、Padding、Container。 1.1. Align组件1.1.1. Align介绍看到Align这个词,我们就知道它有我们的对齐方式有关。在其他端的开发中(iOS、Android、前端)Align通常只是一个属性而已,但是Flutter中Align也是一个组件。 我们可以通过源码来看一下Align有哪些属性: const Align({ Key key, this.alignment: Alignment.center, // 对齐方式,默认居中对齐 this.widthFactor, // 宽度因子,不设置的情况,会尽可能大 this.heightFactor, // 高度因子,不设置的情况,会尽可能大 Widget child // 要布局的子Widget})这里我们特别解释一下widthFactor和heightFactor作用: 因为子组件在父组件中的对齐方式必须有一个前提,就是父组件得知道自己的范围(宽度和高度);如果widthFactor和heightFactor不设置,那么默认Align会尽可能的大(尽可能占据自己所在的父组件);我们也可以对他们进行设置,比如widthFactor设置为3,那么相对于Align的宽度是子组件跨度的3倍;1.1.2. Align演练我们简单演练一下Align class MyHomeBody extends StatelessWidget { @override Widget build(BuildContext context) { return Align( child: Icon(Icons.pets, size: 36, color: Colors.red), alignment: Alignment.bottomRight, widthFactor: 3, heightFactor: 3, ); }} ...

October 1, 2019 · 4 min · jiezi

Mac-终端效率神技

一、 增强各种预览的插件预览查看图片分辨率&大小代码语法高亮快速预览zip压缩包内容快速预览markdown格式内容brew cask install qlcolorcode betterzipql qlimagesize qlmarkdown二、 iTerm2具体的配置网上一大堆。贴一个本人亲身操刀操作过的教程 程序员经常与终端操作打交道,所以很多命令便是做成了命令行模式,在自带的 Terminal 命令都保存在 .bash_profile 文件中,使用了 iterm2,命令都保存在 .zshrc 中。 所以我们将很多命令保存且编辑 cd ~vim .zshrc # 输入自己常用的命令# 文件相关alias co='code ./'alias fo='open ./'alias o='open *.xcodeproj'alias po='open *.xcworkspace'# cocoapodsalias pru='pod repo update'alias pi='pod install'alias pu='pod update'alias piu='pod install --repo-update'alias repoanalysis='specbackwarddependency /Users/liubinpeng/.cocoapods/repos/51xianqu-xq_specs'alias plint='pod spec lint --sources="git@gitlab.51xianqu.com:xq_ios/xq_specs.git"'# Gitalias gck='git checkout'alias gm='git merge'alias gb='git branch'alias gbr='git branch -a'alias gs='git status'alias gc='git clone'alias gl='git log'alias ga='git add .'alias gpull='git pull'alias gpush='git push'alias gcm='git commit -m'alias glocalbranchPush='git push --set-upstream origin 'alias glg="git log --graph --pretty=format:'%Cred%h%Crest -%C(yellow)%d%Creset %s %Cgreen(%cr) %C(bold blue)<%an>%Creset' --abbrev-commit --date=relative"alias ns='npm start'alias ni='npm install'alias nb='npm run build'alias nig='npm install -g '# 浏览器打开alias OpenWithChrome='open -a "/Applications/Safari.app" 'alias OpenWithSafari='open -a "/Applications/Google Chrome.app" '# 用 Typora 打开 markdown 文件预览写作效果。alias OpenMDPreview='open -a "/Applications/Typora.app" 'OCLINT_HOME=/Users/liubinpeng/Documents/oclint/build/oclint-releaseexport PATH=/Users/liubinpeng/Workspace/Native/iOS/sdgcli/bin:$PATHexport PATH=/Users/liubinpeng/Workspace/Native/iOS/sdg_frontend_gitflowcli/bin:$PATHexport PATH=$OCLINT_HOME/bin:$PATH#Flutter 镜像export PUB_HOSTED_URL=https://pub.flutter-io.cn export FLUTTER_STORAGE_BASE_URL=https://storage.flutter-io.cnexport PATH=/Users/liubinpeng/flutter/bin:$PATH# Android Studio SDK 路径export ANDROID_HOME=~/Library/Android/sdkexport PATH=${PATH}:${ANDROID_HOME}/emulatorexport PATH=${PATH}:${ANDROID_HOME}/toolsexport PATH=${PATH}:${ANDROID_HOME}/platform-toolscd ~source .zshrc验证:在你的 git 项目所在的目录的终端下输入 glg ...

September 30, 2019 · 2 min · jiezi

Dio-30发布支持Flutter-Web-和-Http20

Flutter 一周前发布了1.9 版本,其中最大特性是将Flutter Web合入了主分支。而今天,Dio 也正式发布了3.0.0,该版本最大的特性是支持Flutter Web 和 Http/2.0。 升级提示:由于Dio 3.0需要支持Flutter Web,需要对代码进行较大的重构,所以并不能完全向下兼容,2.1版本用户可参考 3.0升级指南 。支持Flutter Web开发者只需要将Flutter升级到1.9或更新的dev版本,然后将Dio升级到最新的3.x即可支持Flutter Web即可。 Http/2.0支持Http/2.0 有链接复用、头部压缩、二进制传输、服务端推送等重多特性。Dio 3.0版本后,官方提供了一个 dio_http2_adapter 插件(HttpClientAdapter)来支持Http/2.0。下面是官方示例: import 'package:dio/dio.dart';import 'package:dio_http2_adapter/dio_http2_adapter.dart';main() async { var dio = Dio() ..options.baseUrl = "https://google.com" ..interceptors.add(LogInterceptor()) ..httpClientAdapter = Http2Adapter( ConnectionManager(idleTimeout: 10000), ); Response<String> response; response = await dio.get("/?xx=6"); print(response.data);}可以看到只需要配置一下Http2Adapter即可。值得注意的是Http2Adapter需要一个ConnectionManager参数。ConnectionManager 主要职责是管理链接,是Http/2.0中链接复用策略的实现载体。官方提供了一个默认的ConnectionManager ,它的策略是同一个域名下的请求共享一个Socket链接,当请求完成时,连接默认继续保持15秒,开发者可以通过idleTimeout来自定义保持时间。开发者提供自己的ConnectionManager来自定义链接复用策略。 另外,Http2Adapter内部已经默认处理了重定向,我们可以通过以下代码验证: response.redirects.forEach((e){ print("redirect: ${e.statusCode} ${e.location}");});输出: redirect: 301 https://www.google.com/?xx=6redirect: 302 https://www.google.com.hk/url?sa=p&hl=zh-CN&pref=hkredirect&pval=yes&q=https://www.google.com.hk/%3Fxx%3D6&ust=1568810110125304&usg=AOvVaw0YbFhKFoslI0LPOPFcekGyredirect: 302 https://www.google.com.hk/?xx=6可以看到我们在大陆请求google时进行了三次重定向! 其它更新FormData 支持嵌套。删除了UploadFileInfo类,引入了MultipartFile 类;MultipartFile类不仅支持通过文件来构造上传头块,也支持通过Stream、Byte数组、字符串来构造。将CookieManager抽离成了单独的包;这是因为在Flutter web中不需要手动管理Cookie(浏览器会自动管理),因此将其抽为单独的插件按需引入会更合理。请求取消后,取消Error可以入队拦截器队列(2.1中取消的异常是直接抛给用户处理)。代码优化:API标准化、语义化;对核心代码进行了全部重构。详情请参考:https://github.com/flutterchi... 。

September 19, 2019 · 1 min · jiezi

usdt跑分系统app开发制作介绍

python打包py文件生成exe,以及运行exe产生的问题 1 安装pyinstaller pip install pyinstaller 我这里已经安装过+usdt跑分app开发找吴燕了(T:I56、2227、7887V)就不再多说啦! 2 用pyinstaller进行打包 (1)选项: -F:表示生成单一的exe文件 -w:表示执行exe文件时去除背后的dos窗口 ps:建议最初试验时保留dos,可以查看是否有错误发生 -p:有些需要手动添加包的位置(dir1,dir2)多个地址时以分号分开 -i:加入图标 (2)打包一个py文件 添加图标(格式为ico的图片): pyinstaller -F XXX.py -i XX.ico (3)打包多个py文件: pyinstaller -F XXX.py -p XX.py -i XX.ico(以此类推) 运行以后,会生成dist和build两个文件夹,exe在dist文件夹下。如果程序需要用到附加的文件则添加到dist文件夹中即可。 3 运行打包后的 多进程exe程序出现的问题 运行之后 我发现计算机不怎么响应了,而且很卡,打开任务管理器: 一会电脑就马上挂掉啦!这并不是我们想要的结果 接着在网上一番百度,终于找到解决问题的办法: pyinstaller版本小于3.3的windows附加代码(不过我的版本是3.4的不知道为什么还会出现这个问题,反正添加上后程序运行就正常啦) 新建一个wenke.py文件 添加如下代码: import osimport sysimport multiprocessing# Module multiprocessing is organized differently in Python 3.4+try: # Python 3.4+ if sys.platform.startswith('win'): import multiprocessing.popen_spawn_win32 as forking else: import multiprocessing.popen_fork as forkingexcept ImportError: import multiprocessing.forking as forkingif sys.platform.startswith('win'): # First define a modified version of Popen. class _Popen(forking.Popen): def __init__(self, *args, **kw): if hasattr(sys, 'frozen'): # We have to set original _MEIPASS2 value from sys._MEIPASS # to get --onefile mode working. os.putenv('_MEIPASS2', sys._MEIPASS) try: super(_Popen, self).__init__(*args, **kw) finally: if hasattr(sys, 'frozen'): # On some platforms (e.g. AIX) 'os.unsetenv()' is not # available. In those cases we cannot delete the variable # but only set it to the empty string. The bootloader # can handle this case. if hasattr(os, 'unsetenv'): os.unsetenv('_MEIPASS2') else: os.putenv('_MEIPASS2', '') # Second override 'Popen' class with our modified version. forking.Popen = _Popen然后在mul_process.py中加入: ...

September 19, 2019 · 1 min · jiezi

RxSwift-MVVM-初体验

一、原起作为一名iOS开发者,必须跟上时代的潮流,随着swift ABI越来越稳定,使用swift开发iOS APP 的人越来越多。从网上看了很多文章,也从github上下载了很多demo进行代码学习。最近使用RxSwift+MVVM+Moya进行了swift的体验之旅。加入到swift开发的大潮中去。 二、目录结构这个demo的项目结构包括:View、Model、ViewModel、Controller、Tool、Extension。 ViewModel是MVVM架构模式与MVC架构模式最大的区别点。MVVM架构模式把业务逻辑从controller集中到了ViewModel中,方便进行单元测试和自动化测试。 ViewModel的业务模型如下: viewmodel相当于是一个黑盒子,封装了业务逻辑,进行输入和输出的转换。 其中View、Model与MVC架构模式下负责的任务相同。controller由于业务逻辑移到了Viewmodel中,它本身担起了中间调用者角色,负责把View和Viewmodel绑定在一起。 demo的整体目录结构如下: 三、使用到的第三方库开发一个App最基本的三大要素:网络请求、数据解析、UI布局,其它的都是这三大要素相关联的,或者更细的功能划分。 网络请求库使用的Moya,数据解析使用的是ObjectMapper,UI布局使用的是自动布局框架Snapkit,图片加载和缓存使用的是Kingfisher,刷新组件使用的MJRefresh,网络加载提示使用的是SVProgressHUD。使用到的三方库的cocoapod目录如下: 四、具体实现4.1 viewmodel的协议viewmodel的实现需要继承NJWViewModelType这个协议,需要实现输入->输出这个方法。这个算是viewmodel的一个基本范式吧。 protocol NJWViewModelType { associatedtype Input associatedtype Output func transform(input: Input) -> Output}4.2 viewmodel的具体实现这里包括了输入、输出的具体实现,与及func transform(input: NJWViewModel.NJWInput) -> NJWViewModel.NJWOutput 这个输入转输出方法具体的实现逻辑。具体代码如下: class NJWViewModel: NSObject { let models = Variable<[GirlModel]>([]) var index: Int = 0}extension NJWViewModel: NJWViewModelType{ typealias Input = NJWInput typealias Output = NJWOutput struct NJWInput { var category = BehaviorRelay<ApiManager.GirlCategory>(value: .GirlCategoryAll) init(category: BehaviorRelay<ApiManager.GirlCategory>) { self.category = category } } struct NJWOutput { let sections: Driver<[NJWSection]> let requestCommand = PublishSubject<Bool>() let refreshStatus = Variable<NJWRefreshStatus>(.none) init(sections: Driver<[NJWSection]>) { self.sections = sections } } func transform(input: NJWViewModel.NJWInput) -> NJWViewModel.NJWOutput { let sections = models.asObservable().map{ (models) -> [NJWSection] in return [NJWSection(items: models)] }.asDriver(onErrorJustReturn: []) let output = Output(sections: sections) input.category.asObservable().subscribe{ let category = $0.element output.requestCommand.subscribe(onNext: { [unowned self] isReloadData in self.index = isReloadData ? 0 : self.index + 1 NJWNetTool.rx.request(.requestWithcategory(type: category!, index: self.index)) .asObservable() .mapArray(GirlModel.self) .subscribe({[weak self] (event) in switch event{ case let .next(modelArr): self?.models.value = isReloadData ? modelArr : (self?.models.value ?? []) + modelArr NJWProgressHUD.showSuccess("加载成功") case let .error(error): NJWProgressHUD.showError(error.localizedDescription) case .completed: output.refreshStatus.value = isReloadData ? NJWRefreshStatus.endHeaderRefresh : NJWRefreshStatus.endFooterRefresh } }).disposed(by: self.rx.disposeBag) }).disposed(by: self.rx.disposeBag) }.disposed(by: rx.disposeBag) return output }}4.3 controller中数据绑定的具体实现把输入、输出和collectionview进行绑定,建立联系,达到操作UI进行数据刷新的目的。具体的绑定逻辑如下: ...

September 11, 2019 · 2 min · jiezi

AOE工程实践NCNN组件

作者:杨科 NCNN是腾讯开源的一个为手机端极致优化的高性能神经网络前向计算框架。在AOE开源工程里,我们提供了NCNN组件,下面我们以SqueezeNet物体识别这个Sample为例,来讲一讲NCNN组件的设计和用法。 直接集成NCNN缺点为SqueezeNet接入NCNN,把相关的模型文件,NCNN的头文件和库,JNI调用,前处理和后处理相关业务逻辑等。把这些内容都放在SqueezeNet Sample工程里。这样简单直接的集成方法,问题也很明显,和业务耦合比较多,不具有通用性,前处理后处理都和SqueezeNcnn这个Sample有关,不能很方便地提供给其他业务组件使用。深入思考一下,如果我们把AI业务,作为一个一个单独的AI组件提供给业务的同学使用,会发生这样的情况:每个组件都要依赖和包含NCNN的库,而且每个组件的开发同学,都要去熟悉NCNN的接口,写C的调用代码,写JNI。所以我们很自然地会想到要提取一个NCNN的组件出来,例如这样: AOE SDK里的NCNN组件在AOE开源SDK里,我们提供了NCNN组件,下面我们从4个方面来讲一讲NCNN组件: NCNN组件的设计对SqueezeNet Sample的改造应用如何接入NCNN组件对NCNN组件的一些思考NCNN组件的设计NCNN组件的设计理念是组件里不包含具体的业务逻辑,只包含对NCNN接口的封装和调用。具体的业务逻辑,由业务方在外部实现。在接口定义和设计上,我们参考了TF Lite的源码和接口设计。目前提供的对外调用接口,主要有以下几个: // 加载模型和paramvoid loadModelAndParam(...)// 初始化是否成功boolean isLoadModelSuccess()// 输入rgba数据void inputRgba(...)// 进行推理void run(...)// 多输入多输出推理void runForMultipleInputsOutputs(...)// 得到推理结果Tensor getOutputTensor(...)// 关闭和清理内存void close()新的代码结构如下: ├── AndroidManifest.xml├── cpp│   └── ncnn│   ├── c_api_internal.h│   ├── include│   ├── interpreter.cpp│   ├── Interpreter.h│   ├── jni_util.cpp│   ├── jni_utils.h│   ├── nativeinterpreterwrapper_jni.cpp│   ├── nativeinterpreterwrapper_jni.h│   ├── tensor_jni.cpp│   └── tensor_jni.h├── java│   └── com│   └── didi│   └── aoe│   └── runtime│   └── ncnn│   ├── Interpreter.java│   ├── NativeInterpreterWrapper.java│   └── Tensor.java└── jniLibs ├── arm64-v8a │   └── libncnn.a └── armeabi-v7a └── libncnn.aInterpreter,提供给外部调用,提供模型加载,推理这些方法。NativeInterpreterWrapper是具体的实现类,里面对native进行调用。Tensor,主要是一些数据和native层的交互。AOE NCNN组件有以下几个特点: ...

September 10, 2019 · 1 min · jiezi

Flutter三之搞定Dart一

前言一:接下来一段时间我会陆续更新一些列Flutter文字教程更新进度: 每周至少两篇; 更新地点: 首发于公众号,第二天更新于掘金、思否等地方; 更多交流: 可以添加我的微信 372623326,关注我的微博:coderwhy 希望大家可以 帮忙转发,点击在看,给我更多的创作动力。 前言二:若干年后,你会发现,选错了公司和行业的损失远比选错了语言来的大。而且计算机这个行业,只会一种编程语言显然是不现实的,接收一门新的语言,并没有你想象中的难! 一. Dart介绍和安装1.1. 认识DartGoogle为Flutter选择了Dart就已经是既定的事实,无论你多么想用你熟悉的语言,比如JavaScript、Java、Swift、C++等来开发Flutter,至少目前都是不可以的。 在讲解Dart的过程中,我会假定你已经有一定的编程语言基础,比如JavaScript、Java、Python、C++等。 其实如果你对编程语言足够的自信,Dart的学习过程甚至可以直接忽略: 因为你学过N种编程语言之后,你会发现他们的差异是并不大;无非就是语法上的差异+某些语言有某些特性,而某些语言没有某些特性而已;在我初次接触Flutter的时候,并没有专门去看Dart的语法,而是对于某些语法不太熟练的时候回头去了解而已;所以,如果你对编程语言已经足够了解,可以跳过我们接下来的Dart学习: 我也并不会所有特性都一一罗列,我会挑出比较重要的语言特性来专门讲解;某些特性可能会等到后面讲解Flutter的一些知识的时候单独拿出来讲解;下面,我们就从安装Dart开始吧! 1.2. 安装Dart为什么还需要安装Dart呢?事实上在安装Flutter SDK的时候,它已经内置了Dart了,我们完全可以直接使用Flutter去进行Dart的编写并且运行。 但是,如果你想单独学习Dart,并且运行自己的Dart代码,最好去安装一个Dart SDK。 下载Dart SDK 到Dart的官方,根据不同的操作系统下载对应的Dart 官方网站:https://dart.dev/get-dart无论是什么操作系统,安装方式都是有两种:通过工具安装和直接下载SDK,配置环境变量 1.通过工具安装 Windows可以通过ChocolateymacOS可以通过homebrew具体安装操作官网网站有详细的解释2.直接下载SDK,配置环境变量 下载地址:https://dart.dev/tools/sdk/ar...我采用了这个安装方式。下载完成后,根据路径配置环境变量即可。1.3. VSCode配置学习Dart过程中,我使用VSCode作为编辑器 一方面编写代码非常方便,而且界面风格我也很喜欢另一方面我可以快速在终端看到我编写代码的效果使用VSCode编写Dart需要安装Dart插件:我目前给这个VSCode装了四个插件 Dart和Flutter插件是为Flutter开发准备的Atom One Dark Theme是我个人比较喜欢的一个主题Code Runner可以点击右上角的按钮让我快速运行代码 二. Hello Dart2.1. Hello World接下来,就可以步入正题了。学习编程语言,从祖传的Hello World开始。 在VSCode中新建一个helloWorld.dart文件,添加下面的内容: main(List<String> args) { print('Hello World');}然后在终端执行dart helloWorld.dart,就能看到Hello World的结果了。 完成了这个执行过程之后,以你之前学习的编程语言来看,你能得到多少信息呢? 2.2. 程序的分析接下来,就是我自己的总结: 一、Dart语言的入口也是main函数,并且必须显示的进行定义;二、Dart的入口函数main是没有返回值的;三、传递给main的命令行参数,是通过List<String>完成的。 从字面值就可以理解List是Dart中的集合类型。其中的每一个String都表示传递给main的一个参数;四、定义字符串的时候,可以使用单引号或双引号;五、每行语句必须使用分号结尾,很多语言并不需要分号,比如Swift、JavaScript;三. 定义变量3.1. 明确声明(Explicit)明确声明变量的方式, 格式如下: 变量类型 变量名称 = 赋值;示例代码: String name = 'coderwhy';int age = 18;double height = 1.88;print('${name}, ${age}, ${height}'); // 拼接方式后续会讲解注意事项: 定义的变量可以修改值, 但是不能赋值其他类型 ...

September 9, 2019 · 4 min · jiezi

AoE如何管理好模型

作者:丁超 前言越来越多的业务会用到AI相关的技术,大多数的AI模型是部署在云端使用的,毕竟服务端计算更快,管理也更容易。随着终端设备性能提升,在终端使用 AI 模型有了更大的价值,可以更好满足业务对响应实时性、数据隐私性的需求。滴滴出行的银行卡识别功能也打算部署在客户端,但是遇到的问题也不少: 模型升级困难,模型在终端的存在一般都是已应用软件为载体,用户可以选择是否对应用软件进行更新,导致模型版本会产生分化。硬件适配问题,不同的终端设备因为厂商深度定制因素,会出现一些兼容问题不同模型运行框架不同,对于客户端工程师不够友好。针对这些问题滴滴的终端智能团队推出了AoE作为解决方案,设计之初就将多模型管理支持可能升级、多框架支持、模型加密等功能定为基础设施。 AoE是怎么做好模型管理的我们针对遇到的问题,主要做了3部分工作: 尝试了多机型覆盖测试做好模型的验证利用运行环境配制化来实现加载模型通过动态更新来升级模型下面针对这三项分别进行介绍。 运行环境配置化AoE SDK将推理框架总结了5个过程,它们分别是初使化、前处理、执行推理、后处理、释放资源。对 AoE 集成运行环境来说,最基本的便是抽象推理操作,通过 依赖倒置 的设计,使得业务只依赖AoE的上层抽象,而不用关心具体推理框架的接入实现。这种设计带来的最大的好处是开发者随时可以添加新的推理框架,而不用修改框架实现,做到了业务开发和 AoE SDK 开发完全解耦。 用户只需要简单的描述json文件即可完成对运行环境的配置,简化了用户的使用过程,更为简洁高效。 简单的配置如下: { "version": "1.0.0", // 版本号 "tag": "tag_mnist", // 区分业务场景 "runtime": "tensorflow", // runtime类型 "source": "installed", // 安装源 "modelDir": "mnist", // 所在文件夹 "modelName": "mnist_cnn_keras", // 模型文件名 "updateURL": "https://www.didiglobal.com" // 升级配置链接}机型覆盖测试针对硬件差异的问题,我们在做模型验证期间尝试了多机型的覆盖测试,将模型在不同机型上的表现都记录下来反馈给模型生产团队,帮助模型不断的升级修复。 截取了部分测试时产生的耗时对比数据大致如下: 虽然模型不相同,使用指令可能不同,但是大致也可以了解到机器的性能,具体数值仅供参考。在这个过程中,沉淀下来了benchmark工具来帮助验证多机型的覆盖测试,将来这个工具也会是开源的一部分来帮助大家验证模型的可用性,以及建立有效的机型比较。 动态更新AoE的模型管理模块将模型按分发方式分为两种: 本地模型,意为应用软件自带的模型远程模型,则是通过策略配置,从服务器下载匹配模型到本地的模型本地模型与远程模型最大的区别就是本地模型无法更改,只能跟随应用软件一起更新,而远程模型则是通过和本地模型作比较后更新的较新模型,模型与模型之间通过版本做比较。本地模型与远程模型二者可以共存,也可以单独存在,在最新版的滴滴出行中,为了减少包的大小甚至没有本地模型,所有的模型都是来自远端下载。 之所以将模型分成两部种,是为了保证模型是可用的且可靠的,为什么这么说?一般本地模型都是经过长时间测试后才作为稳定版本跟随APP带到了线上,既可以作为最新版本,又可以作为后来的稳定版本:即使发现后来下载升级的远程模型效果不理想也可以通过灰度测试停止远程使用远程模型的使用,保证模型的高可用性。 远程模型的存在使业务模型拥有了动态更新的能力,方便了产品的迭代,不再依赖客户端的发布周期。在动态开关的写协助下,甚至可以做到精确指定模型版本的加载。 整体模型管理的结构如下图: 模型加载怎么使用?模型管理器是AoE的一个基础组件,以iOS为例,组件实现在Loader目录下。默认支持的模型配置文件为json格式,运行环境配置化部分的代码就描述了mnist demo的配置。 模型和模型配置文件名的格式配置以及远程版本存放地址,都可以通过继承AoEModelConfig类来做修改,具体的使用方式可以参照squeezenet的实例 在已经开源的版本中AoE还为大家提供了单功能多模型的支持,拿银行卡识别来举例,整个过程分两步,一是找到卡片以及卡片上的数字区域,二是根据数字区域的图片识别出卡号,所以整个过程需要两个模型。开源项目使用的模型配置的tag字段主要用来定义模型所属功能,结合dir字段,就可以定位到具体的模型。 写在最后通过远程加载以及多维度的灰度测试配置是的帮助模型稳定安全运行的保证,虽然模型远程加载功能还没有在开源版本上线,但是已经安排在了日程中,预计在9月低就会上线。如果您对这个项目感兴趣,如果您在终端AI运行环境方面有想法,如果您在使用时有疑问,诚挚邀请您加入我们。 Github地址: 欢迎star~ QQ交流群(QQ群号:815254379): ...

September 9, 2019 · 1 min · jiezi

回答好进程与线程程序员基础面试题20190908

操作系统: 进程与线程进程 = 资源管理 + 线程, 进程是资源分配单位,线程是 CPU 调度单位以前没有线程的时候,进程是操作系统中独立运行的基本单位 线程出现后,线程是操作系统中,更小的能独立运行的基本单位 到底什么是进程?怎么理解进程?一般,可以把运行的程序,看成进程。 程序在整个执行过程中的描述,是进程。 程序在运行过程中,会消耗各种资源,会占用 CPU、内存和网络,会使用文件系统 I/O 进程就是运行的程序的执行过程为什么要有进程?最初的操作系统,只能支持跑一个程序。 随着计算机的发展,CPU 的能力越来越强,内存越来越多。可以在内存中,放入更多的可以运行的程序。 操作系统中,各个运行的程序的状态。用程序来表示,不够准确,需要新的概念。 因为可能存在内存中,一个程序跑了多份的情况。内存中,一个程序的多个实例,怎么来清楚的表示出来呢? 进程的概念,就出来了。进程用来表示程序整个的执行过程我们写的程序,编译为可执行程序,放在文件系统里面,格式是可执行文件,他是静态的。 只有当我们的操作系统,把可执行程序,调入我们的内存中之后,让这个程序能够执行起来。执行,也就是这个程序能够通过 CPU , 来执行程序中的一条条指令,然后对相应的数据,进行处理,完成一定的功能。 这个过程,是一个动态执行的过程,我们称为进程。 进程的标准答案:一个具有一定独立功能的程序,在一个数据集合上的,一次动态执行过程这整个的过程,就是进程为什么要有线程?多进程,消耗太大。 进程之间,他们的地址空间是独立的,一般通过沙盒保护。进程间通信,交换数据,需要通过操作系统,开销大,过程相对复杂。多线程,开销就小了一些。 多线程除了实现了并发,还共享相同的地址空间,也就是共享相同的资源。 一个线程产生的数据,另一个线程可以很容易地拿到。 线程间通信,比较简单。 什么是线程?线程是进程中的一条执行流程从资源组合的角度看:进程,就是用来管理资源的。资源包括地址空间(代码段、数据段)、打开的文件、访问的网络。 把进程执行的功能、执行的状态,做一些拆分,交给线程来管理。 从运行的角度看:线程就是,代码在这个资源平台上的一条执行流程。 进程 = 资源管理 + 线程进程除了管理资源,还有一系列的线程,用来完成执行的过程。一个进程的多个线程,共享进程所拥有的所有资源。 线程 = 进程 - 资源管理线程,用来完成控制流的管理 区别:进程是资源分配单位,线程是 CPU 调度单位进程拥有一个完整的资源平台,线程只独享必不可少的资源,如寄存器和栈,用于维持自己的、每个线程独立的控制流。进程的资源,进程的多个线程共享线程能减少并发执行的时间和空间开销例如: 线程的创建时间比进程短 因为进程在创建过程中,需要创建其他的管理信息。如: 内存要怎么管理,打开的文件要怎么管理 线程要创建的时候,直接重用该线程所属的进程资源,这些资源已经管理好了。所以线程不需要创建这些管理信息

September 8, 2019 · 1 min · jiezi

IOS-Resign-Upload-Tool-Error-replace-with-fastlane-resign

Resign your app with fastlane1.Setup fastlane environment:https://docs.fastlane.tools/g... 2.terminal sudo gem install fastlane 3.command: cd <<ipa path>> 4.Resign Commandfastlane sigh resign -g <<bundleId>> --signing_identity "<<distribution certificate full name>>" -p "<<provision file absolute path>>" --short_version 9.2 --bundle_version 1 --use_app_entitlements <<IPA name>> example:fastlane sigh resign -g com.test --signing_identity "iPhone Distribution: TestDemo (XXXXXXXXXX)" -p "/Users/abby/Documents/APP/TEST.mobileprovision" --short_version 9.2 --bundle_version 1 --use_app_entitlements wrapped.ipa ps:注意空格格式,ruby version>2.3.0成功提示‘ Successfully signed xxxx.ipa!' 验证是否重签名成功,打开ipa解压,Payload路径文件下有个文件,显示包内容 ,可以看到ipa包的所有内容。打开info.plist,找到Bundle identifier查看是否已经更改

August 21, 2019 · 1 min · jiezi

IOS-ApplicationLoader-error

Error: Could not find version: latest of ITMSTransporter to download Step1: GO to folder/Applications/Xcode.app/Contents/Applications/Application Loader.app/Contents/itms/bin/iTMSTransporter Step2: double click iTMSTransporter terminal log as below:saving session......copying shared history......saving history...truncating history files......completed.Deleting expired sessions...193 completed. [Process completed] Step3: Upload your ipa again.

August 21, 2019 · 1 min · jiezi

只有程序员才懂的痛

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

August 20, 2019 · 1 min · jiezi

vPlayer-模块Demo

本文出自APICloud官方论坛 vPlayer iOS封装了AVPlayer视频播放功能(支持音频播放)。iOS 平台上支持的视频文件格式有:WMV,AVI,MKV,RMVB,RM,XVID,MP4,3GP,MPG等,音频文件格式有:MP3,WMA,RM,ACC,OGG,APE,FLAC,FLV等。本模块封装了两套播放方案:一,通过调用 openPlayer 接口,直接打开一个自带默认播放界面的播放器;二,通过 open 接口,打开一个纯播放器界面,再配合 frame 自定义完整的播放页面,通过play、pause等接口控制播放操作。 效果图: openPlayer示例代码: var vPlayer = api.require('vPlayer'); vPlayer.openPlayer({ rect: { x: 0, //(可选项)数字类型;模块左上角的 x 坐标(相对于所属的 Window 或 Frame);默认:0 y: 30, //(可选项)数字类型;模块左上角的 y 坐标(相对于所属的 Window 或 Frame);默认:0 w: api.frameWidth, //(可选项)数字类型;模块的宽度;默认:所属的 Window 或 Frame 的宽度 h: 300 }, path: 'http://www.w3school.com.cn/example/html5/mov_bbb.mp4', autoPlay: true, coverImg: 'widget://image/video/cover_img.png', styles: { head: { bg: 'rgba(161,161,161,0.4)', height: 44, marginTop: 0, hide: false, backBtn: { //ok size: 32, backImg: 'widget://image/video/back.png', marginLeft: 0 }, titleLabel: { //ok title: '视频播放', size: 16, color: '#FFFFFF', width: 170, numberLines: 3, leftMargin: 5, // backgroundColor:'rgba(161,161,161,0.4)' }, customButtons: [{ w: 32, h: 32, rightMagin: 5, img: 'widget://image/video/delete.png', imgSelected: 'widget://image/video/delete_sel.png', }, ] }, foot: { bg: 'rgba(161,161,161,0.4)', height: 44, marginBottom: 0, hide: false, playBtn: { size: 32, playImg: 'widget://image/video/play.png', pauseImg: 'widget://image/video/pause.png', marginLeft: 5 }, currentTimeLabel: { textSize: 14, textColor: "#FFFFFF", marginLeft: 5 }, seekBar: { sliderImg: 'widget://image/video/seek_bar.png', progressColor: '#FA8072', progressSelectedColor: '#A2CD5A', marginLeft: 5, marginRight: 5 }, totalTimeLabel: { textSize: 14, textColor: "#FFFFFF", marginRight: 5 }, fullScreenBtn: { size: 32, img: 'widget://image/video/fullscreencal.png', fullScreenImg: 'widget://image/video/fullScreen.png', marginRight: 10 } } }, fixedOn: api.frameName, fixed: true }, function(ret) { if (ret) { alert(JSON.stringify(ret)); } });复制代码

August 19, 2019 · 1 min · jiezi

开发一款-iOS-音乐播放器的五个点

播放很简单一般分为两个过程,准备播放,与播放准备播放,包括准备播放资源、播放器初始化和播放器准备好 其中准备播放资源 var currentAudioPath:URL! currentAudio = readSongNameFromPlist(currentAudioIndex) if let path = Bundle.main.path(forResource: currentAudio, ofType: "mp3"){ currentAudioPath = URL(fileURLWithPath: path) } else{ alertSongExsit() }播放器初始化和播放器准备好 var audioPlayer:AVAudioPlayer! audioPlayer = try? AVAudioPlayer(contentsOf: currentAudioPath) audioPlayer.delegate = self audioLength = audioPlayer.duration playerProgressSlider.maximumValue = CFloat(audioPlayer.duration) playerProgressSlider.minimumValue = 0.0 playerProgressSlider.value = 0.0 audioPlayer.prepareToPlay()播放audioPlayer.play(), 一行代码 第一点,进度条怎么做? 一般进度条,会做两件事, 随着播放的推移,进度条的滑块会一直向前走,有一个音乐播放与进度条的进展的匹配 进度条的滑块可以拖拽,来控制当前播放的地方,譬如可以回播,可以跳过 播放音乐,进度条的滑块也走,进度是匹配的每次播放前,先设置进度条的进度, maximumValue 最大值,就是放完了,一首歌的时长 minimumValue 最小值,就是没播放,为 0 value 开始的时候,就是没播放,为 0 playerProgressSlider.maximumValue = CFloat(audioPlayer.duration) playerProgressSlider.minimumValue = 0.0 playerProgressSlider.value = 0.0要想进度条的滑块会一直向前走,就要有一个计时器 ...

August 19, 2019 · 3 min · jiezi

在-iOS-与-Android-上实现-React-Native-应用的尝试链接

原文:https://pantao.parcmg.com/press/react-native-deep-linking-for-ios-android.html代码:https://github.com/pantao/react-native-deep-linking-example 我们生活在一个万物兼可分享的年代,而分享的过程,几乎最终都会分享某一个链接,那么,作为开发者,最常遇到的问题中应该包括如何通过一个URL地址快速的打开App,并导航至特定的页面。 什么是深度链接(Deep Link)深度链接是一项可以让一个App通过一个URL地址打开,之后导航至特定页面或者资源,或者展示特定UI的技术,Deep 的意思是指被打开的页面或者资源并不是App的首页,最常使用到的地方包括但远远不限于 Push Notification、邮件、网页链接等。 其实这个技术在很久很久以前就已经存在了,鼠标点击一下 mailto:pantao@parcmg.com 这样的链接,系统会打开默认的邮件软件,然后将 pantao@parcmg.com 这个邮箱填写至收件人输入栏里,这就是深度链接。 本文将从零开始创建一个应用,让它支持通过一个如 deep-linking://articles/{ID} 这样的 URL 打开 文章详情 页面,同时加载 {ID} 指定的文章,比如:deep-linking://articles/4 将打开 ID 为 4 的文章详情页面。 深度链接解决了什么问题?网页链接是无法打开原生应用的,如果一个用户访问你的网页中的某一个资源,他的手机上面也已经安装了你的应用,那么,我们要如何让系统自动的打开应用,然后在应用中展示用户所访问的那一个页面中的资源?这就是深度链接需要解决的问题。 实现深度链接的不同方式有两种方式可以实现深度链接: URL schemeUniversal links前端是最常见的方式,后者是 iOS 新提供的方式,可以一个普通的网页地址链接至App的特定资源。 本文将创建一个名为 DeepLinkingExample 的应用,使得用户可以通过打开 deep-linking://home 以及 deep-linking://articles/4 分别打开 App 的首页以及 App 中 ID 为 4 的文章详情页面。 react-native init DeepLinkingExamplecd DeepLinkingExample安装必要的库紧跟 TypeScript 大潮流,我们的 App 写将使用 TypeScript 开发。 yarn add react-navigation react-native-gesture-handlerreact-native link react-native-gesture-handler我们将使用 react-navigation 模块作为 App 的导航库。 ...

August 18, 2019 · 5 min · jiezi

百度App-iOS工程化实践-EasyBox破冰之旅

前言百度App从单一的搜索工具发展到今天以搜索和Feed流为双引擎的综合性内容消费服务平台,其复杂程度已然不可同日而语矣。 作为一个日活过亿的超级App,业务规模庞大,相关技术人员超过千人,客户端支持主流的移动技术,涉及近百业务方,技术形态复杂,各种组件近三百个,代码百万量级,由此带来的工程化问题是技术团队的一个极大挑战。 项目的膨胀导致了很多不起眼的小问题被无限放大,组件管理不规范、编译时间长、工程文件合并冲突、Xcode默认非彻底编译隔离等等问题,导致开发人员在开发环境上耗费了大量时间。目前业界较流行的工具对于大规模工程的支持力度相对较弱,实践起来总是有些掣肘,难以达到理想状态。 EasyBox的诞生,就是致力于为超级App量身打造一套现代、高效、优雅的研发工具链。 这篇文章的主要目的是站在工具链的角度上,分享一下我们在实践工程化过程中一些经验。 概述EasyBox主体由工程组装器(Installer)、多仓库管理工具(MGit)、二进制管理工具(LFS)三部分构成,分别负责工作区的构建(组件依赖分析、工程的生成与组合)、源码仓库的管理以及二进制的管理。下图为EasyBox架构图:由多仓库管理工具克隆所需仓库源码,由二进制管理工具下载二进制包,然后组装器根据描述表生成对应工程,组合层级并最终生成Workspace。下图为简化工作流程示意图: 实践EasyBox诞生的过程本质上是工程化逐步深入的过程。分而治之的道理大家都明白,但这并不意味着工程化就是简单的拆库重组。其目的是要对项目工程进行合理化改造,让开发人员能够快速理解工程架构并进入开发状态,避免开发人员在开发环境上花费过多时间,从而提高编码、测试等阶段的研发效率。在这个过程中我们在规范组件管理与使用、强化工程能力、提升编译速度这三个方面不断优化,最终形成EasyBox独特的优势。 1.规范组件管理与使用组件(源码/二进制)统一使用描述表(boxspec)描述,依赖与API管理均由描述表唯一决定,配合编译隔离使得组件边界划分明确。组件的版本号严格遵守语义化版本(Semantic Versioning)规范,组件新版本发布时,需经过持续集成分析API变化等一系列检查之后方可完成发布。 破窗理论 环境中的不良现象如果被放任存在,会诱使人们仿效,甚至变本加厉。组件基于源码发布来完成二进制发布及接口发布,以确保安全性,方便进行源码回归校验以及必要时基于同一节点发布不同类型的二进制,接口发布用于完成矩阵产品的组件替换。发布版本的描述表与二进制文件交由专门的文件服务器管理,并禁止二进制文件入源码仓库以避免仓库膨胀。更多源码与二进制管理细节参见后文介绍。 2.强化工程能力2.1 组件的独立与隔离 与业界其他大多数工具不同,EasyBox采取了每个组件都拆分为独立工程的方案,同时也将不同的组件的编译产物放在了不同的目录下,这样做的目的是确保彻底的编译隔离。 这里其实是源自Xcode的遗留的两个坑: 同一个工程的OC/C/C++文件就算不在同一个Target下且没有依赖关系也可以互相访问(Swift是不允许的)。Xcode会自动将编译产物所在的文件夹(BUILD_PRODUCTS_DIR)添加到 FRAMEWORK_SEARCH_PATHS 中,而同一个Workspace下的编译产物默认是在同一文件夹下的。这两个坑都会打破编译隔离,后者更会导致在不同开发者的电脑上编译结果不同,时常出现自己编译过了而别人编译不过的情况,这是其实由于组件编译次序不同导致的。 但由于Xcode工程文件一直是饱受诟病的设计,多人协作时工程文件合并冲突简直就是一场噩梦,所以我们采用了组件配置表的方案来完成去工程文件化,每个组件通过配置一个boxspec文件(类似于CocoaPods的podspec),组件源码会根据实际目录映射对应工程结构,同时生成一个xcfilelist来维护当前组件所配置的资源列表,并最终生成工程文件。该工程文件不被Git追踪,从而避免合并冲突问题。 编译隔离可以带来很多好处,比如组件边界一定是明确的,几百个组件组合在一起能不能编译过不再看"运气"。它限制了组件修改时所影响的范围,这将有助于组件在不同环境(App)下编译构建、多端复用,使得输出时开发者对于组件的依赖是有预期的。 另外配合接口发布及组件化中的协议解耦,使得组件输出时可以选择性的只携带其他组件的接口(而非实现),从而很容易地完成依赖组件的剥离与替换。下图为boxspec描述表示例:下图为自动生成组件工程示例:2.2 多仓库的拆分与管理 代码集中管理导致主仓库越来越臃肿,而且代码权限问题难以管理,这对于大团队的开发而言是个非常痛苦的事情,很容易出现组件在负责人不知道的情况被其他人合入代码,加之一定的组件易手率,情况会变得更加糟糕。 熵增原理 在自然情况下,一切物质都将趋向于无序。为解决这个问题,我们将各组件拆分为独立仓库,完成物理隔离,做到入库权限收紧,物各有主。另外这对于多产品复用起了至关重要的作用,也是中台化建设的必要条件。在实际实践过程中,为了避免某些仓库过于琐碎,也会出现一个仓库多个组件的情况,但并不影响大局。 多仓库的拆分从某种程度上加剧了工程的复杂度,开发者不可避免地出现需要操作多个仓库的情况,这时直接使用Git操作成本高且易出错,为此我们专门设计了多仓库管理工具(MGit, Android/iOS双端共用)。与Android系统源码多仓库管理工具Repo不同,MGit保持了Git的大多数指令和用法,同时在内部执行时保证多仓库操作的安全性,在执行风险操作时做出必要提醒。开发者只需使用mgit替代git命令即可完成多仓库操作,这样既保持了大多数开发者的使用习惯,又可以安全方便地同时操作多个仓库。同时我们利用Gerrit的topic机制并加以改造,实现多仓库的分组提交以确保原子性入库,进而保证自动打包机制的正常运作。下图为MGit与Repo指令对比图:下图为MGit使用示例:多仓库带来一个最核心的问题是组件节点同步问题。当不同组件的源码处于不同节点,很容易出现编译不过或功能不正常等问题。可以通过采用『同名分支原则』来规避这个问题,即将所有要开发的仓库保持在同一个分支,再配合其他组件的版本依赖管理,一起保证各组件节点的匹配。这个规则非常简单易记,在大团队推广起来也不会有什么成本。 仓库嵌套问题 由于历史遗留原因,我们在拆仓库的初期选择了在组件原有位置创建仓库,最初设计上考虑不周给我们带来了不小的麻烦,不同分支的gitignore的不同会引起文件追踪状态的错乱,后来便采用将源码仓库进行平铺,以避免这个问题。2.3 层级的动态划分与构建 大型项目基本都会走上分层架构之路,分层带来的好处也是显而易见的,软件架构更加清晰,规范更容易确立,而层级的单向访问也有助于降低工程复杂度。分层设计本质上是对开闭原则的践行,这一原则通常是我们对架构设计时的主导原则之一,其目的是让软件更易于扩展,限制每次修改所影响的范围。具体就是将软件划分为一系列组件,并将这些组件按层级进行组织,使得下层组件不会因为上层组件的修改而受到影响。 所以与其他工具不同的是,EasyBox选择在设计上支持层级的划分,我们希望EasyBox不仅仅承担包管理器的作用,也起到帮助架构师规范好整个项目的作用。随着团队的扩大,依赖不合理的问题更加显著,很多事情不再是简单的给个规范、喊一嗓子就可以解决的,这时我们倾向于制定强硬的规则,来确保不会出现明显的问题。分层的设计也可以让团队新成员快速理解工程架构设计,时刻提醒系统边界的重要性,同时建立对依赖的约束限制。 "立法" 要远比 "道德规范" 来的直接有效。约束的建立可以规避很多问题,举个例子,PM要求对某个视图的展示事件打点,如果对组件化理解不深或者偷懒的话,很容易直接在UI库里直接进行打点,而这将为后续组件的复用带来很大的问题。下图为百度App现行架构图(上层组件可以访问下层组件,不可逆向访问):下图为层级配置示例:下图为自动生成工程示例:在组合构建过程中,无论是组件还是子组件,都采用了直接链接到最终产物(App或Dynamic Framework)的做法,其原因是静态库之间的合并风险是很高的,比如符号重复时会仅仅会给出一个警告,然后触发自动裁剪。 force_load问题 如果App兼容iOS8的话,苹果对于主包二进制有大小限制,这时可以将底层Layer改为动态库来减少主包二进制体积,此时一些C/C++的组件如果出现跨层调用时是需要force_load的,实际应用过程中应尽可能地使用OC封装这些库而避免上层业务直接使用这些组件,尽可能的少使用force_load,从而避免包体积增大。3.提升编译速度3.1 组件二进制化 业务的膨胀导致百度App代码激增,这大幅拖累了编译速度,仅主业务(抛开几十家业务方及ffmpeg、opencv之类的重量级三方库)编译时长也接近20分钟(13' RMBP),所以我们决定采用二进制化方案来解决这个问题,即由集群将组件打包为二进制并上传至文件服务器,开发时仅保留需要开发的组件的源码,其他各组件均以二进制存在。通过二进制化,正常情况下(1至3个处于开发模式的组件)全量编译时间压缩至2分钟(13' RMBP)以内,增量编译速度也明显加快,而对于工程文件的缓存也可以有效减少全量编译的次数。 宿主工程的配置是由Boxfile、Boxfile.overlay、Boxfile.local三个文件配合完成的,配置生效优先级Boxfile.local > Boxfile.overlay > Boxfile。overlay和local格式相同,都是用于开发联调阶段使用的临时配置文件,用于二进制源码的切换,区别在于overlay被git追踪,用于多人协同开发以及持续集成打包,而local则不被git追踪,仅用于本地调试。当二进制切回开发模式时,如果该仓库分支不存在,会根据当前组件版本节点(而不是master)创建对应分支,以保证分支的起始节点同步。下图为Boxfile.overlay配置示例:下图为可视化配置工具示例: 二进制失效问题 二进制化对组件接口层的稳定是有很强的要求的,组件接口层应当尽可能稳定,尤其注意宏/枚举等声明改动引起其他组件二进制失效问题,接口层尽可能采用增量扩展的形式(旧接口标记废弃)。此时建设监测机制确保版本号的正确性就显得尤为重要,我们通过Clang插件来完成对API变动的监控,在组件发布之前进行校验。二进制化还会带来一个很大的问题就是给开发者的调试带来不便。Java、JS等语言都会有很完善的源码映射(Source Map)机制来弥补打包后带来的调试问题,而对于OC/Swift来说,这方面的建设却是非常少见的。用过Carthage的同学都知道,Carthage可以从工程单步调试进入到源码里面的,但是这仅局限于本地编译出来的二进制,而且也只能从外部通过单步调试进入。而我们希望达到的效果是: 二进制包由集群编译打包,本地开发时使用二进制文件,工程根据配置自动导入源码完成源码映射,源码不参与编译,但断点调试依然有效。这里要先理解断点的本质是什么,断点其实是一个含有触发条件的坐标,断点 = 源文件位置 + 代码行数 + 触发条件。 lldb下通过breakpoint list查看断点信息 集群编译和本机编译的区别在于源文件的位置,所以只要保证源文件位置相同,就可以达到理想效果。可以借助编译参数-fdebug-prefix-map来完成源文件位置的匹配,在集群编译时通过该参数将组件源码目录指向 /tmp/easybox/$(VIRTUAL_ID) (VIRTUAL_ID是根据组件信息与时间戳生成的定长字符串)。当EasyBox需要进行源码映射时,只需导出一份对应时间节点的源码,然后将该源码目录软链到/tmp/easybox/$(VIRTUAL_ID)目录下,映射就已经完成了。再将该文件添加至工程中(不参与编译,引用路径须是/tmp/easybox/$(VIRTUAL_ID)/*),此时便可以愉快地玩(tiao)耍(shi)了。下图为源码映射原理示意图:我们可以借助dwarfdump命令查看二进制中相关的Debug信息。下图为修改前后的对比图: 其他方案 翻阅LLVM文档可以查到另外一些关于源码映射的资料:1.在运行阶段,可以借助lldb的source-map来修改源文件所处的位置,显然使用起来很不方便。2.动态链接库可以通过配置plist文件来完成源码映射,但在实际应用中,动态库会严重影响启动速度和包体积,通常各个组件均以静态库存在,故也未采用此方案。二进制化之后,编译速度有了质的飞跃,在实践过程中还针对一些细节的进行优化。 3.2 Clang模块缓存(头文件检索缓存) ...

August 17, 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

开发经验Flutter避免代码嵌套写好build方法

本文适合使用Flutter开发过一段时间的开发者阅读,旨在分享一种避免Flutter的UI代码嵌套太深问题的方法。如果对本文内容或观点有相关疑问,欢迎在评论中指出。优化效果(缩略图): 距离我接触Flutter已经过去了九个月,在Flutter代码编写的过程中,很多开发者都遇到了“回调地狱”的问题。在Flutter中,称之为回调并不准确,准确的说,是因为众多Widget互相嵌套在一起,导致反括号部分堆积严重,极度影响代码可读性。 本文将介绍一种代码编写风格,最大限度减少嵌套对代码阅读的影响。 初步介绍我们先来简单看一下,Flutter的UI代码: 使用build方法Flutter的Widget使用build方法来创建UI组件,然后通过注入child属性的方式为组件添加子组件,子组件可以继续包含child,通过调用每一个child的build方法,就形成了类似DOM结构的组件树,然后由渲染引擎渲染图形。 一个常见的定义组件的例子如下: class DeleteText extends StatelessWidget { // 我们在build方法中渲染自定义Widget @override Widget build(BuildContext context) { return Text('Delete'); }}组件属性必须为final要在Flutter中定义(继承)一个Widget,则它的属性必须都是final的。final意味着属性必须在构造函数中就被初始化完成,不接受提前定义,也不接受更改。所以,在生命周期中动态的改变Widget对象的属性是不可能的,必须使用框架的build方法来为构造函数动态指定参数,从而达到改变组件属性的功能。 class Avatar extends StatelessWidget { // 如果url属性不是final的,编译器会报出警告 final String url; // 这个构造方法很长,但是主要你写了final属性,VSCode就会帮我们自动生成 const Avatar({Key key, this.url}) : super(key: key); @override Widget build(BuildContext context) { return Container( decoration: BoxDecoration( borderRadius: BorderRadius.circular(8), ), child: Image.network(url), ); }}Tips:自动创建构造方法,只要是构造方法没有的final属性,点击“快速修复”,就可以自动生成构造方法。 Flutter语法与HTML/CSS嵌套正是DOM树的特点,正如HTML其实也会无限嵌套一样(大多数前端可能看HTML看习惯了,都忘了HTML其实也经常会写成嵌套很深的形式),Flutter的UI代码嵌套本质是不可避免的,这正是Flutter UI代码的编写特点——一次成型,而不是通过addView之类的方法来手动管理每一个视图的生命周期。在此基础上,Flutter可以高效的反复重建Widget,在渲染效率上展现出了非常大的优势。 <!-- html的嵌套其实也很深 --><div> <div> <div> <div> <article> <h1></h1> <li></li> </article> </div> </div> </div></div>嵌套代码难以阅读当我们评判一串代码的时候,一个显而易见的点,就是代码距离左边的距离,如果一行代码距离左边达到了十多个tab,可想而知它被嵌套在了多么深的位置。 ...

July 14, 2019 · 5 min · jiezi

swift的一些面试题

一、open与public的区别public:可以别任何人访问,但是不可以被其他module复写和继承。open:可以被任何人访问,可以被继承和复写。二、struct与class 的区别struct是值类型,class是引用类型。 值类型的变量直接包含它们的数据,对于值类型都有它们自己的数据副本,因此对一个变量操作不可能影响另一个变量。引用类型的变量存储对他们的数据引用,因此后者称为对象,因此对一个变量操作可能影响另一个变量所引用的对象。二者的本质区别:struct是深拷贝,拷贝的是内容;class是浅拷贝,拷贝的是指针。property的初始化不同:class 在初始化时不能直接把 property 放在 默认的constructor 的参数里,而是需要自己创建一个带参数的constructor;而struct可以,把属性放在默认的constructor 的参数里。变量赋值方式不同:struct是值拷贝;class是引用拷贝。immutable变量:swift的可变内容和不可变内容用var和let来甄别,如果初始为let的变量再去修改会发生编译错误。struct遵循这一特性;class不存在这样的问题。mutating function: struct 和 class 的差別是 struct 的 function 要去改变 property 的值的时候要加上 mutating,而 class 不用。继承: struct不可以继承,class可以继承。struct比class更轻量:struct分配在栈中,class分配在堆中。三、swift把struct作为数据模型3.1优点安全性: 因为 Struct 是用值类型传递的,它们没有引用计数。内存: 由于他们没有引用数,他们不会因为循环引用导致内存泄漏。速度: 值类型通常来说是以栈的形式分配的,而不是用堆。因此他们比 Class 要快很多!拷贝:Objective-C 里拷贝一个对象,你必须选用正确的拷贝类型(深拷贝、浅拷贝),而值类型的拷贝则非常轻松!线程安全: 值类型是自动线程安全的。无论你从哪个线程去访问你的 Struct ,都非常简单。3.2 缺点Objective-C与swift混合开发:OC调用的swift代码必须继承于NSObject。继承:struct不能相互继承。NSUserDefaults:Struct 不能被序列化成 NSData 对象。参考文章Swift 浅谈Struct与Class

July 14, 2019 · 1 min · jiezi

企业-精选文章技术-iOS与Android在企业中的流行度

最近对10个不同行业的110名首席信息官进行的调查证实,苹果是移动设备和平板电脑的首选企业平台。 该调查报告称,约有50%的受访者支持iOS,而29%选择Android,19%使用Windows,1%使用BlackBerry。 Apple iOS和Google Android是智能手机和平板电脑最受欢迎的操作系统平台,因为它们具有丰富的开发格式和应用程序生态系统。 企业IT部门需要在为企业用户提供和设计解决方案时同等重视安全性。随着全球市场对iOS产品需求的指数增长,企业需要拥抱iOS,并将其整合到他们的需求中。业内专家认为,包括Android在内的其他现有移动平台都无法容纳iOS所能提供的一切。iOS支持简单的电子邮件,轻松访问社交网络和在线讨论平台,可为工作场所创建的应用程序提供自定义。它还支持访问SAP Crystal Reports和Salesforce.com等企业级应用程序。 企业更喜欢iOS用于简化文档,其中可以轻松查看带有Excel电子表格,PowerPoint演示文稿或Word文档的电子邮件附件,而无需强制下载新软件。Apple的iWork应用程序套件支持文档格式化和编辑,也可以导出为Microsoft格式。iOS允许Microsoft Exchange帐户以及与ActiveSync的兼容性,可以同步传输到任何iOS设备的企业Exchange服务器上托管的日历,联系人和电子邮件。宝宝起名网 从IT角度来看,iOS更具吸引力,因为其增强的安全性,因为应用程序被限制访问有风险的外国数据,防止恶意第三方应用程序访问敏感的公司信息。跨行业的CTO强烈建议iOS提供远程锁定功能,以便在设备丢失或被盗时擦除数据。 专家表示,它对标准VPN协议的扩展支持,允许员工使用SSL VPN,IPSec和WPA2企业Wi-Fi安全地访问公司内部网。iOS更新具有更长的使用寿命,与旧硬件具有更好的兼容性。此外,iOS设备可以远程配置,以处理邮件设置,WiFi设置,家长控制,应用程序安装和管理其他企业访问限制的更改。在iOS设备中启用了内部应用程序的远程部署。相比之下,每种类型的Android设备都需要在内部进行配置和支持,并与不同的供应商以及移动提供商进行评估和推广。 由于双方继续拥有更喜欢iOS或Android的有效协议,这场辩论将至少再持续十年。构建业务应用程序的企业开发人员不能仅仅针对其中一个,从而失去一大块市场份额。 微软对企业移动设备领域的控制是复杂的,考虑到其Windows Phone平台在增加市场份额方面的巨大努力。微软即将推出的移动版本是Windows 10的智能手机版本,未能在iOS和Android上获得市场份额。有鉴于此,微软的重要市场是因为运行Windows的平板电脑和可转换笔记本电脑。随着市场上iOS产品的日益普及,IT拥抱iOS并将其集成到企业运营中更为明智。 调查表明,Android过于分散,不断发展,可能不安全,不稳定。但是,企业需要在iOS和Android之间取得适当的平衡,以确保其应用程序的市场成功。

July 13, 2019 · 1 min · jiezi

Flutter开发中的一些Tips二

接着上篇 Flutter开发中的一些Tips,今天再分享一些我遇到的问题,这篇较上一篇,细节方面更多,希望“引以为戒”,毕竟细节决定成败。本篇的所有例子,都在我开源的flutter_deer中。希望Star、Fork支持,有问题可以Issue。附上链接:https://github.com/simplezhli... 1. setState() called after dispose()这个是我偶然在控制台发现的,完整的错误信息如下: Unhandled Exception: setState() called after dispose(): _AboutState#9c33a(lifecycle state: defunct, not mounted)当然flutter在错误信息之后还有给出问题原因及解决方法: This error happens if you call setState() on a State object for a widget that no longer appears in the widget tree (e.g., whose parent widget no longer includes the widget in its build). This error can occur when code calls setState() from a timer or an animation callback. The preferred solution is to cancel the timer or stop listening to the animation in the dispose() callback. Another solution is to check the "mounted" property of this object before calling setState() to ensure the object is still in the tree.This error might indicate a memory leak if setState() is being called because another object is retaining a reference to this State object after it has been removed from the tree. To avoid memory leaks, consider breaking the reference to this object during dispose().大致的意思是,widget已经在dispose方法时销毁了,但在这之后却调用了setState方法,那么会发生此错误。比如定时器或动画回调调用setState(),但此时页面已关闭时,就会发生此错误。这个错误一般并不会程序崩溃,只是会造成内存的泄露。 ...

July 11, 2019 · 4 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

解决ios-1231-微信H5页面文本框失去焦点后输入法收回空白-vue

问题描述:ios微信H5页面文本框失去焦点输入法收回会留有空白;设备:iphone XR系统版本:ios 12.3.1微信版本:7.3.1解决方法<template> <div> <textarea @blur="blurChange" name="" id="" cols="30" rows="10"></textarea> </div></template><script> export default { data () { return { } }, components: {}, props: {}, created () { }, mounted () { }, method: { blurChange () { let ua = navigator.userAgent; //判断设备和微信环境 if (/(iPhone|iPad|iPod|iOS)/i.test(ua) && /MicroMessenger/i.test(ua)) { document.body.scrollTop = document.body.scrollTop; } } }, }

July 9, 2019 · 1 min · jiezi

iOS多渠道统计方法解析

总所周知,iOS 是一个封闭的系统环境,当应用程序需要向外部请求或接收数据时,大部分都需要经过权限认证,否则无法获取到数据。更何况 iOS 本身就无法使用渠道包统计数据,iOS 企业签名包在上传服务器后更是难以引流下载。在这种情况下,如何给多个渠道做推广以及效果统计,是令不少开发者和运营人员头疼的问题。 从技术上,我们要实现 App Store 应用以及 iOS 企业签名包的多渠道推广效果统计。简单来说,包括各个推广渠道下用户的点击、注册、安装等运营推广数据的获取。 方案一:苹果官方统计(iTunes Connect) 在数据权威性上,苹果官方的统计工具必然最权威,也最值得信赖。 登录苹果的官方统计平台 iTunes Connect,在“App分析”模块可以很方便的查看到应用的“展示次数、购买量”等基础数据。 当然,App 推广往往需要多个渠道同时进行,由于 App Store 无法制作渠道包统计,因此 iTunes Connect 也很方便的提供了渠道链接统计服务。只需要在“App分析”的“来源”中点击“营销活动”,右上角有个“生成营销活动链接”,进入后就能自定义设置对应的唯一标识,给每个渠道生成专属的渠道链接。 拿着对应的链接去推广,虽然可以追踪到不同渠道下的精准来源,但 iTunes Connect 的统计也存在许多问题: 只有当营销活动启动后超过一天时间(最长72个小时)后才能显示相关数据;至少有 5 个 App 安装量归因于此营销活动时,营销活动才会在“App 分析”中显示;iOS 8.0 及以上版本的用户可以选择是否将自己的应用使用情况的数据发送给 Apple;iTunes Connect 的统计无法同时兼容 Android 和 iOS,采用不同的统计方法可能会让数据统一性较差。方案二:填写渠道识别码统计(邀请码/渠道码)由于苹果统计数量少时无法展示,以及数据延时性等特性,实际应用中并不适合用来统计地推、邀请有奖等 App 推广场景。 于是在业务流程上,传统做法是让用户填写渠道码来实现业绩统计,比如“老带新”活动中的填写邀请码流程、地推活动中的填写地推码流程等,其本质就是通过获取某个用户填写的专属渠道识别码,来判断用户由哪个渠道邀请来,从而统计推广业绩并发放奖励。 但这种做法会使实际推广中多出一个人工填写的操作流程,高门槛必定导致高流失,用户会产生排斥心理,推广效果也就大打折扣。 方案三:采用第三方SDK追踪以 openinstall 为例,这也是基于渠道链接统计的一种方法,与 iTunes Connect 营销活动链接统计的区别在于: 统计数据能实时反馈并显示;没有数据数量限制,无论采集的样本量多少都能实时显示;能够程序化生成海量专属渠道链接,无需人工定义渠道识别信息;可以同时统计 Android、iOS(包括企业签名) App,数据更有具统一性。同时,由于渠道链接统计的方式具有更高的灵活性,采用 openinstall 可以在不用制作渠道包、填写邀请码的情况下,识别渠道安装来源。这也意味着,开发者甚至能在业务流程上实现免填邀请码、免填地推码统计渠道业绩的需求。 另一方面,在实际应用中,由于能够程序化生成海量渠道链接的特点,可以有效解决 iOS 多渠道统计的难题,主要应用于移动广告效果统计、社交分享效果统计、iOS(包括企业签名)引流与统计、邀请层级关系的建立等方面。 总结:毋庸置疑,苹果官方统计工具在 iOS 领域必然是最优的统计方案,但客观存在的一些弊端在实际应用中也是不可避免的,可以考虑用第三方 openinstall 做这方面的补充。 ...

July 9, 2019 · 1 min · jiezi

qiniuLive-连麦流程介绍

本文出自APICloud官方论坛 qiniuLive 封装了七牛直播云服务平台的移动端开放 SDK。该模块包括视频流采集和视频流播放两部分 iOS连麦流程图:Android连麦流程图:以下部分代码,仅供参考。 <!DOCTYPE HTML><html><head> <meta charset="utf-8"> <meta name="viewport" content="maximum-scale=1.0, minimum-scale=1.0, user-scalable=0, initial-scale=1.0, width=device-width" /> <meta name="format-detection" content="telephone=no, email=no, date=no, address=no"> <title>Hello APP</title> <link rel="stylesheet" type="text/css" href="../css/api.css" /> <style> body { padding-top: 760px; } button { margin: 10px; }</style></head><body> <p>主播操作</p> <button type="button" onclick="fnsetStreamingProfile('ddghh')" name="button">initStreamingEnv--setStreamingProfile</button> <button type="button" onclick="fnstartStream()" name="button">startStream</button> <button type="button" onclick="fnconfigConference(320)" name="button">configConference</button> <button type="button" onclick="fnstartConference('ddghh')" name="button">startConference</button> <p>副主播操作</p> <button type="button" onclick="fnsetStreamingProfile('vbmuy')" name="button">initStreamingEnv--setStreamingProfile</button> <button type="button" onclick="fnstartStream()" name="button">startStream</button> <button type="button" onclick="fnconfigConference(320)" name="button">configConference</button> <button type="button" onclick="fnstartConference('vbmuy')" name="button">startConference</button> <p>播放</p> <button type="button" onclick="fninitPMediaPlayer()" name="button">fninitPMediaPlayer</button> <button type="button" onclick="fnstart()" name="button">fnstart</button></body><script type="text/javascript" src="../script/api.js"></script><script type="text/javascript"> var room_name, user_id, roomToken; var qiniuLive; apiready = function() { qiniuLive = api.require('qiniuLive'); qiniuLive.initStreamingEnv(function(ret) { if (ret.status) { console.log('initStreamingEnv成功' + JSON.stringify(ret)); qiniuLive.addRtcStateDidChangeListener(function(ret) { console.log('addRtcStateDidChangeListener---' + JSON.stringify(ret)); if (ret.state == 'inited') { if (api.systemType == "ios") { fnconfigConference(); //fnstartConference(); } } }); qiniuLive.addEventListener({ //流状态已变更事件 name: 'streamStatus' }, function(ret) { console.log('streamStatus---' + JSON.stringify(ret)); if (ret.streamStatus == 17) { //Android适用 fnconfigConference(); } if (ret.streamStatus == 8) { } }); qiniuLive.addRoomOIListener(function(ret) { console.log('主播进出房间' + JSON.stringify(ret)); if (ret.eventType == "didJoin") { alert(ret.userID + "进入房间") } }); qiniuLive.addRtcDidFailListener(function(ret) { console.log('错误回调addRtcDidFailListener的监听' + JSON.stringify(ret)); }); } }); };// 配置直播流参数,初始化推流预览区域 ...

July 9, 2019 · 3 min · jiezi

iOSer-必知必会的深度链接技术WWDC2019更新

iOSer作为移动开发者中的一员,不得不说深度链接在当下这个“流量”时代已经成为我们的必修课了,那么什么是深度链接呢?简单的说就是,可以通过一个简单的“链接”,打开App并直接进入该App中的内容页。前提是该手机上已安装该App,且该App需要支持深度链接。例如:在Safari里看到的澎湃新闻App的某一篇新闻 “中国又一新的世界遗产...” 点击下面滚动Banner上的“打开App”按钮便可直接进入澎湃新闻App(已安装)中对应的新闻页面:可见在移动端采用深度链接技术,极大的省去了用户打开App、再搜索内容或者点击某处进入指定页面等繁琐的操作,直接点击网页上的打开按钮即可一键到达App内的指定页面。 一、iOS上深度链接的由来在介绍深度链接是怎么来的之前,有一个基础概念需要和大家同步一下: SandBox(沙盒)SandBox(沙盒)是苹果官方规定的iOS系统强制应用程序只能够读取应用程序内部数据,不可以访问其他应用信息数据的一种机制。 在iOS系统的设备中每一个App都有自己的储存空间;App只能访问自己沙盒目录下的内容,不能访问其它存储空间的内容;应用程序的数据请求需要经过权限检测,检测不通过则不执行; 为什么使用沙盒?SandBox(沙盒)是安全体系中的一种机制,从而苹果公司在设计iOS系统时,考虑到应用之间的信息安全,对应用程序的访问权限设置了限制。 SandBox(沙盒)的弊端使用沙盒机制后App之间不能相互访问进行通信,从而使得App成为一个个的信息孤岛。(弱小可怜又无助) 如何解决SandBox(沙盒)问题?不能说苹果的初衷怎么样,但是带来的问题是显而易见的,那么其实苹果早在2010年 iOS 4 的时候就已经意识到App信息孤立的问题了,所以推出了 URL Scheme 技术,此技术使得iOS系统可以通过特定的URL方式传递参数给另外一个App。例如:iOSer://userid=123456&name=sands 。 不得不承认,这个技术确实解决了当初比较棘手的问题,但是在日新月异的今天,URL Scheme的诟病也日渐显著,比如想要实现两个App之间的跳转则需要兼并开发,再比如URL Scheme能够打开App的前提是已经安装了App,如果没有安装则一定会报错,相信下面的错误大家一定都见的不少,更重要的是现在越来越多的浏览器已经不再支持URL Scheme了,这必然让我们不得不另辟蹊径。 相信苹果也是深刻的意识到了URL Scheme已经不再是长久之计了,所以苹果在2015年 iOS 9 中隆重推出了 Universal Links(通用链接) 。 二、深度链接解决的问题Universal Links(通用连接) 一种能够通过点击传统 HTTPS链接 来 启动App 或者 打开对应网站 的技术。 通过唯一的网址, 不需要特别的URI Scheme就可以链接一个特定App里面的视图 。比如:一个App分享内容到微信,用户在微信内置浏览器中看到H5页面内容,然后用户点击触发Universal Links链接后,即可直接打开App内相同的页面内容。(PS: 由于微信 6.5 版本之后做了 屏蔽操作 ,导致无法直接打开App了,但这并不影响系统引导。) NOTE Universal links let users open your app when they tap links to your website within WKWebView and UIWebView views and Safari pages, in addition to links that result in a call to openURL:, such as those that occur in Mail, Messages, and other apps. ...

July 9, 2019 · 4 min · jiezi

Flutter-自定义组件之贝塞尔曲线画波浪球

百度百科: 贝塞尔曲线(Bézier curve),又称贝兹曲线或贝济埃曲线,是应用于二维图形应用程序的数学曲线。一般的矢量图形软件通过它来精确画出曲线,贝兹曲线由线段与节点组成,节点是可拖动的支点,线段像可伸缩的皮筋,我们在绘图工具上看到的钢笔工具就是来做这种矢量曲线的。贝塞尔曲线是计算机图形学中相当重要的参数曲线,在一些比较成熟的位图软件中也有贝塞尔曲线工具,如PhotoShop等。在Flash4中还没有完整的曲线工具,而在Flash5里面已经提供出贝塞尔曲线工具。源码地址设计图效果--设计图地址 开发效果图 贝塞尔曲线画圆 如图当画圆时系数M约等于0.55228475,绘制时调用cubicTo(p1.x,p1.y,p2.x,p2.y,p3.x,p3.y)进行绘制,绘制时以圆心为圆点,x轴、y轴为线划分成4分,进行绘制。 画路径代码void _canvasBesselPath(Path path) { Point p1 = Point(x: radius*2,y: radius); Point p2 = Point(x: radius,y: radius*2); Point p3 = Point(x: 0,y: radius); Point p4 = Point(x: radius,y: 0); if (isToRight) { if (percent <= 0.2) { p1.x = radius*2 + radius*percent/0.2; } else if (percent <= 0.4) { p4.x = p2.x = radius + radius*(percent-0.2)/0.2; p1.x = p2.x + radius*2; } else if (percent <= 0.6) { p4.x = p2.x = radius*2 ; p1.x = radius*4 - radius*(percent - 0.4)/0.2; } else if (percent <= 0.8) { p4.x = p2.x = radius*2 - radius*(percent - 0.6)/0.2; p1.x = p2.x+radius; } else if (percent <= 0.9) { p3.x = radius*(percent - 0.8)/0.3; p4.x = p2.x = radius; p1.x = radius*2; } else if (percent <= 1.0) { p3.x = radius*(1 - percent)/0.3; p4.x = p2.x = radius; p1.x = radius*2; } } else { if (percent <= 0.2) { p3.x = - radius*percent/0.2; } else if (percent <= 0.4) { p3.x = -radius - radius*(percent-0.2)/0.2; p4.x = p2.x = p3.x + 2*radius; } else if (percent <= 0.6) { p3.x = radius*(percent - 0.4)/0.2 - radius*2; p4.x = p2.x = 0; } else if (percent <= 0.8) { p3.x = -radius+radius*(percent - 0.6)/0.2; p4.x = p2.x = p3.x + radius; p1.x = p2.x + radius*2 - radius*(percent - 0.6)/0.2; } else if (percent <= 0.9) { p1.x = radius*2 - radius*(percent - 0.8)/0.3; } else if (percent <= 1.0) { p1.x = radius*2 - radius*(1 - percent)/0.3; } } final p1Radius = p2.y - p1.y; final p24LeftRadius = p2.x - p3.x; final p24RightRadius = p1.x - p2.x; final p3Radius = p2.y - p3.y; path.moveTo(p1.x, p1.y); path.cubicTo( p1.x, p1.y + p1Radius*M, p2.x + p24RightRadius*M, p2.y, p2.x, p2.y ); path.cubicTo( p2.x - p24LeftRadius*M, p2.y, p3.x, p3.y + p3Radius*M, p3.x, p3.y ); path.cubicTo( p3.x, p3.y - p3Radius*M, p4.x - p24LeftRadius*M, p4.y, p4.x, p4.y ); path.cubicTo( p4.x + p24RightRadius*M, p4.y, p1.x , p1.y - p1Radius*M, p1.x, p1.y ); }分享一个贝塞尔曲线在线演示网站源码地址分享一个Flutter项目后续在UI中国上看到了一个不错的设计,里面也涉及贝塞尔曲线全手势操作灯的demo,这里的贝塞尔曲线p2、p4的Y轴向中间做一个伸缩就可以。 ...

July 5, 2019 · 2 min · jiezi

Babelvscode实现APICloud开发中兼容ES6及以上代码

本文出自APICloud官方论坛,感谢论坛版主 penghuoyan 的分享。 使用APICloud开发时,考虑到兼容问题一直使用ES5开发,时间越久感觉越落后,整理了一个兼容ES6的开发环境,供大家参考。步骤1:安装Node开发环境,不清楚的可以百度 步骤2:新建APICloud项目,结构目录如下 步骤3:在vscode中打开项目,如图: 步骤4:在项目中安装Bable项目的npm包 npm init –y  生成package.json文件  npm install --save-dev @babel/core@babel/cli @babel/preset-envnpm install --save @babel/polyfill步骤5:在package.json文件中添加babel进行配置"scripts": {    "build": "babelscript -d parsedScript --presets=@babel/env -watch"  }, 
步骤6:运行脚本:npm init build编辑script文件夹下的ES6代码并保存时,将会自动把代码编译为ES5代码并保持在parsedScript文件夹下面(保存时文件名称、对应路径相同)注:项目使用时,使用编译过后的js代码(parsedScript),已保证兼容性问题 步骤7:Wifi真机同步设置,忽略node_modules文件夹1.  在项目根目录新建.syncignore文件 文件内容:{/*.js.map,/node_modules/,src/}    //真机同步时忽略node_modules 步骤8:提交代码时忽略node_modules文件夹(APICloud2.0开发工具)在项目根目录中新建.gitignore文件,文件内容:node_modules/

July 5, 2019 · 1 min · jiezi