关于swift:self-与-Self

简略说说 Swift 中的 self 和 Self。总结下就是:self 是在实例办法或闭包中援用以后实例的关键字。Self 是在协定中援用遵循该协定的类型自身的关键字。 self:在一个实例办法或闭包中,self 是一个非凡的关键字,用于援用以后实例。它指代以后实例对象,能够用于拜访和操作实例的属性、办法和下标等成员。 class MyClass { var name: String init(name: String) { self.name = name } func printName() { print("My name is \(self.name)") }}let obj = MyClass(name: "John")obj.printName() // 输入 "My name is John"在上述示例中,self.name 援用了类中的属性 name,而 self 代表以后实例本身。 Self:在协定中,Self 是一个非凡的关键字,用于援用遵循该协定的类型自身。它以大写字母结尾,示意类型自身,而不是特定的实例。 protocol Printable { static func printSomething()}class MyClass: Printable { static func printSomething() { print("Printing something") }}MyClass.printSomething() // 输入 "Printing something"在上述示例中,Self 用作协定 Printable 中的关联类型,它示意遵循该协定的理论类型(例如 MyClass)。在协定中,Self 示意理论实现该协定的类型。 ...

September 22, 2023 · 1 min · jiezi

关于swift:百度工程师移动开发避坑指南Swift语言篇

作者 | 启明星小组上一篇咱们介绍了挪动开发常见的内存透露问题,见《百度工程师挪动开发避坑指南——内存透露篇》。本篇咱们将介绍Swift语言局部常见问题。 对于Swift开发者,Swift较于OC一个很大的不同就是引入了可选类型(Optional),刚接触Swift的开发者很容易在相干代码上踩坑。 本期咱们带来与Swift可选类型相干的几个避坑指南:可选类型要判空;防止应用隐式解包可选类型;正当应用Objective-C标识符;审慎应用强制类型转换。心愿能对Swift开发者有所帮忙。 一、可选类型(Optional)要判空在Objective-C中,能够应用nil来示意对象为空,然而应用一个为nil的对象通常是不平安的,如果应用不慎会呈现解体或者其它异样问题。在Swift中,开发者能够应用可选类型示意变量有值或者没有值,能够更加清晰的表白类型是否能够平安的应用。如果一个变量可能为空,那么在申明时能够应用?来示意,应用前须要进行解包。例如: var optionalString: String?在应用可选类型对象时,须要进行解包操作,有两种解包形式:强制解包与可选绑定。 强制解包应用 ! 润饰一个可选对象 ,相当于通知编译器『我晓得这是一个可选类型,但在这里我能够保障他不为空,编译时请疏忽此处的可空校验』,例如: let unwrappedString: String = optionalString! // 运行时报错:Thread 1: Fatal error: Unexpectedly found nil while unwrapping an Optional value这里应用 ! 进行了强制解包,如果optionalString为nil,将会产生运行时谬误,产生解体。因而,在应用 ! 进行强制解包时,必须保障变量不为nil,要对变量进行判空解决,如下: if optionalString != nil { let unwrappedString = optionalString!}相较于强制解包的不安全性,一般而言举荐另一种解包形式,即可选绑定。例如: if let optionalString = optionalString { // 这里optionalString不为nil,是曾经解包后的类型,能够间接应用}综上,在对可选类型进行解包时应尽量避免应用强制解包,采纳可选绑定代替。如果肯定要应用强制解包,那么必须在逻辑上齐全保障类型不为空,并且做好正文工作,以减少后续代码的可维护性。 二、防止应用隐式解包可选类型(Implicitly Unwrapped Optionals)因为可选类型每次应用之前都须要进行显式解包操作,有时变量在第一次赋值之后,就会始终有值,如果每次应用都显式解包,显得繁琐,Swift引入了隐式解包可选类型,隐式解包可选类型能够应用 ! 来示意,并且应用时不须要显式解包,能够间接应用,例如: var implicitlyUnwrappedOptionalString: String! = "implicitlyUnwrappedOptionalString"var implicitlyString: String = implicitlyUnwrappedOptionalString上述例子的隐式解包,在编译和运行过程中都不会产生问题,但如果在两行代码两头插入一行 implicitlyUnwrappedOptionalString = nil将会产生运行时谬误,产生解体。 ...

May 24, 2023 · 2 min · jiezi

关于swift:万字长文详解如何使用Swift提高代码质量-京东云技术团队

前言京喜APP最早在2019年引入了Swift,应用Swift实现了第一个订单模块的开发。之后一年多咱们继续在团队/公司外部推广和遍及Swift,目前Swift曾经撑持了70%+以上的业务。通过应用Swift进步了团队内同学的开发效率,同时也带来了品质的晋升,目前来自Swift的Crash的占比不到1%。在这过程中一直的学习/实际,团队内的Code Review,也对如何应用Swift来进步代码品质有更深的了解。 Swift个性在探讨如何应用Swift进步代码品质之前,咱们先来看看Swift自身相比ObjC或其余编程语言有什么劣势。Swift有三个重要的个性别离是富裕表现力/安全性/疾速,接下来咱们别离从这三个个性简略介绍一下: 富裕表现力Swift提供更多的编程范式和个性反对,能够编写更少的代码,而且易于浏览和保护。 根底类型 - 元组、Enum关联类型办法 - 办法重载protocol - 不限度只反对class、协定默认实现、类专属协定泛型 - protocol关联类型、where实现类型束缚、泛型扩大可选值 - 可选值申明、可选链、隐式可选值属性 - let、lazy、计算属性`、willset/didset、Property Wrappers函数式编程 - 汇合filter/map/reduce办法,提供更多规范库办法并发 - async/await、actor规范库框架 - Combine响应式框架、SwiftUI申明式UI框架、CodableJSON模型转换Result builder - 形容实现DSL的能力动态性 - dynamicCallable、dynamicMemberLookup其余 - 扩大、subscript、操作符重写、嵌套类型、区间Swift Package Manager - 基于Swift的包管理工具,能够间接用Xcode进行治理更不便struct - 初始化办法主动补齐类型推断 - 通过编译器弱小的类型推断编写代码时能够缩小很多类型申明提醒:类型推断同时也会减少肯定的编译耗时,不过Swift团队也在一直的改善编译速度。安全性代码平安let属性 - 应用let申明常量防止被批改。值类型 - 值类型能够防止在办法调用等参数传递过程中状态被批改。访问控制 - 通过public和final限度模块外应用class不能被继承和重写。强制异样解决 - 办法须要抛出异样时,须要申明为throw办法。当调用可能会throw异样的办法,须要强制捕捉异样防止将异样裸露到下层。模式匹配 - 通过模式匹配检测switch中未解决的case。类型平安强制类型转换 - 禁止隐式类型转换防止转换中带来的异样问题。同时类型转换不会带来额定的运行时耗费。。提醒:编写ObjC代码时,咱们通常会在编码时增加类型查看防止运行时解体导致Crash。KeyPath - KeyPath相比应用字符串能够提供属性名和类型信息,能够利用编译器查看。泛型 - 提供泛型和协定关联类型,能够编写出类型平安的代码。相比Any能够更多利用编译时查看发现类型问题。Enum关联类型 - 通过给特定枚举指定类型防止应用Any。内存平安空平安 - 通过标识可选值防止空指针带来的异样问题ARC - 应用主动内存治理防止手动治理内存带来的各种内存问题强制初始化 - 变量应用前必须初始化内存独占拜访 - 通过编译器查看发现潜在的内存抵触问题线程平安值类型 - 更多应用值类型缩小在多线程中遇到的数据竞争问题async/await - 提供async函数使咱们能够用结构化的形式编写并发操作。防止基于闭包的异步形式带来的内存循环援用和无奈抛出异样的问题Actor - 提供Actor模型防止多线程开发中进行数据共享时产生的数据竞争问题,同时防止在应用锁时带来的死锁等问题疾速值类型 - 相比class不须要额定的堆内存调配/开释和更少的内存耗费办法动态派发 - 办法调用反对动态调用相比原有ObjC音讯转发调用性能更好编译器优化 - Swift的动态性能够使编译器做更多优化。例如Tree Shaking相干优化移除未应用的类型/办法等缩小二进制文件大小。应用动态派发/办法内联优化/泛型特化/写时复制等优化进步运行时性能提醒:ObjC音讯派发会导致编译器无奈进行移除无用办法/类的优化,编译器并不知道是否可能被用到。ARC优化 - 尽管和ObjC一样都是应用ARC,Swift通过编译器优化,能够进行更快的内存回收和更少的内存援用计数治理提醒: 相比ObjC,Swift外部不须要应用autorelease进行治理。代码质量指标 以上是一些常见的代码质量指标。咱们的指标是如何更好的应用Swift编写出合乎代码质量指标要求的代码。 提醒:本文不波及设计模式/架构,更多关注如何通过正当应用Swift个性做部分代码段的重构。一些不错的实际利用编译查看缩小应用Any/AnyObject因为Any/AnyObject短少明确的类型信息,编译器无奈进行类型查看,会带来一些问题: 编译器无奈查看类型是否正确保障类型平安代码中大量的as?转换类型的缺失导致编译器无奈做一些潜在的编译优化应用as?带来的问题 当应用Any/AnyObject时会频繁应用as?进行类型转换。这如同没什么问题因为应用as?并不会导致程序Crash。不过代码谬误至多应该分为两类,一类是程序自身的谬误通常会引发Crash,另外一种是业务逻辑谬误。应用as?只是防止了程序谬误Crash,然而并不能避免业务逻辑谬误。 func do(data: Any?) { guard let string = data as? String else { return } // }do(1)do("")以下面的例子为例,咱们进行了as?转换,当data为String时才会进行解决。然而当do办法内String类型产生了扭转函数,应用方并不知道已变更没有做相应的适配,这时候就会造成业务逻辑的谬误。 提醒:这类谬误通常更难发现,这也是咱们在一次实在bug场景遇到的。应用自定义类型代替Dictionary代码中大量Dictionary数据结构会升高代码可维护性,同时带来潜在的bug: key须要字符串硬编码,编译时无奈查看value没有类型限度。批改时类型无奈限度,读取时须要反复类型转换和解包操作无奈利用空平安个性,指定某个属性必须有值提醒:自定义类型还有个益处,例如JSON转自定义类型时会进行类型/nil/属性名查看,能够防止将谬误数据丢到下一层。不举荐 let dic: [String: Any]let num = dic["value"] as? Intdic["name"] = "name"举荐 ...

May 10, 2023 · 7 min · jiezi

关于swift:iOS-预编译指令

iOS 预编译指令是否是模拟器#if targetEnvironment(simulator) print("模拟器")#else print("真机")#endif是否是DEBUG模式#if DEBUG print("DEBUG")#else print("其余")#endif

May 6, 2023 · 1 min · jiezi

关于swift:iOS代码覆盖率一全量覆盖率自动化实践

作者:京东批发 邓立兵 简介这是一个统计基于 Swift & Objective-C 工程的代码覆盖率的自动化脚本。之所以做成 Pod ,是便于更好的复用,该 Pod 只蕴含了收集生成代码覆盖率的脚本。整体比较简单不便。 这里只将流程,咱不讲原理。后续另外介绍 这里只将流程,咱不讲原理。后续另外介绍 应用1、装置:通过 CocoaPods 进行装置,在你的 Podfile 文件增加如下代码: pod 'HDCoverage' 复制代码 而后 pod install 装置下载相干脚本文件。 2、关联脚本:在我的项目的 Xcode 的 Build Phases 增加新的脚本(New Run Script Phase)(App在Build会执行该脚本): "${PODS\_ROOT}/HDCoverage/HDCoverage/hd\_coverage_env.sh" 复制代码 3、工程配置代码覆盖率参数:这里原本是在 HDCoverage 有脚本反对的,然而基于对哪些模块(Pod作为独立模版)进行代码覆盖率,所以倡议在 Podfile 自主增加如下代码灵便治理,具体阐明如下: # 实现post_install Hooks# 须要收集Code Coverage的模块ntargets = Array['AFNetworking']require 'xcodeproj'post_install do |installer| # 批改Pods中某一个模块的配置文件,好采集代码覆盖率,须要源码! installer.pods_project.targets.each do |target| target.build_configurations.each do |config| if(config.name <=> 'Release') == 0 config.build_settings['OTHER_CFLAGS'] = '$(inherited)' config.build_settings['OTHER_SWIFT_FLAGS'] = '$(inherited)' config.build_settings['OTHER_LDFLAGS'] = '$(inherited)' ntargets.each do |ntarget| if(ntarget <=> target.name) == 0 config.build_settings['OTHER_CFLAGS'] = '$(inherited) -fprofile-instr-generate -fcoverage-mapping' config.build_settings['OTHER_SWIFT_FLAGS'] = '$(inherited) -profile-generate -profile-coverage-mapping' config.build_settings['OTHER_LDFLAGS'] = '$(inherited) -fprofile-instr-generate' break end end else config.build_settings['OTHER_CFLAGS'] = '$(inherited)' config.build_settings['OTHER_SWIFT_FLAGS'] = '$(inherited)' config.build_settings['OTHER_LDFLAGS'] = '$(inherited)' end end end # 批改主工程 project_path = './HDCoverage.xcodeproj' project = Xcodeproj::Project.open(project_path) puts project project.targets.each do |target| if(target.name <=> 'HDCoverageDemo') == 0 target.build_configurations.each do |config| if ((config.name <=> 'Release') == 0 || (config.name <=> 'Debug') == 0) # 设置预编译变量CODECOVERAGE config.build_settings['GCC_PREPROCESSOR_DEFINITIONS'] = '$(inherited) CODECOVERAGE=1' # OC代码覆盖率插桩配置 config.build_settings['OTHER_CFLAGS'] = '$(inherited) -fprofile-instr-generate -fcoverage-mapping' # Swift代码覆盖率插桩配置 config.build_settings['OTHER_SWIFT_FLAGS'] = '$(inherited) -profile-generate -profile-coverage-mapping' # 采集代码覆盖率配置 config.build_settings['OTHER_LDFLAGS'] = '$(inherited) -fprofile-instr-generate' # Release须要设置,不然无奈解析代码覆盖率 config.build_settings['SWIFT_OPTIMIZATION_LEVEL'] = '-Onone' else config.build_settings['OTHER_CFLAGS'] = '$(inherited)' config.build_settings['OTHER_SWIFT_FLAGS'] = '$(inherited)' config.build_settings['OTHER_LDFLAGS'] = '$(inherited)' config.build_settings['SWIFT_OPTIMIZATION_LEVEL'] = '' end end end end project.save()end4、代码执行数据收集:应用 GCC 无奈满足 同时兼容 Swift和 Objective-C ,所以这里是基于 LLVM 进行,官网文档 。也能够参考笔者翻译的 Source-based Code Coverage ,残缺具体的教程能够看 Source-based Code Coverage for Swift Step by Step。 ...

April 4, 2023 · 2 min · jiezi

关于swift:Swift之struct二进制大小分析

作者:京东批发 邓立兵 随着Swift的日渐成熟和给开发过程带来的便利性及安全性,京喜App中的原生业务模块和根底模块应用Swift开发占比逐步增高。本次探讨的是struct比照Class的一些优劣势,重点剖析对包体积带来的影响及躲避措施。一、基础知识1、类型比照 援用类型:将一个对象赋值给另一个对象时,零碎不会对此对象进行拷贝,而会将指向这个对象的指针赋值给另一个对象,当批改其中一个对象的值时,另一个对象的值会随之扭转。【Class】 值类型:将一个对象赋值给另一个对象时,会对此对象进行拷贝,复制出一份正本给另一个对象,在批改其中一个对象的值时,不影响另外一个对象。【structs、Tuples、enums】。Swift中的【Array, String, and Dictionary】 两者的区别能够查阅Apple官网文档 2、Swift中struct和Class区别1、class是援用类型、struct是值类型2、类容许被继承,构造体不容许被继承3、类中的每一个成员变量都必须被初始化,否则编译器会报错,而构造体不须要,编译器会主动帮咱们生成init函数,给变量赋一个默认值4、当你须要继承Objective-C某些类的的时候应用class5、class申明的办法批改属性不须要`mutating`关键字;struct须要6、如果须要保证数据的唯一性,或者保障在多线程数据安全,能够应用struct;而心愿创立共享的、可变的状态应用class以上三点能够参考深刻了解Swift中的Class和Struct进行更多细节的浏览学习 二、struct优选孔子曰:择其善者而从之,其不善者而改之。1、安全性 应用struct是值类型,在传递值的时候它会进行值的copy,所以在多线程是平安的。无论你从哪个线程去拜访你的 Struct ,都非常简单。2、效率性 struct存储在stack中(这比malloc/free调用的性能要高得多),class存储在heap中,struct更快。3、内存泄露 没有援用计数器,所以不会因为循环援用导致内存透露基于这些因素,在日常开发中,咱们能用struct的咱们尽量应用struct。 三、struct的不完满孟子曰:鱼,我所欲也,熊掌亦我所欲也;二者不可得兼。“熊掌” 再好,吃多了也难以消化。特地在中大型项目中,如果没有节制的应用struct,可能会带来意想不到的问题。 1、内存问题值类型有哪些问题?比方在两个struct赋值操作时,可能会发现如下问题: 1、内存中可能存在两个微小的数组;2、两个数组数据是一样的;3、反复的复制。 解决方案:COW(copy-on-write) 机制 1、Copy-on-Write 是一种用来优化占用内存大的值类型的拷贝操作的机制。2、对于Int,Double,String 等根本类型的值类型,它们在赋值的时候就会产生拷贝。(内存减少)3、对于 Array、Dictionary、Set 类型,当它们赋值的时候不会产生拷贝,只有在批改的之后才会产生拷贝。(内存按需延时减少)4、对于自定义的数据类型不会主动实现COW,可按需实现。那么自定义的数据如何实现COW呢,能够参考官网代码: /*咱们应用class,这是一个援用类型,因为当咱们将援用类型调配给另一个时,两个变量将共享同一个实例,而不是像值类型一样复制它。*/final class Ref { var val : T init(_ v : T) {val = v}}/*创立一个struct包装Ref:因为struct是一个值类型,当咱们将它调配给另一个变量时,它的值被复制,而属性ref的实例仍由两个正本共享,因为它是一个援用类型。而后,咱们第一次更改两个Box变量的值时,咱们创立了一个新的ref实例,这要归功于:isUniquelyReferencedNonObjC这样,两个Box变量不再共享雷同的ref实例。*/struct Box { var ref : Ref init(_ x : T) { ref = Ref(x) } var value: T { get { return ref.val } set { // isKnownUniquelyReferenced 函数来查看某个引 用只有一个持有者 // 如果你将一个 Swift 类的实例传递给这个函数,并且没有其余变量强援用 这个对象的话,函数将返回 true。如果还有其余的强援用,则返回 false。不过,对于 Objective-C 的类,它会间接返回 false。 if (!isUniquelyReferencedNonObjC(&ref)) { ref = Ref(newValue) return } ref.val = newValue } }}// This code was an example taken from the swift repo doc file OptimizationTips // Link: https://github.com/apple/swift/blob/master/docs/OptimizationTips.rst#advice-use-copy-on-write-semantics-for-large-values实例阐明:咱们想在一个应用struct类型的User中应用copy-on-write的: ...

March 30, 2023 · 4 min · jiezi

关于swift:云音乐-Swift-混编-Module-化实践

图片来自:https://unsplash.com 本文作者:冰川背景云音乐 iOS App 经验多年的迭代,积攒了大量的 Objective-C(以下简称 OC) 代码,目前曾经实现主工程壳化,各层组件关系如下: 组件化后混编的场景次要集中在 Framework 内混编和 Framework 之间混编,Framework 内的混编老本较低,重头次要在 Framework 间的混编。 在云音乐中集成的翻新业务,因为依赖的历史根底库较少,曾经投入使用 Swift。主站业务迟迟没有投入,次要起因是波及到大量的 OC 业务根底库和公共根底库不反对 Swift 混编,OC 组件库参加混编的前提是要实现 Module 化。 以上是咱们实现混编打算的几个阶段,本文次要介绍在反对云音乐 Swift 混编过程中,Module 化阶段的剖析与实际。 什么是 Modules早在 2012 苹果就提出了 Modules 的概念(比 Swift 公布还要早),Module 是组件的形象形容,蕴含组件接口以及实现。它的外围目标是为了解决 C 系语言的扩展性和稳定性问题。 Cocoa 框架很早就反对了 Module,并且前向兼容,正因为它的兼容性,纯 Objective-C 开发对它的感知可能不强。 AFramework.framework├─ Headers├─ Info.plist├─ Modules│ └─ module.modulemap└─ AFrameworkModule 化的 OC 二进制 Framework 组件,在 Modules 目录下存在一个 .modulemap 格局的文件,它形容了组件对外裸露的能力。当援用的组件蕴含 modulemap,Clang 编译器会从中查找头文件,进行 Module 编译,并将编译后果缓存。 Clang 编译器要求 Swift 援用的 Objective-C 组件必须反对 Module 个性。咱们把 OC 组件反对 Module 的过程,称为 Module 化。 ...

March 6, 2023 · 3 min · jiezi

关于swift:SwiftUI模块0001-SwiftUI自定义Tabbar动画效果

SwiftUI模块系列 - 已更新23篇SwiftUI我的项目 - 已更新2个我的项目往期Demo源码下载效果图 - 自定tabbar动画成果 思路应用Table先创立一个动态的tabbar在自定义tabbar插入一个黄色的圆圈 设置偏移量是tabbar的按钮图标x的10偏移量左右监听按钮点击去触发去偏移黄色圆圈的y值即可代码ContentView.swiftstruct ContentView: View { // MARK: Hiding Native One // 暗藏Native One init(){ UITabBar.appearance().isHidden = true } @State var currentTab : Tab = .home var body: some View { VStack(spacing:0){ TabView(selection:$currentTab) { // MARK: Need to Apply BG For Each Tab View // 须要为每个标签视图利用BG Text("Home") .ApplyBG() .tag(Tab.home) Text("Label") .ApplyBG() .tag(Tab.label) Text("Position") .ApplyBG() .tag(Tab.position) Text("Found") .ApplyBG() .tag(Tab.found) Text("My") .ApplyBG() .tag(Tab.my) } CustomTabbar(currentTab: $currentTab) } }}struct ContentView_Previews: PreviewProvider { static var previews: some View { ContentView() }}extension View{ func ApplyBG() -> some View { // infinity 无穷的 self .frame(maxWidth:.infinity,maxHeight: .infinity) .background{ Color("BG") .ignoresSafeArea() } }}CustomTabbar.swiftstruct CustomTabbar: View { @Binding var currentTab : Tab // MARK : To Animate Like Curve // 动画曲线 @State var yOffset : CGFloat = 0 var body: some View { GeometryReader{ proxy in // 获取整体的宽度 let width = proxy.size.width HStack(spacing:0) { ForEach(Tab.allCases,id:\.rawValue){tab in Button { withAnimation(.easeInOut(duration: 0.2)) { currentTab = tab yOffset = -60 } withAnimation(.easeInOut(duration: 0.1).delay(0.07)){ yOffset = 0 } } label: { Image(tab.rawValue) .renderingMode(.template) .resizable() .aspectRatio(contentMode: .fit) .frame(width:30,height: 30) .frame(maxWidth:.infinity) .foregroundColor(currentTab == tab ? Color("Purple"):.gray) // MARK : Little Scaling Effect // 图标一个缩放动画成果 .scaleEffect(currentTab == tab && yOffset != 0 ? 1.5 : 1) } } } .frame(maxWidth:.infinity) .background(alignment:.leading){ Circle() .fill(Color("Yellow")) .frame(width:25,height:25) .offset(x:10,y:yOffset) .offset(x:indicatorOffset(witdh: width)) } } .frame(height:30) .padding(.bottom,10) .padding([.horizontal,.top]) } // MARK: Indicator Offset // 指示器偏移 func indicatorOffset(witdh : CGFloat) ->CGFloat { let index = CGFloat(getIndex()) if index == 0 {return 0} let buttonWidth = witdh / CGFloat(Tab.allCases.count) return index * buttonWidth } func getIndex() -> Int { switch currentTab { case .home: return 0 case .label: return 1 case .position: return 2 case .found: return 3 case .my: return 4 } }}struct CustomTabbar_Previews: PreviewProvider { static var previews: some View { ContentView() }}Tab.swiftimport SwiftUI// Mark : Enum For Tabs with Rawvlaue as Asset Image// 标记:Enum选项卡与Rawvlaue作为资产图像enum Tab : String ,CaseIterable { case home = "Home" case label = "Label" case position = "Position" case found = "Found" case my = "My"}demo源码如需看源码,请点击下载! ...

September 4, 2022 · 2 min · jiezi

关于swift:wallysIPQ8074A2x4×4-or-8×8-11AX-MUMIMO-DUAL-CONCURRENT

2x(4×4 or 8×8) 11AX MU-MIMO DUAL CONCURRENT EMBEDDEDBOARDFeatures IPQ8074A 4x4 2.4G 8x8 5G 802.11ax IPQ8074A 4x4 2.4G 8x8 5G 802.11ax DR8074A(HK01) https://www.wallystech.com/Ro... MT7915/MT 7975/IPQ6000/IPQ6018/IPQ6010/IPQ4019/IPQ4029/ipq4018/IPQ8072/IPQ8074/QCN9074/QCN9072/QCN9024/IPQ5018/BY:Wallys Communications (Suzhou ) Co., LTDEMAIL:sales3@wallystech.com Wallys Communications (SuZhou) Co., Ltd., http://www.wallystech.com,which is a professional supplier specializing in product design, manufacturing and offering superior OEM/ODM/JDM services in wireless communications. As a specialized manufacturer and exporter for these products in China,We sincerely hope to establish business relations with your esteemed corporation. We mainly develop high power wireless products based on Quacomm chip such asIPQ6000/IPQ6018/IPQ6010/IPQ4019/IPQ4029/IPQ8072/IPQ8074/QCN9074 and so on . Features Qualcomm Atheros IPQ8074A AR Quad Core CPUOn-board 5GHz radio, up to 4804Mbps physical data rate 32MB NOR Flash, 256MB NAND FlashOn-board 2.4GHz radio, up to 1147Mbps physical data rateSupport 11ax TX BeamformingSupport 11ac/ax MU-MIMO DL and ULSupport OFDMA DL and ULTri-band support with 5G SBS (4×4/5GHz + 4×4/5GHz) + 4×4/2.4GHzSupports Dynamic Frequency Selection (DFS)Applications ...

June 30, 2022 · 2 min · jiezi

关于swift:Swift-57-中的-any-和-some-译

因为 any 和 some 都实用于协定,因而我想在这篇博文中将它们放在一起比拟以便更好地解释它们解决别离解决了什么问题,以及在什么状况下应用 any、some 或其余的。 理解 any 和 some 解决的问题为了解释 any 解决的问题,咱们能够通过一个列子来理解这两个关键字。上面是一个Pizza模型的协定: protocol Pizza { var size: Int { get } var name: String { get }}在Swift 5.6,你可能会写上面的这种办法,来接管一个Pizza func receivePizza(_ pizza: Pizza) { print("Omnomnom, that's a nice \(pizza.name)")}当这个函数被调用时,receivePizza 函数接管一个所谓的披萨协定类型,咱们能够了解为一个披萨盒子。为了晓得这个披萨名称,必须关上这个盒子,也就是获取实现Pizza协定的具体对象,而后获取名称。这意味着 Pizza 简直没有编译时优化,这使得 receivePizza 办法调用的开销比咱们想要的更大。 另外上面的函数,看起来如同是一样的 func receivePizza<T: Pizza>(_ pizza: T) { print("Omnomnom, that's a nice \(pizza.name)")}不过,这里有一个很次要区别。 Pizza 协定在这里没有用作参数类型。它被用作泛型 T 的束缚。这就使得编译器将可能在编译时解析 T 的类型,使得 receivePizza 承受到的是一个具体化的类型。 因为下面这两种办法差别并不是很分明,所以 Swift 团队引入了 any 关键字。此关键字不增加任何新性能。它迫使咱们分明地传播“这是一种存在主义”:(有点拗口,也不是很好了解,我就把他了解成这么类型的一个货色) ...

June 27, 2022 · 2 min · jiezi

关于swift:Swift-Learning-Summary-Access-Control

Access ControlAccess control restrict access to part of the code form code in other source files and modules. It enable us to hide the implement detail of the code, and to specify a preferred interface which that code can be accessed and used. Set access levels to individual types (classes, structures, enumerations), as well as to properties, methods, initializers, and subscripts belonging to those types. Protocols can be restricted to a certain context, as can global constants, variable, and functions. ...

June 18, 2022 · 10 min · jiezi

关于swift:swift-符号命名规则name-demangle

swift符号命名规定通过穷举法,得出以下规定 swiftc -target arm64-apple-ios8.0 -framework UIKit -emit-sil -sdk \ /Applications/Xcode.app/Contents/Developer/Platforms/iPhoneOS.platform/Developer/SDKs/iPhoneOS.sdk/ WXTestCell.swift

June 16, 2022 · 1 min · jiezi

关于swift:swiftc-demangle符号清洗

Swift 符号荡涤swift在crash堆栈里的信息是蛮奇怪的,比如说这种:_TtC16MTSS_SDKTestDemo13WXCrashTestVC其实他这是通过Name demangle名字命名技术之后,让人看起来诡异,如何荡涤成失常的符号呢: xcrun swift-demangle __TFV5hello4Rectg9subscriptFOS_9DirectionSi荡涤成人眼可看的符号了 说到底swift的Name demangle是抄的c++的Name demangle技术,swift的符号命名规定详见swift符号规定 oc-Swift混编 oc调用swift类的app名-Swift.h文件,这里会寄存着swift本义成oc的类的列表,如果在oc里须要通过runtime调用swift类,则须要调用本义之后的swift类名,如下图中的_TtC16MTSS_SDKTestDemo13WXCrashTestVC如何寻找编译之后的app名-Swift.h文件,找到编译日志里的swiftFileList地位就行了,和这个放在同一级目录下, C++符号荡涤c++filt -n _ZN5physx2Gu24PCMMeshContactGenerationC2ERKNS_6shdfnd3aos6FloatVES6_RKNS3_12PsTransformVES9_RNS0_33MultiplePersistentContactManifoldERNS0_13ContactBufferE一般来说c++filt命令要加-n参数,能力失常解析 参考文章:c++符号荡涤

June 14, 2022 · 1 min · jiezi

关于swift:Swift-Learning-Summary-Memory-Safety

Memory SafetyMost of the time we don’t have to think about accessing memory, but it’s important to understand where potential conflicts can occur, so we can avoid writing code that has conflicting access to memory. Here we are talking about the situation that happen on a single thread. Memory Accessvar one = 1 // write access to the memory one is stored.print("\(one)") // read access from the memory one is stored.The conflicting access to memory can occur when different part of the code are trying to access the same location in memory at the same time. ...

June 12, 2022 · 5 min · jiezi

关于swift:Swift-Learning-Summary-Automatic-Reference-Counting

Automatic Reference CountingSwift use ARC to track and manage the app’s memory usage. ARC frees up the memory used by class instances when those instances are no longer needed. Reference counting applies only to instance of classes. Reference: strong: retain the obj. (default)weak: don’t retain the object referred to, track the object referred to.unowned: don’t retain the object referred to, don’t tract the object referred to.How ARC WorkAllocate a chunk of memory to store information about that instance ...

June 12, 2022 · 6 min · jiezi

关于swift:Swift-Learning-Summary-Opaque-Type

Opaque TypeA function with an opaque type hides its return value’s type information. Hiding type information at some boundaries between a module and code that calls into the module. Unlike returning a value whose type is a protocol type, opaque type preserve type identity —the compile has access to the type information, but clients of the module don’t. The SituationHere we have a Shape protocol. protocol Shape { func draw() -> String}The struct Triangle conform to the Shape. Describe how to draw(). ...

June 5, 2022 · 4 min · jiezi

关于swift:Swift-Learning-Summary-Generic

GenericsWrite code in a more abstract way. It make the code flexible and more reusable. Array and Dictionary types are both generic collections. The Problem That Generics SolveIf the code with no generics: func swapTwoInts(_ a: inout Int, _ b: inout Int) { let tmp = a a = b b = tmp}func swapTwoStrings(_ a: inout String, _ b: inout String) { let tmp = a a = b b = tmp}func swapTwoDoubles(_ a: inout Double, _ b: inout Double) { let tmp = a a = b b = tmp}// Use the functionvar aInt = 3, bInt = 4var aString = "Mike", bString = "Amy"var aDouble = 1.1, bDouble = 2.5swapTwoInts(&aInt, &bInt)swapTwoStrings(&aString, &bString)swapTwoDoubles(&aDouble, &bDouble)print(aInt, bInt)print(aString, bString)print(aDouble, bDouble)// Print:// 4 3// Amy Mike// 2.5 1.1Once the code use generics: ...

June 5, 2022 · 7 min · jiezi

关于swift:理解-Swift-中的-inlinable-译

@inlinable 属性是 Swift 鲜为人知的属性之一。与其余同类一样,它的目标是启用一组特定的微优化,您能够应用它们来进步应用程序的性能。让咱们来看看这个是如何工作的。 在 Swift 中应用 @inline 进行内联扩大兴许最须要留神的是,尽管 @inlinable 与代码内联无关,但它与咱们之前曾经介绍过的 @inline 属性不同。然而为了防止您不得不浏览两篇文章,咱们将在介绍 @inlinable 之前再次介绍这些概念。 在编程中,内联扩大,也称为内联,是一种编译器优化技术,它用所述办法的主体替换办法调用。 调用办法的操作很难做到没有性能开销。正如咱们在对于内存调配的文章中所述,当应用程序心愿将新的堆栈跟踪推送到线程时,会进行大量的编排来传输、存储和扭转应用程序的状态。尽管从方面来说,堆栈跟踪能够简化调试过程,但您可能想晓得是否有必要每次都这样做。如果一个办法太简略,调用它的开销可能不仅是不必要的,而且对应用程序的整体性能无害: func printPlusOne(_ num: Int) { print("My number: \(num + 1)")}print("I'm going to print some numbers!")printPlusOne(5)printPlusOne(6)printPlusOne(7)print("Done!")printPlusOne 这样的办法过于简略,无奈在应用程序的二进制文件中证实残缺的定义。只是为了清晰起见,咱们在代码中定义了它,但在公布这个应用程序时最好去掉它, 用残缺的实现替换所有调用它的中央,如下所示: print("I'm going to print some number!")print("My number: \(5 + 1)")print("My number: \(6 + 1)")print("My number: \(7 + 1)")print("Done!")这种缩小办法调用开销,可能会进步应用程序的性能,但可能会减少整体包大小,具体取决于被内联的办法的大小(能够了解为替换,被替换的办法中的代码多,天然代码量就多了)。这个过程是由 Swift 编译器主动实现的,依据你工程构建时的优化级别,水平是可变的。@inline 属性能够用来疏忽优化级别,强制编译器遵循特定的方向。内联也能够用于混同。 @inlinable 的目标是什么?大多数优化,如内联,大多在外部实现。尽管能够保障本人正在开发的模块将失去适当的优化,但当咱们解决来自其余模块的调用时,事件会简单得多。 编译器优化之所以产生,是因为编译器对正在编译的内容有残缺的理解,但在构建framework时,编译器不可能晓得导入方将如何应用他。因而尽管框架的外部代码将失去优化,但公共接口很可能会不变。 咱们可能会想,咱们能够通知编译器组成framework的源信息,以便它可能从新取得framework的信息,并对其优化。但当咱们意识到该框架的导入器正在链接的是曾经编译的货色时,这会变得复杂。源文件上的所有信息都不见了,如果这是第三方框架,甚至可能一开始就没有这些源信息。(拿不到framework所有的源信息) 这不是一个不可能呈现的问题,尽管编译器有很多种解决这个问题的办法,但实质是一样的,就是让模块的公共接口在链接时蕴含更多编译器可用的信息,这样就能够进一步优化framework中的代码,不限于内联。 在实践中,你可能会留神到这样做可能会重大失控。如果咱们给公共接口的每个办法都增加信息,这样会让框架的体积增大,而且大部分都会被节约掉。咱们不晓得framework将会被如何应用,所以不能一概而论的这样操作。 Swift会让你本人决定来解决这种问题。@inlinable 属性容许您为特定办法启用跨模块内联。这样做了之后,办法的实现将作为模块公共接口的一部分来公开,容许编译器进一步优化来自不同模块的调用。 @inlinable func printPlusOne(_ num: Int) { print("My number: \(num + 1)")}如果内联办法恰好在外部调用其余办法,编译器会要求您也公开这些办法。这能够通过将它们标记为 @inlinable 或 @usableFromInline。@usableFromInline 表明尽管该办法在内联办法中应用的,但它自身并不真正应该被内联。 ...

June 2, 2022 · 1 min · jiezi

关于swift:Swift-Learning-Summary-Error-Handling

Error HandlingFirst-class support for throwing, catching, propagating, and manipulating recoverable errors at runtime. When an optional fails, it’s useful to understand what cause the failure, so that the code can respond accordingly. Example:Reading a file from the disk may be fail in some way. File not existHave no permission to read.File not being encoded in a compatible format.Distinguishing among these different situations allows a program to resolve some errors and to communicate to the user any errors it can’t resolve. ...

May 7, 2022 · 4 min · jiezi

关于swift:Swift-首次调试断点慢的问题解法-优酷-Swift-实践

作者:段继统 & 夏磊 调试断点是与开发体验关系最为亲密点之一,优酷iOS团队在内部调研时候发现,大量国内的iOS APP研发团队也遇到了相似的问题。思考到国内Swift热火朝天的现状,咱们尽快整顿了该计划并通过本文分享进去,心愿能在这个问题上帮忙到大家。前言家喻户晓,Swift是苹果公司于2014年苹果开发者年会(WWDC2014)上公布的编译式新开发语言,反对多编程范式,能够用来撰写基于macOS、iOS、iPadOS、watchOS和tvOS上的APP。对于宽广iOS开发同学来说,这也是研发将来iOS APP开发必须要把握的语言技能。Swift语言在公布后的数年里失去了飞速发展,在2019年苹果公布了Swift5.0版本并宣告Swift ABI稳固。 在Swift5.0版本的ABI稳固后,Swift正式具备了欠缺的生产研发根底,优酷iOS研发团队也开始进行优酷iOS、iPadOS版本的Swift迁徙。优酷在被阿里巴巴收买后,取得了大量团体挪动基建和中间件的反对,因而优酷iOS App在继续演变数年后,根本成为规范的大型组件化工程,由数十个垂直团队负责各自业务并行开发。其中,优酷播放详情页场景是最重要的视频内容生产场景,也率先在2020年初开始业务页面框架、播放器框架及业务模块的Swift迁徙。 2020年底,优酷iOS生产团队实现了业务页面框架和播放器框架的Swift化,这两个框架代码量较少,外部代码后果正当清晰,而且对外部依赖较少。因而在齐全Swift化后,性能上失去了晋升,并且得益于Swift的优良语法,团队开发业务需要代码行数降落,团队效力也取得了增幅。整个过程都比拟顺畅,也并未遇到显著的工程开发或者品质问题。 进入2021年后,在业务页面框架及播放器框架Swift版本的根底上,优酷iOS团队全面启动了业务层代码Swift迁徙,而在这个阶段,Swift调试断点慢的问题开始呈现并日趋严重。 在视频内容场景,外围主业务模块代码7万多行,内部依赖各种模块达200以上,在这个业务模块里,首次断点的工夫顽劣状况下能够达到180秒以上,团队研发效率被重大制约。 2022年初优酷iOS团队实现了80%以上业务代码的Swift迁徙,调试首次断点慢的问题曾经成为业务场的效率瓶颈。在外部的研发幸福感问卷调查里,97%的iOS开发同学认为调试首次断点慢是目前研发过程的最大痛点,这个问题给iOS研发同学带来的挫败感,足以打消Swift的其余劣势。因而,解决这个问题也成为优酷iOS团队年度首要指标。 调试首次断点慢景象及初步剖析Swift调试断点慢次要景象是,当Xcode工程运行起来之后,咱们进行首次断点的等待时间会特地漫长。大部分状况下,工程首次断点失效后,第二次及后续断点的等待时间都非常短暂,根本能够认为无等待时间。不过从团队外部收集的状况来看,不同Mac电脑开发设施和不同的iOS设施体现不全统一,局部同学首次断点之后进行断点的等待时间也极其迟缓。 这个景象或者说问题在团队外部频繁呈现后,咱们首先向苹果中国开发者关系团队反馈,并附上了具体的工程文档等信息。苹果方面也基于反馈在外部进行了考察和验证,并最终给咱们回答,示意外部并没有相似问题的发现。在交换过程中咱们发现,苹果外部的大型APP工程模式都是传统的单工程模式,与国内的组件化多个工程模式截然不同。基于各方面汇总信息,咱们对这个问题开始进行初步剖析和解决。 从下表中能够剖析,播放器框架模块和播放主业务模块状况联合断点工夫来看,断点工夫仿佛与内部依赖数量出现等比关系,所以能够初步判定断点工夫和内部依赖数量存在较强的相关性。 另外还有一个景象,如果子工程和壳工程所依赖SDK的module没有对齐,lldb会很快断点失效,然而打印报错信息,同时无奈po任何值。通过此景象也能够初步剖析进去,在断点时lldb对子工程依赖的module进行了扫描。 但仅仅依赖表象剖析还不够,所以后续的工作咱们从两个方向着手,第一是从播放主业务模块的解耦测试,疾速解耦播放主业务模块的内部依赖,测试耦合数量的缩小对断点工夫是否能有帮忙;第二是从lldb本身断点原理的剖析,看首次断点如此长的工夫中lldb到底在做什么动作。 通过业务模块解耦动手咱们通过删除及整顿工程依赖援用代码的形式,疾速清理内部模块依赖,最终将播放主业务模块的内部依赖降到90个左右。整顿结束后,播放主业务首次调试断点工夫也从200秒左右降到120秒左右,对团队开发艰难现状有所缓解。然而通过理论验证和利用后,咱们也发现这种依赖业务层解耦的形式是对于团队来说不可行的,根本原因有二: 1、革新老本高 播放主业务模块从200多个模块依赖降到了90多个,一方面来说说对于避免工程腐化起到了踊跃帮忙,另一方面在业务需要的压力下,研发人员须要投入了微小的精力来进行代码重构和解耦。长期来看,不同垂直业务团队面临的状况不同,将来的业务技术需要复杂度也不尽相同,这个计划是无奈做到疾速复用。从人力老本来说,这个计划只能短期进行工程治理,无奈长期坚持下去。 2、理论收益低 从取得的收益来看,播放主业务模块内部依赖升高到90多个后,咱们原来的预期是调试首次断点工夫能升高50%甚至更低,然而后果来看,在内部依赖曾经无奈解除的状况下,首次断点等待时间仍然长达120秒以上,这样的收益后果是咱们无奈承受的。因而也得进去论断,在优酷iOS这样大型组件化多工程的模式下,咱们用业务模块解耦的形式是无奈根治该问题的。 通过LLDB剖析动手通过工程治理后,咱们感觉还是应该从侧面攻克该问题,从LLDB剖析来查看根本原因并且解决。如果要剖析LLDB动手,对于工程师来说最好的方法还是查看Swift源码,跑起来看一看外部的原型机制。咱们首先依据苹果的文档将源码下载下来,而后进行配置,具体文档能够参考 How to Set Up an Edit-Build-Test-Debug Loop,一步一步的跟着做就能够。 因为Swift是依赖于LLVM,并且在其根底上做了本人的定制化开发,所以切换分支不能只切换Swift源码的,须要将LLVM一起切到对应的分支上, 保障代码同步。正好Swift提供了相应的工具来帮忙咱们切换对应分支,只须要运行Swift文件下的utils/update-checkout相干命令即可。优酷iOS团队目前应用的是Swift5.4版本,对应Xcode版本为13.2.1。 1、应用LLVM自带耗时工具 想要看到底在断点命中后,到底哪块最耗时,就须要应用工具来计算耗时,而这块LLVM有自带的工具类TimeProfiler,外面封装了计时办法,并且输入相干json文件,而后能够用chrome自带的tracing工具解析后事实相干图表 //TimeProfiler.h void timeTraceProfilerBegin(StringRef Name, StringRef Detail); void timeTraceProfilerBegin(StringRef Name, llvm::function_ref<std::string()> Detail); void timeTraceProfilerEnd();2、耗时最多的两个中央 通过TimeProfiler对要害函数进行耗时埋点,发现有两个函数耗时较多,如下代码: // SwiftASTContext.cppbool SwiftASTContext::GetCompileUnitImportsImpl( SymbolContext &sc, lldb::StackFrameWP &stack_frame_wp, llvm::SmallVectorImpl<swift::AttributedImport<swift::ImportedModule>> *modules, Status &error)// SymbolFileDWARF.cppvoid SymbolFileDWARF::FindTypes( ConstString name, const CompilerDeclContext &parent_decl_ctx, uint32_t max_matches, llvm::DenseSet<lldb_private::SymbolFile *> &searched_symbol_files, TypeMap &types)一个是SwiftASTContext类的GetCompileUnitImportsImpl办法,这个办法次要是解析以后编译单元与Module相干的操作,另一个则是在某一个变量如果是Any类型,则须要对其进行解析,找到其类型相干的操作,而最终这两个函数的操作都与以后工程的二进制依赖剖析有关系,所以,如果能缩小在断点命中后对依赖的剖析,那么断点工夫就会越快。 ...

May 6, 2022 · 2 min · jiezi

关于swift:Swift-Learn-Summary-Strings-and-Characters

Strings and CharactersString type is bridged with Foundation’s NSString. Foundation extends String to expose methods defines by NSString. If import Foundation, you can access those NSString methods on String without casting. String Literals// "Hello World!" is a String literal. let sentence = "Hello World!"// Multilinelet story = """There are some people in the room.They are having a party.Because today is the Christmas."""// every line has the line breaks.// the start and end sign(""") must take a single line.// backslash(\), it means that the string is not broken. Then line breaks not to be part of the string's value.let content = """In a happy atmos\phere. We start the conversation"""print(content) // result: In a happy atmosphere. We start the conversation.// Alignment of quotation marks in the multiline string.let desc = """ Ha it's so funny."""print(desc) // result: Ha its so funny.let desc = """ Ha it's so funny. """print(desc) // result: Ha its so funny.let desc = """ Ha it's so funny. """print(desc) // result: Ha its so funny.Special Character in String Literals ...

March 20, 2022 · 6 min · jiezi

关于swift:Swift-Learning-Summary-Basic-Operator

Basic OperatorsTerminologyUnary operator (such as -a)Binary operator(such as 2 + 3)Ternary operator(such as a ? b : c)The values that operators affect are operands. Prefix, infix, suffix Assignment Operator // Assignmentlet b = 10var a = 5a = b// tuple assignmentlet (x, y) = (1, 2)// not allowed, it is to prevent miss use when the equal operator(==) is actually intended.if x = y { // This isn't valid, because x = y doesn't return a value.}Arithmetic Operators ...

March 19, 2022 · 3 min · jiezi

关于swift:Swift-Learning-Summary-Basics

BasicSyntax // variablevar number = 6var title : String?// const value, should be initialized before use it, can't be changed after its definitionlet speed = 200 let name : String? = nilBase data typeTypeDescUInt8UInt8.min is 0, UInt8.max is 255IntOn the 32-bit platform, it’s as Int32; On the 64-bit platform, it’s as Int64.UintOn the 32-bit platform, it’s as UInt32; On the 64-bit platform, it’s as UInt64.Double64-bit floating-point numberFloat32-bit floating-point numberBooleantrue or falseNumeric ...

March 12, 2022 · 8 min · jiezi

关于swift:Swift-之父正式退出-Swift-核心团队这只是在浪费我的时间

近日,swift 发布公告发表其外围团队目前正在思考重组我的项目的领导层,以便于更多的社区成员积极参与该项目标治理。 同时,此布告证实了“ Swift 之父、LLVM 我的项目创始人 Chris Lattner 将来到外围团队”一事,并示意 Chris Lattner 已于去年中断工作,并决定来到外围团队,将工夫集中在其余我的项目上。 布告收回后,ChrisLattner在帖子中表明其来到 Swift 外围团队和 Swift Evolution 社区的起因并不简略。 Chris Lattner 于五年前来到苹果,尔后他做过特斯拉主动驾驶软件VP、谷歌Tensorflow基础设施主管、SiFive工程总裁。他在帖子中说到本人总是很忙,但Swift对他而言十分重要,所以他违心破费大量工夫来改良和推动它,他保持每周加入例会、参加社区探讨,也亲自编写和迭代了许多代码。 正因为此,作出来到 Swift的决定对Chris Lattner来说才显得尤为艰巨。 他示意来到外围团队的根本原因是会议自身的“有毒环境”。 而减速他来到的导火索是去年夏天的一次特定会议:在通过 WebEx 被羞辱和呼啸之后(这种状况并不是第一次,也不只是一个外围团队成员),他决定劳动一下。 去年秋天,他促使领导层与他探讨了这一状况;但在防止解决这一问题后,他们找了借口并明确示意他们不打算对此采取任何口头。 因而,他决定来到。 此外,Chris Lattner 认为本人以及一些社区成员的想法曾经越来越不受器重,甚至被外围团队所漠视;且团队透明性也呈现了肯定的问题。 “我只是在节约我的工夫。我不认为我的感觉在这里是举世无双的。” Chris Lattner 认为他曾经不对 Swift 产生影响了,他所关怀的一些设计前提(比方"simple things that compose")仿佛不再风行了。 而且除了 Swift 之外,他还有很多其余的趣味,也不不足能够花工夫的货色。他表明本人是那种总是向前看的人,所以尽管这种状况很让他惆怅,但他曾经信心向前看了。 最初,Chris Lattner 示意“一个衰弱和容纳的社区将持续有利于 Swift 的设计和进化”。

February 23, 2022 · 1 min · jiezi

关于swift:Swift-在手淘商品评价的技术重构与实践

作者:王浙剑(柘剑) 手淘新版商品评估列表在经验一个半月的技术重构,几个月的迭代和放量,最终在 2021 年的双十一上,以 100% 的流量稳固的跑完了整个过程。咱们不仅在业务上有了比拟明确的晋升,同时还积淀了不少技术摸索,比方积淀基于 DinamicX + 事件链编排的轻模式研发框架、推动原生语言升级成 Swift/Kotlin,最终使得整体研发效率和稳定性有一个比拟大的晋升。(注:DinamicX 为外部自研动态化 UI 框架) 这篇文章,我会重点议论对于 Swift 的局部。如果你想理解对于 Swift 如何晋升研发效率/品质、现有我的项目/模块是否须要 Swift 作为原生语言如何选型、在商品评估落地 Swift 过程中咱们遇到了哪些问题以及最初有哪些收益和论断的一些问题,心愿这篇文章能够给你带来一些帮忙。 首先是,我为什么会抉择学习 Swift? 技术改革,将来已来因为,我心田非常动摇,相比拟于 OC,Swift 更能承载将来。 刚强后盾最次要的起因就是它有一个刚强的后盾,Swift 作为 Apple 将来最重要的开发语言,光对外输入的 WWDC 内容就曾经高达 73 个,包含但不限于语法、设计、性能、开发工具链等,具体内容如图所示: 回过头来看 Swift 这几年的倒退,从 2014 年开始正式对外公布,到当初曾经经验了 7 个年头了,在整个过程中,Apple 投入了大量精力建设 Swift,尤其是 Swift Only 框架的呈现,也意味着 Apple 正在踊跃提倡各位投入到 Swift 开发中来。 三大劣势其次,Swift 有三个比拟明确的劣势: 更快、更平安且更具备表白性。 更快 是指 Swift 在执行效率上做了很多优化。比方,Swift 零碎库自身就采纳了很多不须要援用计数的根底类型,无论是内存调配大小、援用计数损耗、办法派发动态剖析等方面的问题都失去了一个无效的晋升。具体细节这里就不开展剖析,感兴趣的能够移步 Understanding Swift Performance 理解细节。 所谓的 平安 不等于不产生 Crash,而是指任何的输出都有一个比拟明确的体现定义。Swift 设计初衷是心愿开发者无需任何不平安的数据结构就能编写代码,因而 Swift 领有一个非常强壮的类型零碎,开发者简直不须要思考指针的问题,就能实现所有的开发工作。同时还提供了一系列前缀为 Unsafe 的类型或函数,用于与不平安语言(例如 C 语言)的高性能交互、操作原始内存等绝对不平安的操作,一方面以 Unsafe 警觉开发者应用这些 API ,另外一方面是辨别类型以保障大部分开发场景应用的都是平安的类型。 ...

January 6, 2022 · 9 min · jiezi

关于swift:httplxgongxuanwangcomsszt4htm

pub 等标识前面紧跟着的是密钥的加密算法,例如 rsa3072 示意密钥长度为 3072 位的 RSA 算法。以后版本 (2.2.19) 遴选公务员应用 gpg --gen-key 生成的默认密钥应用的就是 3072 位的 RSA 算法。反对的其余算法能够输出 gpg --version 查看。留神这里指的是非对称加密的算法,对称加密应用的算法能够在每次加密时通过参数指定不同算法。 如果想要生成其余算法的主密钥,能够应用 gpg --full-gen-key 命令。这里也体现了应用子密钥的灵活性,例如咱们能够应用 4096 位的主密钥,更加平安。而后应用 3072 位的子密钥用于加密和签名,更加高效。如果将来 3072 位密钥不够平安时,只须要从新生成 4096 位的子密钥,而不必从新生成主密钥。http://lx.gongxuanwang.com/ss... 须要留神信赖和有效性不肯定有关系。遴选公务员信赖示意的是对这个密钥签名其余密钥的信赖水平,而有效性是对这个密钥自身的验证。例如我晓得某个密钥的确是特朗普的,他是无效的。但我感觉这个人满嘴跑火车,他签名的其余密钥我一律不认。http://lx.gongxuanwang.com/

November 29, 2021 · 1 min · jiezi

关于swift:智汀家庭云iOS端业务功能场景篇

【1】增加场景创立场景:EditSceneViewController.swift private func createScene() { guard let name = inputHeader.textField.text else { return } if name == "" { showToast(string: "场景名称不能为空".localizedString) return } if scene.scene_conditions.count == 0 { showToast(string: "请先增加条件".localizedString) return } if scene.scene_tasks.count == 0 { showToast(string: "请先增加执行工作".localizedString) return } scene.name = name /// 执行条件 if scene.scene_conditions.first?.condition_type == 0 { // 手动 scene.auto_run = false } else { // 主动 scene.auto_run = true if self.conditionHeader.conditionRelationshipType == .all { // 满足所有条件 scene.condition_logic = 1 } else { // 满足任一条件 scene.condition_logic = 2 } if scene.time_period == nil { scene.time_period = 1 let format = DateFormatter() format.dateStyle = .medium format.timeStyle = .medium format.dateFormat = "yyyy:MM:dd HH:mm:ss" if let startTime = format.date(from: "2000:01:01 00:00:00")?.timeIntervalSince1970 { scene.effect_start_time = Int(startTime) } if let endTime = format.date(from: "2000:01:02 00:00:00")?.timeIntervalSince1970 { scene.effect_end_time = Int(endTime) } scene.repeat_type = 1 scene.repeat_date = "1234567" } } /// 申请接口 saveButton.selectedChangeView(isLoading: true) ApiServiceManager.shared.createScene(scene: scene.transferedEditModel) { [weak self] response in self?.showToast(string: "创立胜利".localizedString) self?.navigationController?.popViewController(animated: true) } failureCallback: { [weak self] (code, err) in self?.showToast(string: err) self?.saveButton.selectedChangeView(isLoading: false) }}【2】场景管制场景的管制,包含手动场景的执行、主动场景的开启/敞开。 ...

October 9, 2021 · 2 min · jiezi

关于swift:swift-第二节课学习笔记-字符串连接

var i = 200var str = "Hello"str = str + "World"str = "\(str),asdhjdh,\(i)"print(str)输入后果HelloWorld,asdhjdh,200请按任意键持续. . .

August 27, 2021 · 1 min · jiezi

关于swift:swift-学习第一节课

第一步 关上xcode creat workspace 创立一个工作空间第二步 create project 创立一个我的项目 import Foundationprinln("Hello,World!")应用commad + R运行 也能够用上面图片的三角符号 输入

August 26, 2021 · 1 min · jiezi

关于swift:2021Swift参数及泛型参数参考

本节波及泛型类型、泛型函数以及泛型结构器的参数,包含形参和实参。申明泛型类型、函数或结构器时,须指定相应的类型参数。类型参数相当于一个占位符,当实例化泛型类型、调用泛型函数或泛型结构器时,就用具体的类型实参代替之。 对于 Swift 语言的泛型概述,见泛型(第二局部第22章)。 泛型形参语句泛型形参语句指定泛型类型或函数的类型形参,以及这些参数的关联束缚和要求。泛型形参语句用尖括号(<>)包住,并且有以下两种模式: 1. <generic parameter list> <generic parameter list where requirements > 1. <generic parameter list> <generic parameter list where requirements > 泛型形参列表中泛型形参用逗号离开,每一个采纳以下模式: 1. type parameter : constrain 泛型形参由两局部组成:类型形参及其后的可选束缚。类型形参只是占位符类型(如T,U,V,KeyType,ValueType等)的名字而已。你能够在泛型类型、函数的其余部分或者结构器申明,以及函数或结构器的签名中应用它。束缚用于指明该类型形参继承自某个类或者恪守某个协定或协定的一部分。例如,在上面的泛型中,泛型形参T: Comparable示意任何用于代替类型形参T的类型实参必须满足Comparable协定。 1. func simpleMin<T: COmparable>(x: T, y: T) -> T {2. if x < y {3. return y4. }5. return x6. } 如,Int和Double均满足Comparable协定,该函数承受任何一种类型。与泛型类型相同,调用泛型函数或结构器时不须要指定泛型实参语句。类型实参由传递给函数或结构器的实参推断而出。 1. simpleMin(17, 42)2. // T is inferred to be Int3. simpleMin(3.14159, 2.71828)4. // T is inferred to be Double 材料收录处Where语句要想对类型形参及其关联类型指定额定要求,能够在泛型形参列表之后增加where语句。where语句由关键字where及其后的用逗号宰割的多个要求组成。 ...

July 27, 2021 · 1 min · jiezi

关于swift:swift项目取消Mainstoryboard文件使用纯代码启动

1.删除Main.storyboard文件2.删除General里Deployment info中的Main interface配置项里的"Main"3.SceneDelegate是用来在iPad上做分屏应用的, 个别开发手机app的话能够间接删除4.删除AppDelegate中的configurationForConnecting和didDiscardSceneSessions办法, 这两个办法是用来设置AppDelegate代理给SceneDelegate用的5.删除info.plist中的Application Scene Manifest配置项done! $ cmd + R运行我的项目看成果

July 14, 2021 · 1 min · jiezi

关于swift:Swift-UI项目调用core-data

一. 前言这篇文章是我写的第一篇Swift UI相干的笔记吧。 这真的难搞哦,毕竟新进去的。国内文档真的是少之又少,国外的文档也真不多,根本搜进去的都是UIKit的。官网文档,也是一言难尽,根本就处于,我要实现一个性能,而后去搜一下各种帖子,筛选掉无用的帖子,找到有用的点,当然也是时常基本找不到有用的帖子,而后就要去油管上看各种教程,而后发现:哦!原来还有这个办法!接着去官网文档搜一下,看一下属性,本人调用调用...... 光是这个core data我就折腾了近一周,最初发现,原来还是官网好呀..... 吐槽一下自学swift ui,当初进入正题了。 core data呢,是苹果官网的本地数据库,然而其存储的文件其实是sqlite文件。其能够通过icloud实现备份和同步,当然icloud我会额定写一篇文档来具体讲述的(又是一把辛酸泪...)。 二. 环境如下是我以后的环境: 零碎:macOS Big Sur 11.4Xcode:Version 12.5 (12E262)Swift:5.4三. 操作步骤1. 创立我的项目在创立我的项目的时候,能够间接抉择Use Core Data选项,xcode会间接在ContentView.swift中生成一个相干demo。 2. 查看相干文件创立之后,查看文件目录,绝对于不抉择Use Core Data,会多出如下几个文件: <Your-Project-Name>.xcdatamodeIdPersistence.swift3. 创立core data表点击<Your-Project-Name>.xcdatamodeId文件,进入页面。 能够看到页面内有默认的CONFIGURATIONS为Default,默认的ENITITIES为Item,能够了解为别离对应sql中的库和表。 而后点击Item,能够看到其字段,有默认的timestamp字段。 若要新增ENTITIES(表),点击底部的Add Entity按钮即可。 若要新增Attributes(字段),点击右侧Attributes中的+即可,留神字段要抉择类型。 比方,此处以默认的Item为例,新增username和age两个字段。 4. 代码层面操作core data1)查看@Environment(\.managedObjectContext) private var viewContext@FetchRequest( sortDescriptors: [NSSortDescriptor(keyPath: \Item.timestamp, ascending: true)], animation: .default)private var items: FetchedResults<Item>// body中便当items即可2)新增private func addItem() { withAnimation { let newItem = Item(context: viewContext) newItem.timestamp = Date() do { try viewContext.save() } catch { // Replace this implementation with code to handle the error appropriately. // fatalError() causes the application to generate a crash log and terminate. You should not use this function in a shipping application, although it may be useful during development. let nsError = error as NSError fatalError("Unresolved error \(nsError), \(nsError.userInfo)") } }}如上是xcode主动生成的新增函数,若是要自定义增加字段能够这样改变下: ...

June 7, 2021 · 3 min · jiezi

关于swift:Swift循环

有的时候,咱们可能须要屡次执行同一块代码。个别状况下,语句是按程序执行的:函数中的第一个语句先执行,接着是第二个语句,依此类推。 编程语言提供了更为简单执行门路的多种控制结构。 循环语句容许咱们屡次执行一个语句或语句组,上面是大多数编程语言中循环语句的流程图: 循环类型Swift 语言提供了以下几种循环类型。点击链接查看每个类型的详细描述: 循环类型形容for-in遍历一个汇合外面的所有元素,例如由数字示意的区间、数组中的元素、字符串中的字符。语法:for index in var { 循环体 }for 循环该循环形式在 Swift 3 中曾经弃用。while 循环运行一系列语句,如果条件为true,会反复运行,直到条件变为false。语法:while condition { statement(s) } 其余的不罕用循环管制语句循环管制语句扭转你代码的执行程序,通过它你能够实现代码的跳转。Swift 以下几种循环管制语句: 管制语句形容continue 语句通知一个循环体立即进行本次循环迭代,从新开始下次循环迭代。break 语句中断以后循环。fallthrough 语句如果在一个case执行完后,继续执行上面的case,须要应用fallthrough(贯通)关键字。

April 9, 2021 · 1 min · jiezi

关于swift:Swift-条件语句

Swift 条件语句条件语句通过设定的一个或多个条件来执行程序,在条件为真时执行指定的语句,在条件为 false 时执行另外指定的语句。 能够通过下图来简略理解条件语句的执行过程: Swift 提供了以下几种类型的条件语句: 语句形容if 语句if 语句 由一个布尔表达式和一个或多个执行语句组成。if...else 语句if 语句 后能够有可选的 else 语句, else 语句在布尔表达式为 false 时执行。if...else if...else 语句if 后能够有可选的 else if...else 语句, else if...else 语句罕用于多个条件判断。内嵌 if 语句你能够在 if 或 else if 中内嵌 if 或 else if 语句。switch 语句switch 语句容许测试一个变量等于多个值时的状况。? : 运算符咱们曾经在后面的章节中解说了 条件运算符 ? :,能够用来代替 if...else 语句。它的个别模式如下: Exp1 ? Exp2 : Exp3;其中,Exp1、Exp2 和 Exp3 是表达式。请留神,冒号的应用和地位。 ? 表达式的值是由 Exp1 决定的。如果 Exp1 为真,则计算 Exp2 的值,后果即为整个 ? 表达式的值。如果 Exp1 为假,则计算 Exp3 的值,后果即为整个 ? 表达式的值。 ...

April 2, 2021 · 1 min · jiezi

关于swift:Swift输入

Swift 根本输出如果要在Swift中从用户那里获取输出,则必须在不应用UIKit框架的状况下能力在Xcode Playground中进行输出。 然而,应用Swift框架,您能够在Xcode中创立一个命令行应用程序,以从用户那里获取输出。 这是您能够用来获取用户输出的代码。 示例:应用readLine()从用户那里获取输出示例 print("请输入您最喜爱的编程语言", terminator: ".")let name = readLine()print("您最喜爱的编程语言是 \(name!).")运行该程序时,输入为: 请输入您最喜爱的编程语言.Swift您最喜爱的编程语言是 Swift.在上述程序中,打印函数输入请输入您喜爱的编程语言。在调试区域。语句 let name = readLine() 期待用户在调试区域中输出。 如果键入“ Swift”并按Enter,则readLine函数会将字符串调配给常量 name 并将其显示为 您最喜爱的编程语言是Swift。 因为 readLine 函数返回一个可选字符串,因而咱们像 name! 申明中一样强行解开了常量print("Your favorite programming language is (name!)")。 因为readLine函数返回一个可选字符串,因而在语句print(“您最喜爱的编程语言是(name!)”)中,咱们强制将常量解包为 name!

March 29, 2021 · 1 min · jiezi

关于swift:Swift-常量

常量一旦设定,在程序运行时就无奈扭转其值。 常量能够是任何的数据类型如:整型常量,浮点型常量,字符常量或字符串常量。同样也有枚举类型的常量: 常量相似于变量,区别在于常量的值一旦设定就不能扭转,而变量的值能够随便更改。 常量申明常量应用关键字 let 来申明,语法如下: let constantName = <initial value>以下是一个简略的 Swift 程序中应用常量的实例: import Cocoalet constA = 42print(constA)以上程序执行后果为: 42类型标注当你申明常量或者变量的时候能够加上类型标注(type annotation),阐明常量或者变量中要存储的值的类型。如果要增加类型标注,须要在常量或者变量名前面加上一个冒号和空格,而后加上类型名称。 var constantName:<data type> = <optional initial value>以下是一个简略是实例演示了 Swift 中常量应用类型标注。须要留神的是常量定义时必须初始值: import Cocoalet constA = 42print(constA)let constB:Float = 3.14159print(constB)以上程序执行后果为: 423.14159常量命名常量的命名能够由字母,数字和下划线组成。 常量须要以字母或下划线开始。 Swift 是一个辨别大小写的语言,所以字母大写与小写是不一样的。 常量名也能够应用简略的 Unicode 字符,如下实例: import Cocoalet _const = "Hello, Swift!"print(_const)let 你好 = "你好世界"print(你好)以上程序执行后果为: Hello, Swift!你好世界常量输入变量和常量能够应用 print(swift 2 将 print 替换了 println) 函数来输入。 在字符串中能够应用括号与反斜线来插入常量,如下实例: import Cocoalet name = "Apple"let site = "http://www.apple.com.cn"print("\(name)的官网地址为:\(site)")以上程序执行后果为: ...

March 23, 2021 · 1 min · jiezi

关于swift:Swift-可选Optionals类型

Swift 的可选(Optional)类型,用于解决值缺失的状况。可选示意"那儿有一个值,并且它等于 x "或者"那儿没有值"。 Swfit语言定义后缀?作为命名类型Optional的简写,换句话说,以下两种申明是相等的: var optionalInteger: Int?var optionalInteger: Optional<Int>在这两种状况下,变量 optionalInteger 都是可选整数类型。留神,在类型和 ?之间没有空格。 Optional 是一个含有两种状况的枚举,None 和 Some(T),用来示意可能有或可能没有值。任何类型都能够明确申明为(或者隐式转换)可选类型。当申明一个可选类型的时候,要确保用括号给 ? 操作符一个适合的范畴。例如,申明可选整数数组,应该写成 (Int[])? 写成 Int[]? 会报错。 当你申明一个可选变量或者可选属性的时候没有提供初始值,它的值会默认为 nil。 可选项遵循 LogicValue 协定,因而能够呈现在布尔环境中。在这种状况下,如果可选类型T?蕴含类型为T的任何值(也就是说它的值是 Optional.Some(T) ),这个可选类型等于 true,反之为 false。 如果一个可选类型的实例蕴含一个值,你能够用后缀操作符 !来拜访这个值,如下所示: optionalInteger = 42optionalInteger! // 42应用操作符!去获取值为nil的可选变量会有运行时谬误。 你能够用可选链接和可选绑定选择性执行可选表达式上的操作。如果值为nil,任何操作都不会执行,也不会有运行报错。 让咱们来具体看下以下实例来理解 Swift 中可选类型的利用: import Cocoavar myString:String? = nilif myString != nil { print(myString)}else{ print("字符串为 nil")}以上程序执行后果为: 字符串为 nil可选类型相似于Objective-C中指针的nil值,然而nil只对类(class)有用,而可选类型对所有的类型都可用,并且更平安。 强制解析当你确定可选类型的确蕴含值之后,你能够在可选的名字前面加一个感叹号(!)来获取值。这个感叹号示意"我晓得这个可选有值,请应用它。"这被称为可选值的强制解析(forced unwrapping)。 实例如下: import Cocoavar myString:String?myString = "Hello, Swift!"if myString != nil { print(myString)}else{ print("myString 值为 nil")}以上程序执行后果为: ...

March 23, 2021 · 1 min · jiezi

关于swift:Swift-变量

======== 变量是一种使用方便的占位符,用于援用计算机内存地址。 Swift 每个变量都指定了特定的类型,该类型决定了变量占用内存的大小,不同的数据类型也决定可存储值的范畴。 上一章节咱们曾经为大家介绍了根本的数据类型,包含整形Int、浮点数Double和Float、布尔类型Bool以及字符串类型String。此外,Swift还提供了其余更弱小数据类型, Optional, Array, Dictionary, Struct, 和 Class 等。 接下来咱们将为大家介绍如何在 Swift 程序中申明和应用变量。 变量申明变量申明意思是通知编译器在内存中的哪个地位上为变量创立多大的存储空间。 在应用变量前,你须要应用 var 关键字申明它,如下所示: var 变量名 = 初始值 以下是一个 Swift 程序中变量申明的简略实例: import Cocoavar varA = 42print(varA)var varB:FloatvarB = 3.14159print(varB) 以上程序执行后果为: 423.14159 变量命名变量名能够由字母,数字和下划线组成。 变量名须要以字母或下划线开始。 Swift 是一个辨别大小写的语言,所以字母大写与小写是不一样的。 变量名也能够应用简略的 Unicode 字符,如下实例: import Cocoavar _var = "Hello, Swift!"print(_var)var 你好 = "你好世界"var Apple = "www.apple.com.cn"print(你好)print(Apple) 以上程序执行后果为: Hello, Swift!你好世界www.apple.com.cn 变量输入变量和常量能够应用 print(swift 2 将 print 替换了 println) 函数来输入。 在字符串中能够应用括号与反斜线来插入变量,如下实例: import Cocoavar name = "苹果"var site = "http://www.apple.com.cn"print("(name)的官网地址为:(site)") ...

March 21, 2021 · 1 min · jiezi

关于swift:Swift-数据类型

========== 在咱们应用任何程序语言编程时,须要应用各种数据类型来存储不同的信息。 变量的数据类型决定了如何将代表这些值的位存储到计算机的内存中。在申明变量时也可指定它的数据类型。 所有变量都具备数据类型,以决定可能存储哪种数据。 内置数据类型Swift 提供了十分丰盛的数据类型,以下列出了罕用了几种数据类型: Int一般来说,你不须要专门指定整数的长度。Swift 提供了一个非凡的整数类型Int,长度与以后平台的原生字长雷同: 在32位平台上,Int和Int32长度雷同。在64位平台上,Int和Int64长度雷同。除非你须要特定长度的整数,一般来说应用Int就够了。这能够进步代码一致性和可复用性。即便是在32位平台上,Int能够存储的整数范畴也能够达到-2,147,483,648~2,147,483,647,大多数时候这曾经足够大了。 UIntSwift 也提供了一个非凡的无符号类型UInt,长度与以后平台的原生字长雷同: 在32位平台上,UInt和UInt32长度雷同。在64位平台上,UInt和UInt64长度雷同。留神: 尽量不要应用UInt,除非你真的须要存储一个和以后平台原生字长雷同的无符号整数。除了这种状况,最好应用Int,即便你要存储的值已知是非负的。对立应用Int能够进步代码的可复用性,防止不同类型数字之间的转换,并且匹配数字的类型推断。整数类型须要留神以下几点: 在 32 位零碎上, Int 和 Int32 长度雷同。在 64 位零碎上, Int 和 Int64 长度雷同。在 32 位零碎上, UInt 和 UInt32 长度雷同。在 64 位零碎上, UInt 和 UInt64 长度雷同。Int8, Int16, Int32, Int64 别离示意 8 位, 16 位, 32 位, 和 64 位的有符号整数模式。UInt8, UInt16, UInt32, UInt64 别离示意 8 位, 16 位, 32 位 和 64 位的无符号整数模式。浮点数:Float、Double浮点数是有小数局部的数字,比方 3.14159,0.1 和 -273.15。 浮点类型比整数类型示意的范畴更大,能够存储比 Int 类型更大或者更小的数字。Swift 提供了两种有符号浮点数类型: ...

March 21, 2021 · 2 min · jiezi

关于swift:Swift-基本语法

========== 在上一章节中咱们曾经讲到如何创立 Swift 语言的 "Hello, World!" 程序。当初咱们来温习下。 如果创立的是 OS X playground 须要引入 Cocoa : import Cocoa / 我的第一个 Swift 程序 / var myString = "Hello, World!" print(myString) 如果咱们想创立 iOS playground 则须要引入 UIKit : import UIKit var myString = "Hello, World!" print(myString) 执行以上程序,输入后果为: Hello, World! 以上代码即为 Swift 程序的根本构造,接下来咱们来具体阐明构造的组成部分。 Swift 引入咱们能够应用 import 语句来引入任何的 Objective-C 框架(或 C 库)到 Swift 程序中。例如 import cocoa 语句导入了应用了 Cocoa 库和API,咱们能够在 Swift 程序中应用他们。 Cocoa 自身由 Objective-C 语言写成,Objective-C 又是 C 语言的严格超集,所以在 Swift 利用中咱们能够很简略的混入 C 语言代码,甚至是 C++ 代码。 ...

March 21, 2021 · 2 min · jiezi

关于swift:Swift-环境搭建

========== Swift是一门开源的编程语言,该语言用于开发OS X和iOS应用程序。 在正式开发应用程序前,咱们须要搭建Swift开发环境,以便更好敌对的应用各种开发工具和语言进行疾速利用开发。因为Swift开发环境须要在OS X零碎中运行,因而其环境的搭建将不同于Windows环境,上面就一起来学习一下swift开发环境的搭建办法。 胜利搭建swift开发环境的前提: 必须领有一台苹果电脑。因为集成开发环境XCode只能运行在OS X零碎上。电脑系统必须在OS 10.9.3及以上。电脑必须装置Xcode集成开发环境。Swift 开发工具Xcode下载Swift 开发工具官网地址:https://developer.apple.com/xcode/download/。 Swift 源代码下载:https://swift.org/download/#latest-development-snapshots 下载实现后,双击下载的 dmg 文件装置,装置实现后咱们将 Xcode 图标踢挪动到利用文件夹。 你也能够在 App Store 中搜寻 xcode 装置,如下图所示: 第一个 Swift 程序Xcode 装置实现后,咱们就能够开始编写 Swift 代码了。 接下来咱们在利用文件夹关上 Xcode,关上后在屏幕顶部抉择 File => New => Playground。 接着 为 playground 设置一个名字并抉择 iOS 平台。 Swift 的 playground 就像是一个可交互的文档,它是用来练手学swift的,写一句代码出一行后果(右侧),能够实时查看代码后果,是学习swift语言的利器! 以下是 Swift Playground 窗口默认的代码: import UIKitvar str = "Hello, playground" 如果你想创立 OS x 程序,须要导入 Cocoa 包 import Cocoa 代码如下所示: ...

March 21, 2021 · 1 min · jiezi

关于swift:Mac系统上学习Swift第1节认识Swift

Swift 是一种反对多编程范式和编译式的开源编程语言,苹果于2014年WWDC(苹果开发者大会)公布,用于开发 iOS,OS X 和 watchOS 应用程序。 Swift 联合了 C 和 Objective-C 的长处并且不受 C 兼容性的限度。 Swift 在 Mac OS 和 iOS 平台能够和 Object-C 应用雷同的运行环境。 2015年6月8日,苹果于WWDC 2015上发表,Swift将凋谢源代码,包含编译器和规范库。第一个 Swift 程序 第一个 Swift 程序当然从输入 "Hello, World!" 开始,代码如下所示: 实例/ 我的第一个 Swift 程序 / var myString = "Hello, World!" print(myString) 实例解析 var myString = "Hello, World!" : 应用 var 关键字定义一个变量 myString,值为 Hello, World!print : 输入变量的值*相干材料Swift 官网手册

March 20, 2021 · 1 min · jiezi

关于swift:3个关于SwiftUI中TextField不得不看的知识点

简直每一个iOS App都须要解决用户交互,输入框作为获取用户输出内容的最罕用控件之一是开发者常常用到的一个控件。这篇文章咱们来看一下在iOS14中怎么应用全新的SwiftUI框架来解决TextField。 到公众号【iOS开发栈】学习更多SwiftUI、iOS开发相干内容。SwiftUI创立一个根底的TextFieldstruct ContentView: View { @State private var aStr = "" var body: some View { VStack { TextField("公众号<iOS开发栈>", text: $aStr) Text(aStr) } }}创立一个TextField须要两个参数,一个占位符和一个Bind<Binding<String。 TextFieldStyle通过设置textFieldStyle能够批改TextField的展示款式,在SwiftUI中零碎自带了UIKit框架中的几个款式:DefaultTextFieldStyle/PlainTextFieldStyle/RoundedBorderTextFieldStyle。 设置的办法是: TextField("公众号<iOS开发栈>", text: $aStr) .textFieldStyle(DefaultTextFieldStyle())如果零碎自带的这三种款式都不能满足需要,也能够自定义TextField的款式TextField文本对齐形式在SwiftUI中设置TextField的文本对齐形式应用的modifier是multilineTextAlignment,其中蕴含leading/center/trailing三种对齐形式。 应用这个modifier的办法如下: TextField("公众号<iOS开发栈>", text: $aStr) .multilineTextAlignment(.trailing)到公众号【iOS开发栈】学习更多SwiftUI、iOS开发相干内容。总结这篇文章对SwiftUI框架中的TextFiled控件进行了解说,次要波及了创立办法、批改展现款式和设置文本对齐办法三个方面,心愿对你有所帮忙。

March 1, 2021 · 1 min · jiezi

关于swift:京东App-Swift-混编及组件化落地

背景自 Swift 诞生以来,逐渐见证其从饱受诟病到日渐欠缺。在苹果的全力推动下,耳濡目染地把开发反对核心从 Objective-C 转向 Swift,在业界的呼声也越演越烈。当咱们相继迎来 ABI稳固、Module stability、Library evolution 等性能后,咱们期盼已久的 Swift 未然到来,决然启动了京东 App 的混编之旅。咱们仍然保持操之过急,后期对 Swift 技术做了诸多调研工作,具体可见《Swift环境及编译优化调研》。2020年7月京东 App 的首个混编版本上线苹果商店,实现了组件内和主工程的混编工作;近期,咱们实现了对京东组件化管理工具(iBiuTool)的革新,混编组件化性能正式落地,这也标记着京东 Swift 混编根底反对建设结束。然而,Just the beginning... 期待的Swift曾经到来2.1 ABI稳固Swift 5.0,提供 ABI 稳固,解决了 Swift runtime 的版本兼容问题。这意味着通过 Swift 5.0 及以上的编译器编译进去的二进制,就能够运行在任意 Swift 5.0 及以上的 Swift runtime 上。ABI 稳固后,Swift runtime 和规范库曾经植入 macOS 10.14.4、iOS 12.2、watchOS 5.2 及以上零碎中。依据苹果官网数据,截止到 2020年12月15日,四年内公布的 iPhone 设施中 iOS 13及以上占比已达 98%。 另外,ABI 稳固还带来了性能上的晋升。因为 Swift runtime 曾经被深刻的集成在了设施的操作系统中,并且联合零碎层做了许多优化,这就使得 Swift 程序具备更快的启动速度、更好的运行性能,以及更少的内存占用量。 2.2 Module StabilitySwift 5.1,反对 Module Stability,解决模块间编译器版本兼容的问题。这意味着应用不同版本编译器构建的 Swift 模块能够在同一个应用程序中一起应用。即便某些三方库的 Swift 编译器版本与你所应用的不同,也不会存在编译问题。官网文档中举了一个非常失当的例子,应用 Swift 6 构建的 framwork,能够被 Swift 6 和将来的 Swift 7 编译器失常应用。所以这个进化对于开发者来说,相对是一件十分美妙事件。 ...

February 8, 2021 · 4 min · jiezi

关于swift:CollectionView详解

最近将 UICollectionView 进行了一个全面的学习及总结,参考了网上大量的文章,把官网文档进行了大略翻译,最初有个小Demo。 UICollectionView基础知识collection view 是使用一个灵便多变的布局出现一系列有序数据项的一种办法。collection view最通常的应用应用像网格状排列来出现数据项,然而 iOS 的 collection view 的能力不仅限于行和列。应用 collection views, 视觉元素的准确布局可通过子类化定义并被动静扭转。所以你能够实现网格,栈,圆形布局,动静扭转布局,或任何你能够设想的排列布局。Collection View 是由多个对象合作而成 在下面能够看出,一个 CollectionView 视图从 data source 中获取数据信息,data source 和 delegate 来治理具体的单元对象,包含选中、未选中、高亮、未高亮等状态。Layout 决定了每个 cell 之间的边界及布局信息,并通过 layoutattributes 来决定每个 cell 的属性。 ReusableView 采纳的可重用 cell 的办法,能够提高效率,反对三种不同的可重用的viewcell 展现了 collection view 的次要内容,cell 的内容来自你的数据对象,每个 cell 必须是 UICollectionViewCell 类的实例。cell 对象能够治理本人的选中和高亮状态Supplementary views 展现了对于 section 的信息,同样也是数据驱动的,是可选的。Decoration views 装璜视图,不与数据相干,齐全属于 layout 对象。Layout 对象管制视觉显示layout object 决定着 cell 的大小,地位还有其余显示相干的属性。layout object 没有接触任何视图,它只是一个属性汇合。而没有任何限度,你能够随你所想。layout object 不止能够管制视图的宽高尺寸,还能够管制视图之间的关系属性,透明度,3D,和他跟其余视图之间的可见性比照。 ...

December 12, 2020 · 6 min · jiezi

关于swift:Swift-界面路由

随着业务减少,我的项目中的模块越来越多,并且这些模块进行互相的调用,使得它们交缠在一起,减少了保护老本,并且会升高开发效率。此时就须要对整个我的项目进行模块划分,将这些模块划分给多个开发人员(组)进行保护,而后在主工程中对这些模块进行调用。 每个模块独立存在,提供接口供其余模块调用。从而如何无效且解耦的进行模块间的调用成了重中之重。 那么如何传递参数并且初始化对应模块的主窗口成为了次要问题。上面会介绍两种计划来解决这个问题。 计划1得益于Swift中枚举能够传参的个性,咱们能够通过枚举来进行参数传递,而后增加办法来映射控制器。 在枚举中通过参数来确定初始化控制器的数据,内部进行调用时能够很明确的晓得须要传入哪些参数,同时还能传递非常规参数(Data、UIImage等) enum Scene { case targetA case targetB(data: Data) func transToViewController() -> UIViewController { switch self { case .targetA: return ViewControllerA() case let .targetB(data): return ViewControllerB(data: data) } }}解决了UIViewController映射的问题后,咱们接下来解决如何对立跳转办法。 我的项目中,基本上都会应用UINavigationController来进行界面的跳转,次要波及push、pop操作。此时简便的做法是扩大UIViewController为其增加对立界面跳转办法。 extension UIViewController { func push(to scene: Scene, animated: Bool = true) { let scene = scene.transToViewController() DispatchQueue.main.async { [weak self] in self?.navigationController?.pushViewController(scene, animated: animated) } } func pop(toRoot: Bool = false, animated: Bool = true) { DispatchQueue.main.async { [weak self] in if toRoot { self?.navigationController?.popToRootViewController(animated: animated) } else { self?.navigationController?.popViewController(animated: animated) } } }最初咱们跳转界面时进行如下调用即可 ...

November 28, 2020 · 2 min · jiezi

关于swift:SwiftUI-中创建反弹动画

SwiftUI 中的动画在写动画之前呢先简略回顾一下 SwiftUI 中动画的几个要点: 动画是 view 发生变化时的突变成果SwiftUI 动画分为隐式动画(.animation())与显式动画(withAnimation())两种隐式动画是给 view 加动画,view 所有的能动画的变动都能被隐式动画影响显式动画是针对某个变动进行动画,能精准管制。view 的插入和移除通过过渡(transition)来做成果,能够组合多个过渡或自定义过渡要构建自定义动画,咱们须要实现一个可动画的 view 润饰器(恪守 AnimatableModier 协定)或者实现一个 GeometryEffect,并将可动画的属性通过 animatableData 裸露进去反弹动画反弹动画属于“起始点和终止点相等”的动画,所以不可能通过 SwiftUI 中内建的动画来实现(因为这个 view 从后果来看没有发生变化) 咱们先来构建反弹动画润饰器的框架如下: struct Bounce: AnimatableModifier { var animCount: CGFloat = 0 var amplitude: CGFloat = 10 // 振幅 var animatableData: CGFloat { get { animCount } set { animCount = newValue } } func body(content: Content) -> some View { // change view to animate }}上面一步一步来 ...

October 29, 2020 · 2 min · jiezi

关于swift:Swift-中的-Function-Builder-理解与运用

Function Builder 是 Swift 5.1 引入的个性,大大加强了 Swift 语言构建内置 DSL 的能力。SwiftUI 申明式 UI 构建形式就是靠的 DSL 实现的。 从 DSL 说起DSL 是 Domain Specific Language 的缩写,意思就是特定畛域的语言。与之对应的就是咱们相熟的 C, Java, Swift 这些通用的语言,通用语言什么畛域都能够去插一脚,无非就是适不适宜,好不好用罢了。DSL 则是局限在某个特定场景的特地设计过的语言,因为专一,所以业余,它们往往能以十分轻量级的语法和易于了解的形式来解决特定问题。 举几个驰名的 DSL 的例子: 正则表达式通过一些规定好的符号和组合规定,通过正则表达式引擎来实现字符串的匹配 HTML & CSS尽管写的是相似XML 或者 .{} 一样的字符规定,然而最终都会被浏览器内核转变成Dom树,从而渲染到Webview上 SQL诸如 create select insert 这种单词前面跟上参数,这样的语句实现了对数据库的增删改查一系列程序工作 那么这种语言内建的 DSL 有什么益处呢,咱们先来看一个 HTML 的界面搭建: <div> <p>Hello World!</p> <p>My name is KY!</p></div>在 UIKit 里要搭建上述界面很显著要麻烦很多: let container = UIStackView()container.axis = .verticalcontainer.distribution = .equalSpacinglet paragraph1 = UILabel()paragraph1.text = "Hello, World!"let paragraph2 = UILabel()paragraph2.text = "My name is KY!"container.addSubview(paragraph1)container.addSubview(paragraph2)这就是申明式 UI 与 命令式 UI 的区别,申明式 UI 用 DSL 来形容 “UI 应该是什么样子的”,命令式 UI 则须要先创立一个 View,再指定这个 View 的个性,再指定这个 View 要放到哪里,一步一步来,显得较为轻便。 ...

October 26, 2020 · 4 min · jiezi

关于swift:swift53-UIView-与-UIButton-点击事件传递参数

UIView 与 UIbutton 点击事件的参数传递;目前我通过 tag 解决了 UIButton 的点击参数传递设置 UIButton 的 tag,具体见代码 let playButton = UIButton(type: .custom)playButton.setTitle("开始播放", for: .normal)playButton.backgroundColor = UIColor(white: 1.0, alpha: 0.8)playButton.layer.cornerRadius = 17.5// 要害在这一行playButton.tag = index playButton.addTarget(self, action: #selector(playClicked(button:)), for: .touchUpInside)itemView.addSubview(playButton)接管@objc func playClicked(button: UIButton){ print(button.tag)}UIView 的点击事件与传输传递UIView 是没有点击事件这个货色的,不过咱们能够用 UITapGestureRecognizer 手势来解决 具体见代码 let itemView = UIView()itemView.isUserInteractionEnabled = trueitemView.tag = index // 传输传递// 创立手势let tap = UITapGestureRecognizer(target: self, action:#selector(tapClick(sender:)))// 增加到 UIView 上itemView.addGestureRecognizer(tap)homeMusicScrollView.addSubview(itemView)接管同样的情理 @objc func tapClick(sender: UIGestureRecognizer){ let itemView = sender.view! print(itemView.tag)}

October 15, 2020 · 1 min · jiezi

关于swift:swift53-UIColor-使用十六进制颜色

本文环境 Xcode 12Swift 5.3iOS 13UI 给出的色彩往往都是十六进制的,如 #1a1a1a 等,然而咱们在 iOS中是不能间接应用的,查问了一些代码,发现比拟老旧,这里给出一个改良版本 应用 Extension 扩大新建一个 swift 文件比方我的 string.swift ,复制以下代码 //// String.swift// bestWhiteNoise//// Created by 袁超 on 2020/10/10.//import Foundationimport UIKitextension String { /// 十六进制字符串色彩转为UIColor /// - Parameter alpha: 透明度 func uicolor(alpha: CGFloat = 1.0) -> UIColor { // 存储转换后的数值 var red: UInt64 = 0, green: UInt64 = 0, blue: UInt64 = 0 var hex = self // 如果传入的十六进制色彩有前缀,去掉前缀 if hex.hasPrefix("0x") || hex.hasPrefix("0X") { hex = String(hex[hex.index(hex.startIndex, offsetBy: 2)...]) } else if hex.hasPrefix("#") { hex = String(hex[hex.index(hex.startIndex, offsetBy: 1)...]) } // 如果传入的字符数量有余6位依照后边都为0解决,当然你也能够进行其它操作 if hex.count < 6 { for _ in 0..<6-hex.count { hex += "0" } } // 别离进行转换 // 红 Scanner(string: String(hex[..<hex.index(hex.startIndex, offsetBy: 2)])).scanHexInt64(&red) // 绿 Scanner(string: String(hex[hex.index(hex.startIndex, offsetBy: 2)..<hex.index(hex.startIndex, offsetBy: 4)])).scanHexInt64(&green) // 蓝 Scanner(string: String(hex[hex.index(startIndex, offsetBy: 4)...])).scanHexInt64(&blue) return UIColor(red: CGFloat(red)/255.0, green: CGFloat(green)/255.0, blue: CGFloat(blue)/255.0, alpha: alpha) }}应用比方 UI 给的色彩是 #5188e1, 那么咱们间接应用字符的扩大函数即可 ...

October 12, 2020 · 1 min · jiezi

关于swift:从现在开始可以使用Swift语言在Windows-10开发软件

Swift是苹果在2014年WWDC上发表的一种古代语言,它取代了Objective-C成为了苹果平台构建应用程序的次要语言。尔后,苹果对Swift进行了开源,此开源我的项目曾经为Ubuntu、CentOS和Amazon Linux发行版提供了该语言的工具,当然还有macOS,当初又增加了Windows。 Swif团队官网博客: 将Swift移植到Windows并不是简略地移植编译器,而是要确保整个生态系统在该平台上可用。这包含编译器、规范库和外围库(dispatch、Foundation、XCTest)。这些库是使开发者可能轻松编写弱小的应用程序的一部分,而不用放心底层零碎的许多细节。在将Swift带到Windows上可用状态的故事中,有很多技术细节,如果你对这些细节感兴趣,我会举荐你看看我在LLVM开发者大会上对于这个主题的演讲。有了这些外围库以及Swift与C语言的灵便互操作性,就能够在Windows上纯正应用Swift开发应用程序,同时利用Windows平台上现有的库群。 这篇博文持续演示了一个齐全用Swift编写的简略计算器利用。它是用Windows的Swift工具链,以及装置Visual Studio 2019创立的。 长期从事Mac和iOS开发的Readdle公司曾经在Windows上试验了一年多的Swift,作为其将Spark电子邮件客户端带到Windows平台上的致力的一部分。 Readdle示意:只管有些性能还没有筹备好,但Windows上的Swift后果齐全满足了他们的需要。事实上,一些第三方的C/C++依赖性比Swift自身更让他们头疼。Spark的所有业务逻辑都位于一个独立的Core模块中。其实是一包模块,但他们把它们称为Core。这使得他们能够在指标平台上应用任何UI框架。MacOS上的AppKit,iOS上的UIKit,Android上的原生UI Toolkit。所以,基本上,他们必须在Windows上移植Spark Core。在所有初始概念失去证实后,将其在Windows上用起来将是日常工作。 Windows版Swift工具链传送门:https://swift.org/download/

September 23, 2020 · 1 min · jiezi

关于swift:mac-OS-Big-Sur-11-Beta-5发行说明

mac OS Big Sur 11 Beta 5发行阐明更新您的应用程序以应用新性能,并针对API更改测试您的应用程序。 对于SwiftUI局部SwiftUI已知的问题ProgressView通用类型签名已更改,增加了通用参数。此更改不须要任何源更改,但会导致应用较早版本的Beta SDK编译的应用程序意外退出。(63580200)currentValueLabel解决办法:从新编译macOS Big Sur 11 Beta 5或更高版本的应用程序以解决此问题。 macOS Big Sur 11 beta 5中的新性能Color能够与转换CGColor。在当初也能够用绑定到配置。(56939085)ColorPickerCGColor作为将多个我的项目搁置在不可自定义工具栏的特定地位的一种便捷形式而引入。(64178863)ToolbarItemGroupProgressView当初反对增加辅助“以后值标签”来形容工作的以后进度级别。应用label来形容总体工作,并应用提供无关工作进度的更多具体细节。(63580200)currentValueLabelFileDocument 并更新了协定要求: 当初,他们的初始化程序要求只有一个参数,并且能够从中读取。FileDocumentReadConfiguration`fileWrapper`contentType他们write()本来心愿写入inout 参数的函数当初是返回a的函数。FileWrapperfileWrapper()`FileWrapper`Xcode中基于文档的应用程序模板已更新,以反映API中的此更改。与先前要求的源兼容性最终将被删除。(65146043)macOS Big Sur 11 beta 5中已解决的问题蕴含多色符号的图像当初将适应色彩外观。(63726389)Text(_ style:)不再截断。Text当初在小部件内具备灵便的宽度。(60588299)当初已按预期显示Text已利用修饰符的占位符款式的删节。(66346107)lineLimit(1)SwitchToggleStyle并且当初能够在macOS上进行着色。(62906674)LinearProgressViewStyleNavigationLink并在macOS上体现更为统一:不会因为抉择更改而将的抉择谬误地重置为;程序抉择和勾销抉择a 呈现按预期形式。(63791979)NavigationViewNavigationLink`nil`NavigationLink利用一个修改器当初将修复它的大小其内容的大小。(64855179)fixedSize())TextEditor当初已按预期显示Text已利用修饰符的占位符款式的删节。(66242496)minimumScaleFactor总览macOS 11 SDK反对为运行macOS Big Sur 11的Mac开发应用程序。该SDK与Xcode 12 beta 5捆绑在一起,可从Beta软件下载处取得。无关Xcode 12 beta 5兼容性要求的信息,请参阅Xcode 12 Beta 5发行阐明。 个别已知的问题重要 iMac以后不反对macOS Big Sur 11 beta 5(Retina 5K,27英寸,2020年)。如果您在iMac(Retina 5K,27英寸,2020)上遇到装置问题,则须要重新安装macOS Catalina。按住电源按钮强制重新启动,而后在关上Mac之后立刻按Command-R。而后,依照阐明从Recovery重新安装macOS Catalina。 重要 从以前的macOS版本升级到macOS Big Sur 11 beta可能要花费比预期更长的工夫。如果更新中断,则可能会产生数据失落。(59101197) 空投已知的问题如果启用了零碎完整性爱护,则可能无奈应用AirDrop。(67033173)利用商店在macOS Big Sur 11 beta 5中解决[](https://developer.apple.com/d...,在同一文件中导入和SwiftUI 时,能够拜访StoreKit符号。(66337479)StoreKitAppKit请参阅实用于macOS Big Sur 11的AppKit发行阐明。苹果平安赏金macOS Big Sur 11 beta 5被指定为有资格取得50%Apple Security Bounty奖金,以解决Apple未知或在此版本中从新引入的问题。无关更多信息,请参阅Apple平安赏金。端点平安在macOS Big Sur 11 beta 5中解决诸如透露内核内存之类的身份验证类型事件不再导致您的零碎无响应。(65750498)ES_EVENT_TYPE_AUTH_EXECFinal Cut Pro和iMovie已知的问题您目前无奈应用内置摄像头或外部设备捕捉视频。(63528489)应用“导入媒体”窗口时,应用程序可能会意外退出。(65543216)解决办法:将媒体从Finder中拖动以导入它。 ...

August 20, 2020 · 1 min · jiezi

关于swift:Xcode-12-Beta-5发行说明

疾速预览1、终于反对coredata模版创立了 2、已知解决,动画可能无奈在实时SwiftUI预览中应用3、Xcode 12 beta 5开始,在iOS 14上应用InsetGroupedListStyle不再会使在iOS 13上运行的应用程序解体,即便将其包装在版本查看中也是如此。解决方案_was_封装在AnyView中。 总览Xcode 12 beta 5包含实用于iOS 14,iPad OS 14,tvOS 14,watchOS 7和macOS 11的SDK。Xcode 12 beta 5版本反对针对iOS 9和更高版本,tvOS 9和更高版本以及watchOS 2和更高版本的设施上调试。 。Xcode 12 beta 5须要运行macOS Big Sur 11 beta或更高版本的Apple芯片,或者运行macOS Catalina 10.15.4或更高版本的基于Intel的Mac。 苹果Clang编译器弃用i386,x86_64和arm的独立零碎组装程序已弃用,并且可能在当前的Xcode版本中删除。将as命令与-Q标记一起应用会显示正告,批示您过渡到Clang的集成汇编器和-q标记。该as(1)驱动程序将持续,因为调用锵的综合汇编,以及由开发商装置的任何汇编的规范办法。(61299833)建设零碎弃用“构建设置”编辑器不再蕴含“无效体系结构”构建设置(),不倡议应用。而是有一个新的“排除的体系结构”构建设置()。如果我的项目包含,则该设置将显示在“构建设置”编辑器的“用户定义”局部中。(15145028)VALID_ARCHS`EXCLUDED_ARCHS`VALID_ARCHS旧版构建零碎已弃用,并将在当前的版本中删除。(62742902)外围ML弃用不举荐应用Xcode中主动生成的模型接口上的默认初始化程序,而举荐应用init(configuration:)。请改用init(configuration:)或新引入的.load()办法,并适当解决模型加载谬误。(62875309)调试解决从Xcode构建和运行时,Mac上的iPad和iPhone应用程序再次能够拜访按需资源。(62074124)内存图调试器当初能够正确地对以后Xcode工作区中SwiftUI应用程序中定义的类型的起源进行分类。调试导航器会在正确的局部列出这些类型,并在抉择“仅显示工作区中的内容”时将其包含在内。(63899779)解决了苹果芯片上的一个问题,该问题是在模仿设施上调试tvOS利用失败,并显示谬误“无奈附加到pid”。(65375566)已知的问题在运行iOS 14,iPadOS 14,watchOS 7或tvOS 14 beta 4及更高版本的设施上进行调试,测试和性能剖析须要Xcode 12 beta 3或更高版本。尝试在不受反对的操作系统版本上进行开发时,较早版本的Xcode可能会显示谬误“无奈启动近程服务”。(60850305)关上调试仪表时,Xcode可能解体。(64181692)仪器已知的问题插入macOS应用程序时,“动画挂钩”模板不会显示挂钩距离。(61082729)弃用instruments当初不举荐应用该命令,而倡议应用它替换:xctrace。xctrace记录,导入和导出Instruments .trace文件中的数据。(36641078)界面生成器解决UISplitViewController默认状况下,除非已连贯辅助视图控制器,否则Interface Builder中的实例将再次默认应用“未指定”款式。要利用iOS 14的改良,请从查看器的“款式”菜单中选择“双列”。(65966010)(FB8107534)修复了无奈拖动以将情节提要或.xib文件中的对象连接到现有Objective-C插座的问题。(66293812)修复了为蕴含关联视图的iOS文档启用平安区域布局指南时可能产生的解体。(64564818)当初,媒体库在SF Symbols的详细信息区域中显示已弃用的符号信息。(63692751)弃用Interface Builder不再提供对“能够同时绘制”属性的拜访。您仍能够在代码中应用来配置此行为。(42437767)canDrawConcurrentlyQTCaptureView和QTMovieView已被弃用,不再受反对。从情节提要和.xib文件中删除这些视图。(64263402)游乐场已知的问题Xcode可能会在创立后显示文本“ No Editor”,而不是立刻关上Playground的源代码编辑器。(56484197)解决办法:应用“视图”>“导航器”>“我的项目”显示“我的项目导航器”,而后手动抉择“游乐场”。 预告片解决蕴含多个预览的macOS中的Live SwiftUI预览再次是交互式的。(62156572)PreviewProvider对于作为应用程序和小部件链接的框架一部分的文件,SwiftUI预览更为牢靠。(63785700)从macOS 11开始,进步了Mac Catalyst实时预览的可靠性。(63998976)将Mac Catalyst的Live SwiftUI预览调出或显示时不再退出。(64151326)已知的问题Xcode不提供macOS小部件扩大的预览。(57990060)您无奈在“预览”画布的“窗口小部件”中抉择视图。(62517078)动画可能无奈在实时SwiftUI预览中应用。(63333795)进行更改之前,可能无奈显示macOS的Live SwiftUI预览,除非您单击画布上的Bring Forward按钮。(63865018)预览iPad设施的小部件扩展名可能会在顶部显示多余的空白栏。(64277772)利用外观修改器时,窗口小部件扩大预览不适宜深色外观。(64277915).preferredColorScheme(.dark)我的项目浏览器解决调整窗口大小后,“查找”面板仍会响应鼠标事件。(66256586)已知的问题即便MacOS不反对App Clip,App Clip计划也提供“ My Mac(为iPad设计)”或“ My Mac(为iPhone设计)”运行目的地。(65702469)事实作曲家弃用由Xcode 12创立的Reality文件只能在macOS 10.15.4或更高版本,iOS和iPadOS 13.4或更高版本以及Reality Composer 1.4或更高版本中加载。(58825031)签订和散发已知的问题签名为在macOS上本地运行的Mac Catalyst应用程序可能无奈在Mac(Rosetta)运行指标上运行。(64421496)利用剪辑无奈再通过“通行证类型ID”权力拜访电子钱包通行证。然而,App Clips能够确定电子钱包中是否曾经存在特定的通行证,并在必要时提醒用户增加通行证。如果您曾经将电子钱包性能增加到App Clip指标,则可能无奈构建或将利用提交到App Store Connect。在指标编辑器的“签名和性能”窗格中删除“电子钱包”性能,并禁用或删除任何应用此已删除性能的代码。(65244156)应用Ad Hoc或Development办法散发iOS存档时,Xcode不会验证主动签名生成的配置文件中是否蕴含具备Apple芯片的Mac。(66803918)仿真器已知的问题当在具备content-available键集的Simulator中模仿推送告诉时,零碎将调用而不是。(60426170,60974170)(FB7625283)application(_:performFetchWithCompletionHandler:)application(_:didReceiveRemoteNotification:fetchCompletionHandler:)即便Xcode Preferences容许您下载这些较早的运行时,iOS 13,tvOS 13和watchOS 6或更早版本的模拟器也不能在Developer Transition Kit上运行。带有苹果芯片的将来Mac将反对某些较旧的iOS和tvOS模拟器。(66115743)弃用在macOS 11中运行时,Simulator反对iOS 11.4或更高版本。(59938106)watchOS 6或更早版本的模拟器须要32位过程,而带有Apple芯片的Mac则不反对。(66352760)源代码编辑器解决修复了在启用某些扩展名或启用这些扩展名时可能在启动时产生的挂起。(61952790)已知的问题新的Xcode Source Editor Extension指标不会主动在扩大中设置嵌入。(59274389)XcodeKit.framework解决办法:手动嵌入扩大中。XcodeKit.framework ...

August 19, 2020 · 1 min · jiezi

关于swift:Swift枚举关联值的内存探究

enum Season { case Spring, Summer, Autumn, Winter}let s = Season.Spring这是枚举最根底的用法,然而在swift中,对枚举的性能进行了增强,也就是关联值。 关联值能够将额定信息附加到 enum case中,像上面这样子。 enum Test { case test1(v1: Int, v2: Int, v3: Int) case test2(v1: Int, v2: Int) case test3(v1: Int) case test4}let t = Test.test1(v1: 1, v2: 2, v3: 3) switch t {case .test1(let v1, let v2, let v3): print(v1, v2, v3)default: break}// 输入: 1 2 3咱们能够看到,在咱们创立一个枚举值t的时候,设置他的选项为test1,同时能够关联3个Int类型的值,而后在switch中,咱们还能够把这3个Int值取出来进行应用。 咱们明天的次要工作就是摸索一下有关联值的枚举类型,再底层的内存布局是什么样子的,这些值都是怎么贮存的。 在OC中咱们应用sizeOf此类办法,能够输入一个变量占用内存的大小,在swift中也有此类的工作类,那就是MemoryLayout。 print(MemoryLayout<Int>.size)// 理论应用内存大小print(MemoryLayout<Int>.stride)//分配内存大小print(MemoryLayout<Int>.alignment)//内存对其参数// 输入 8 8 8 下面的例子是只是简略的实例MemoryLayout的用法,这个咱们晓得,在64位的零碎中Int类型的确是占用8个字节(64位)。接下来咱们就看一下枚举的内存占用状况。 ...

August 18, 2020 · 2 min · jiezi

Swift-UI对Flutter的意义JSConf-2019归来记未来属于声明式编程丨体验科技精选第-4-期

这里是蚂蚁金服体验科技精选 第 4 期,本期内容包括原创精选、蚂蚁前端动态和行业动态,希望你喜欢! 蚂蚁前端动态Ant Design https://github.com/ant-design... 九月上旬,Ant Design 又招募到三位优秀的社区协作者@yoyo837 @shaodahong @orzyyyy ,相信其社区生态将会更加繁荣! TechUI https://www.yuque.com/yuque/h... TechUI 9 月上旬新增高级成员搜索组件以及标签设置模板。Kitchen 发布 2.16.0 新增设计资产功能模块,TechUI 设计资产即拖即用 原创精选从链式调用到管道组合 https://zhuanlan.zhihu.com/p/... 如果让你在不用 this 和原型链,不用 ES6 Generator/Iterator,不用箭头函数的前提下,实现「惰性求值」,你是不是想到使用链式调用实现?但初具规模之后,链式调用带来的问题该如何解决? 未来属于声明式编程 http://djyde.github.io/blog/d... 提升开发效率,我们应该去想如何尽量让开发者声明式地编写代码,而不是只去想我们在 Serverless 上能做什么。 行业动态JSConf 2019 归来记 https://www.yuque.com/zhenzis... 作者参加了 JSConf 2019, 归来后写下此文。本文将着重分享:TDCD、Make it boring、Web norms of the world、Tour de bikeshare 这四个 Talk,希望给大家带来一些信息和启发。 尤雨溪:在框架设计中寻求平衡 https://zhuanlan.zhihu.com/p/... 当你试图去设计一个框架时,最佳平衡点在哪?或许说是否存在一个完美的平衡点?它又是否是一个单一、完美的平衡点,甚至是以 JS 开发人员作为一个整体的最佳平衡点? MVVM框架的数据状态管理的发展与探索 https://github.com/farzer/blo... 前端应用日渐复杂,页面状态的可控制及可跟踪已成为开发和调试的重要手段,显然我们有必要了解状态管理方案可以解决什么问题,解决问题的核心方式是什么。 抖音研发实践 https://mp.weixin.qq.com/s/Dr... 基于二进制文件重排的解决方案,APP 启动速度提升超 15%。启动是 App 给用户的第一印象,对用户体验至关重要。为了保证抖音在业务迅速迭代情况下的高效启动,抖音 iOS 客户端团队做了大量优化工作及开拓性探索,将应用启动速度提高了约 15%。 ...

October 14, 2019 · 1 min · jiezi

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

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

October 8, 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

只有程序员才懂的痛

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

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

SwiftUI

1.1 基本模板介绍小贴士: 你可能会认为这一章完全可以跳过,但除非你是一个 Swift 的天才,否则你应该读到最后再确定。自 Xcode 11 之后,基本的 Single View App 模板为您提供以下内容: AppDelegate.swift 它负责监视外部事件,例如,如果另一个应用程序视图向您发送要打开的文件。SceneDelegate.swift 它负责管理应用程序的显示方式,例如让多个实例同时运行,或者当一个实例移动到后台时采取措施。ContentView.swift 这是我们的初始用户界面。如果这是一个 UIKit 项目,这将是 Xcode 提供给我们的 ViewController 类。Assets.xcassets 这是一个资产目录,存储着我们项目中使用的所有图像和颜色。LaunchScreen.storyboard 这是在您的应用加载时显示的屏幕。Info.plist 是一个属性列表文件,在本例中,它用于存储应用程序的系统范围设置 - 例如,在 iOS 主屏幕的图标下面应该显示什么名称。Preview content 一个名为 “预览内容” 的组,其中包含另一个名为 “预览资产” 的资产目录。就是这样 - 这是一个令人愉快的少量代码和资源,这意味着我们可以在此基础上进行构建。 我们真正关心的是 ContentView.swift , 事实上,这是唯一重要的部分。这是我们应用程序的主要功能,在这里我们可以立即开始尝试各种 SwiftUI 代码。 首先,是什么让 ContentView.swift 显示在屏幕上? 好吧,如果你还记得我说过 SceneDelegate.swift 负责管理你的应用程序的显示方式。那么,继续打开 SceneDelegate.swift ,您会看到下面的代码: let window = UIWindow(windowScene: windowScene)window.rootViewController = UIHostingController(rootView: ContentView())self.window = windowwindow.makeKeyAndVisible() 这段代码创建了一个新的 ContentView 实例(这是我们即将看到的主要功能),并将其放在 window 中,以便在屏幕上显示。它通过显示 ContentView 的第一个实例,可以有效地引导我们的应用程序,并从那里结束 - 你想做什么? ...

July 15, 2019 · 1 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

怎么做大发直属一手代理咨询843302

导师【筘:843302】那么多玩彩导师中,我是一个实事求是的、不夸大其词给你们画饼的导师!专业与否不是我说了算,是实力说话,我能带你回血稳赚,甚至捞第一桶金。这边我想跟彩友说的,计划方案是一方面,自己玩谁都不可能一直中,自认为会看走势盲目下注的都会死的很惨。计划不一定百分百中,谁都不是神人,我们讲究实事求是、以诚相待,不带忽悠的,怎么玩中奖率高,这是一个值得长期摸索探讨的话题,我有技巧你有需求。你们要清楚,有规律技巧的计划方案绝对比盲目下注中奖率高得多! 1、不单要学会赢还要懂得怎样去止损,定下个止损目标是为了帮助自己在失手的时候有个损失限度,不受到翻本情绪的影响导致自己更加的错误下去。这里就跟炒股一样,有止损就一定要有止赢,不要以为你赢那天你会一直赢下去,我告诉你,玩彩十个有九个都是一开始赢后面爆了帐户的。彩是每天都会有,不要急着打回来,今天输了,明天还有机会。今天挂了。改日再战! 2、稳赚方法技巧是有的,但我在这里说的稳赚是最少以一个星期为单位,短期暴利模式网上一搜一大堆,但都拼不过变态期,一把回到解放前很多人都经历过,快三方式很多接近稳赚,我告诉自己是稳赚,但我不敢这么跟大家说,因为没有什么事情是绝对的,何况堵搏。为什么我说很多方法都可行呢?因为我不是叫你从早上九点到晚上12点都玩,那什么方法都是会回到解放前的。抓住重要,几把就可以收工了,用得着这么麻烦吗?而且庄家最怕你什么?不怕你赢,就怕你赢了不玩。 3、止损与止赢的比例。我个人建设止损与止赢的比例定在1:1。就是说比如你帐号是1W的,你今天的目标是赢1千,那你一定要做到赢1千就收,同样的到你输1千的时候你也要收。不要跟我说拿1W只赢1千很小,不合理。我相信这么多人玩彩的没有多少人能做到平均每天25%利润,我估计99%的人都做不到,其实不是他们的技术做不到,是他们的心态做不到。而我可以带你们做得到回血本金25%以上! 我是一位真正可以教你们看懂规律,学懂下注技巧的导师。信任的第一步是自己跨出去的,机会也要自己把握住的,翻身致富的梦想还是要有的,兴许很快就实现了。我已经在这等你们了,想学真本事在自己手里,找我就对了,带你稳赚回血,不仅授之以鱼,关键的是授之以渔!彼此把握,你们会欣慰现在的选择,因为我有100%的用心和信心带你们稳赚回血!信人先信己,既然在观望,不如就试试,信赖了,一不小心,我就带你捞第一桶金,带你飞了呢!【筘:843302】

July 3, 2019 · 1 min · jiezi

UICollectionView-固定行距列表左排-来一个自定制-Layout

一般我们是使用 UICollectionViewFlowLayout , 熟悉的格子视图。也可以自定制 UICollectionViewLayout ,对于每一个列表元素,想放哪就放哪。 譬如: 固定行距列表左排 这种情况,系统的就不好直接拿来使了,需要自己定制一个 UICollectionViewLayout. 一般 new 一个 UICollectionViewLeftAlignedLayout, 继承自 UICollectionViewFlowLayout 通常要重写 UICollectionViewFlowLayout 的这两个方法, layoutAttributesForElements(in:), 这个方法需要提供,给定的矩形里面所有的格子的布局属性。给定的矩形区域,就是 UICollectioonView 的内容视图区域 contentSize. layoutAttributesForItem(at:): 这个方法需要提供,格子视图需要的具体的布局信息。我们要重写这个方法,返回要求的 indexPath 位置上格子的布局属性。 有时候也要重写这个属性: collectionViewContentSize, 一般我们是把内容区域的尺寸,作为计算属性处理的。他提供格子视图的内容区域的宽度与高度。格子视图的内容区域,不是格子视图的可见区域。 因为格子视图 UICollectionView,继承自 UIScrollView。 格子视图使用该属性,配置他作为可滑动视图 UIScrollView 的内容视图尺寸。 主要代码见如下: 其中辅助函数没有列出来,具体见文尾的 github repo. class UICollectionViewLeftAlignedLayout: UICollectionViewFlowLayout {// 这个函数没有做什么事情,主要是调用做事情的函数 layoutAttributesForItem,获取信息,提供出去 override func layoutAttributesForElements(in rect: CGRect) -> [UICollectionViewLayoutAttributes]? { var attributesCopy: [UICollectionViewLayoutAttributes] = [] if let attributes = super.layoutAttributesForElements(in: rect) { attributes.forEach({ attributesCopy.append($0.copy() as! UICollectionViewLayoutAttributes) }) } for attributes in attributesCopy { if attributes.representedElementKind == nil { let indexpath = attributes.indexPath // 做事情的地方 if let attr = layoutAttributesForItem(at: indexpath) { attributes.frame = attr.frame } } } return attributesCopy } // 这个函数里面,具体处理了固定行距列表左排的布局 override func layoutAttributesForItem(at indexPath: IndexPath) -> UICollectionViewLayoutAttributes? { if let currentItemAttributes = super.layoutAttributesForItem(at: indexPath as IndexPath)?.copy() as? UICollectionViewLayoutAttributes, let collection = collectionView { let sectionInset = evaluatedSectionInsetForItem(at: indexPath.section) let isFirstItemInSection = indexPath.item == 0 let layoutWidth = collection.frame.width - sectionInset.left - sectionInset.right // 让每一行的第一个元素排头,分两种情况处理。这是第一种,这个 section 的第一个元素,自然是排头。 guard !isFirstItemInSection else{ currentItemAttributes.leftAlignFrame(with: sectionInset) return currentItemAttributes } let previousIndexPath = IndexPath(item: indexPath.item - 1, section: indexPath.section) let previousFrame = layoutAttributesForItem(at: previousIndexPath)?.frame ?? CGRect.zero let previousFrameRightPoint = previousFrame.origin.x + previousFrame.width let currentFrame = currentItemAttributes.frame let strecthedCurrentFrame = CGRect(x: sectionInset.left, y: currentFrame.origin.y, width: layoutWidth, height: currentFrame.size.height) let isFirstItemInRow = !previousFrame.intersects(strecthedCurrentFrame) // 让每一行的第一个元素排头,分两种情况处理。这是第二种,这个 section 的其他的排头,算出来,就是:上一个格子在上一行,不在当前行, guard !isFirstItemInRow else{ currentItemAttributes.leftAlignFrame(with: sectionInset) return currentItemAttributes } // 剩下的,简单了。统一处理掉。 剩下的格子都不是排头,与上一个固定间距完了。 var frame = currentItemAttributes.frame frame.origin.x = previousFrameRightPoint + evaluatedMinimumInteritemSpacing(at: indexPath.section) currentItemAttributes.frame = frame return currentItemAttributes } return nil }// ...}说一下 func layoutAttributesForItem(at indexPath: IndexPath) 的设计思路。因为如果使用 UICollectionViewFlowLayout ,什么都不干,与上图的区别就一点。每一行的元素个数一致,具体也一致,就是那些格子是居中的。毕竟 minimumInteritemSpacing 是最小行内间距的意思,不是固定的行内间距。 ...

June 18, 2019 · 5 min · jiezi

实战iOSobjectivecswift静态代码分析

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

June 15, 2019 · 2 min · jiezi

swift中的声明关键字详解

原起学习swift,swift中的关键字当然要了解清楚了,最近在网上看到了关于声明关键字的文章,整理记录一下。 关键字是类似于标识符的保留字符序列,除非用重音符号(`)将其括起来,否则不能用作标识符。关键字是对编译器具有特殊意义的预定义保留标识符。常见的关键字有以下4种: 与声明有关的关键字:class、deinit、enum、extension、func、import、init、let、protocol、static、struct、subscript、typealias和var。与语句有关的关键字:break、case、continue、default、do、else、fallthrough、if、in、for、return、switch、where和while。表达式和类型关键字:as、dynamicType、is、new、super、self、Self、Type、__COLUMN__、__FILE__、__FUNCTION__和__LINE__。在特定上下文中使用的关键字:associativity、didSet、get、infix、inout、left、mutating、none、nonmutating、operator、override、postfix、precedence、prefix、rightset、unowned、unowned(safe)、unowned(unsafe)、weak和willSet。声明关键字一览图swift常见的声明关键字整理如下(不想看长文的,直接看下图即可) 声明关键字详解1、class在swift中,我们使用class关键字去声明一个类或者类方法。 class Person: NSObject { /// add `class` key word before function, this function become a class function class func work(){ print("everyone need work!") }}这样我们就声明了一个Person类。 2、letswift里有let关键字声明一个常量,及我们不可以对他进行修改。(注意:我们用let修饰的常量是一个类, 我们可以对其所在的属性进行修改) class iOSer: Person{ let name: String = "ningjianwen" var age: Int = 30 var height: Float = 170}let ITWork: iOSer = iOSer()ITWork.age = 25print("老子希望永远25岁")在iOSer类中let声明的name不可修改,var声明的age&height可以修改。同时let关键字声明的ITWork实例不可变,但是内部的var关键字声明的刷新是可以修改的。 3、varswift中var修饰的变量是一个可变的变量,可以对她的值进行修改。注意:我们不会用var去引用一个类, 也没有必要。 func iOSerClassFunction(){ let ITWork: iOSer = iOSer() ITWork.age = 25 print("老子希望永远\(ITWork.age)岁") let iOS1 = ITWork iOS1.age = 18 print("iOS1 age =\(iOS1.age)") print("ITWork age = \(ITWork.age)") } /** 打印结果 老子希望永远25岁 iOS1 age =18 ITWork age = 18 */从结果可以看出对iOS1的修改同样影响了ITWork,说明两个对象指向同一块内存空间。 ...

May 27, 2019 · 3 min · jiezi

swift开发中那些值得借鉴的写法

写在前面最近在学习swift,从github上下载很多demo进行学习,收获不小,发现了一些不错的写法,记录一下方便以后查询,同时分享给大家,共同成长。 UI相关的一些常量和辅助方法以下代码主要定义了一个swift工程中的UI部分的常量亮和定义,当然,这只是demo,正式工程可以按照这个思路进行扩展。一个XYUI结构体囊括了Screen、Color、Font三个子结构体,分别定义了屏幕、颜色、字体相关的常量和方法,结构清晰,方便后续扩展。 struct XYUI { struct Screen { static let Width : CGFloat = UIScreen.main.bounds.width static let Height : CGFloat = UIScreen.main.bounds.size.height static let NavH : CGFloat = XYUI.Screen.IphoneX == true ? 88 : 64 static let StatusbarH : CGFloat = XYUI.Screen.IphoneX == true ? 44 : 20 static let IphoneX: Bool = Int(XYUI.Screen.Height/XYUI.Screen.Width) == 216 //判断是否是iPhoneX序列 } // 颜色 struct Color { /// 主色调 static let primary = UIColor.hexString(color: "#FFCA07") static let black = UIColor.hexString(color: "#333333") static let white = UIColor.white } struct Font { static func fitFont(size:CGFloat) -> CGFloat { if UIScreen.main.bounds.size.width == 320 { return size * 0.8 } return size } static let f10 = UIFont.systemFont(ofSize: 10) static let f11 = UIFont.systemFont(ofSize: 11) static let f12 = UIFont.systemFont(ofSize: 12) static let f13 = UIFont.systemFont(ofSize: 13) static let f14 = UIFont.systemFont(ofSize: 14) static let f15 = UIFont.systemFont(ofSize: 15) static let f16 = UIFont.systemFont(ofSize: 16) static let f17 = UIFont.systemFont(ofSize: 17) static let f18 = UIFont.systemFont(ofSize: 18) static let f20 = UIFont.systemFont(ofSize: 20) }}关于cellIdentifier使用关于tableview和collectionView的cellIdentifier定义,在objective-c中,我之前是这样定义的: ...

May 21, 2019 · 2 min · jiezi

swift5展示全球国家列表

CountryCodeList是swift5学习的一个项目。主要练习了UITableView的swift使用,使用HandyJson把从本地读取的json文件转化为数据模型数组。全球国家列表的数据来源是之前从一张全球国家列表的sql表中使用Python洗出来的数据,经过处理之后形成了一个Json文件。 每一个国家对象主要包括了:国家的英文名、国家的缩写、国际区号。 全球国家列表已经进过精心整理成JSON文件,数据对于要做面向海外开发的同学还是有一定帮助的,可以直接使用。demo地址 demo结构概览:效果图:

May 12, 2019 · 1 min · jiezi

马蜂窝-iOS-App-启动治理回归用户体验

增长、活跃、留存是移动 App 的常见核心指标,直接反映一款 App 甚至一个互联网公司运行的健康程度和发展动能。启动流程的体验决定了用户的第一印象,在一定程度上影响了用户活跃度和留存率。因此,确保启动流程的良好体验至关重要。 「马蜂窝旅游」App 是马蜂窝为用户提供服务的主要阵地,其承载的业务模块不断丰富和完善,产品功能日趋复杂,已经逐渐成长为一个集合旅行信息、出行决策、自由行产品及服务交易的一站式移动平台。 「马蜂窝旅游」iOS App 历经几十个版本的开发迭代,在启动流程上积累了一定的技术债务。为了带给用户更流畅的使用体验,我们团队实施了数月的专项治理,也总结出一些 iOS 启动治理方面的实践经验,借由本文和大家分享。 0X0 如何定义「启动」要分析和解决启动问题,我们首先需要界定启动的内涵和边界,从哪开始、到哪结束,中间经历了哪些阶段和过程。以不同视角去观察时,可以得出不同结论。 技术视角App 启动原本就是程序启动的技术过程。作为开发人员,我们很自然地更愿意从技术阶段去看待和定义启动的流程。 App 启动的方式分为冷启动和热启动两种。简单来说,冷启动发生时后台是没有这个应用的进程的,程序需要从头开始,经过漫长的准备和加载过程,最终运行起来。而热启动则是在后台已有该应用进程的情况下发生的,系统不需要重新创建和初始化。因此,从技术视角讨论启动治理时,主要针对冷启动。 从技术视角出发,分析 iOS 的启动过程,主要分为两个阶段: pre-main: main() 函数是程序执行入口,从进程创建到进入 main 函数称为 premain 阶段, 主要包括了环境准备、资源加载等操作;post-main: main() 函数到-didFinishLaunchWithOptions:方法执行结束。该阶段已获得代码执行控制权,是我们治理的主要部分。<premain>                  <postmain>  +----------------X------------------------------------X--------->start             main                   -didFinishLaunchWithOptions:用户视角iOS App 是面向终端用户的产品,因此衡量启动的最终标准还是要从用户视角出发。 从用户视角定义启动,主要以用户主观视觉为依据,以页面流程为标准。这样看来,常见的 App 启动可以分为三个阶段: T1:闪屏页 闪屏页是启动过程中的静态展示页。在冷启动的过程中,App 还没有运行起来,需要经历环境准备和初始化的过程。这个过渡阶段需要展示一些视图,供阻塞等待中的用户浏览。iOS 系统 (SpringBoard) 根据 App Bundle 目录下的 Info.plist 中"Launch screen interface file base name"字段的值,找到所指定的 xib 文件,加载渲染展示该视图。闪屏页的展示是系统行为,因此无法控制;加载的是 xib 描述文件,无法定制动态展示逻辑,因此是静态展示。对应技术启动阶段的 pre-main 阶段T2(可选):欢迎页(广告) App 运行后根据特定的业务逻辑展示的第一个页面。常见的有广告页和装机引导流程。欢迎页是业务定制的,因此可根据业务需要优化展示策略,该阶段本身也是可选的。T3:目标页 (落地页)  App 启动的目标页。可以是首页或特定的落地页目标页的加载渲染渲染完成标志着 T3 阶段的结束,也标志着启动流程的结束。启动治理的最终目标是提升用户体验,在这样的思想下,本文关于启动流程的讨论主要围绕用户视角进行。 0X1 方法论及关键指标APM 方法论对 iOS 启动的治理,本质上是对应用性能优化 (App Performance Management) 的过程,其基本的方法论可以归纳为: ...

May 10, 2019 · 3 min · jiezi

Core-Data-的-Delete-Rule-详解

Core Data 是苹果原生自带的数据库管理框架,功能强大但使用起来也很复杂。在配置 Core Data 的 relationship 时有一个属性叫 Delete Rule。Delete Rule 表明了数据对象在被删除时,和他有 relationship 的其他数据对象的处理规则。 Deny除非有 relationship 的其他数据对象全部被删除,否则该数据对象将无法被删除。 你在撤销一个事业部门前,需要先将部门员工全部 fire。Cascade当你删除一个数据对象时,有 relationship 的其他数据对象将一并被自动删除。 你撤销了一个事业部门,该部门的员工在同一时间一并被 fire 。Nullify当你删除一个数据对象时,有 relationship 的其他数据对象的 relationship 指针将被设置为 null。 只有当 relationship 是 Optional 时,该设置才是有意义的。否则,你必须在删除前,为有 relationship 的数据对象手动设置新的 relationship。 No Action正如其名。在删除时,不做对 relationship 任何操作。 注意⚠️ 当你使用这种删除规则时,所有的 relationship 都需要你手动管理。

April 28, 2019 · 1 min · jiezi

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

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

April 26, 2019 · 1 min · jiezi

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

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

April 25, 2019 · 2 min · jiezi

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

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

April 22, 2019 · 1 min · jiezi

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

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

April 16, 2019 · 1 min · jiezi

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

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

April 15, 2019 · 1 min · jiezi

让UINavigationController更好用

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

April 12, 2019 · 1 min · jiezi

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

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

April 12, 2019 · 1 min · jiezi

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

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

April 11, 2019 · 1 min · jiezi

iOS开发常用设计模式

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

April 10, 2019 · 1 min · jiezi

如何为Xcode添加漂亮的主题

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

April 5, 2019 · 1 min · jiezi

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

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

April 3, 2019 · 1 min · jiezi

七牛云图床上传工具-iUpload

软件介绍: iUpload主要功能将图片上传至七牛云,返回 Markdown 格式的链接到剪粘板功能介绍:图片本地压缩图片右键上传图片复制上传图片拖拽上传https加密上传开发: 继承七牛云SDK,使用swift开发,App自签上传凭证,自动选择存储区域,通过https加密上传。下载: https://github.com/iChochy/iUpload/releases/download/1.0.1/App.dmg联系方系:邮箱:iChochy@qq.com网站:http://www.chochy.cnGitHub: https://github.com/iChochy/iUpload注: 处女作

April 3, 2019 · 1 min · jiezi

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

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

April 3, 2019 · 1 min · jiezi

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

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

April 1, 2019 · 1 min · jiezi

MVVM

原文链接目前客户端最流行的架构应该就是MVVM,然而在看了一些文章之后发现大部分是理论而并没有仔细讲解具体的架构方法和实践,这篇博客说说我在实际工作中的使用。引言提到MVVM我们不得不先来认识一下MVC:MVC模式(Model–view–controller)是软件工程中的一种软件架构模式,把软件系统分为三个基本部分:模型(Model)、视图(View)和控制器(Controller)。MVC模式最早由Trygve Reenskaug在1978年提出[1],是施乐帕罗奥多研究中心(Xerox PARC)在20世纪80年代为程序语言Smalltalk发明的一种软件架构。MVC模式的目的是实现一种动态的程序设计,使后续对程序的修改和扩展简化,并且使程序某一部分的重复利用成为可能。除此之外,此模式通过对复杂度的简化,使程序结构更加直观。软件系统通过对自身基本部分分离的同时也赋予了各个基本部分应有的功能。专业人员可以通过自身的专长分组:控制器(Controller)- 负责转发请求,对请求进行处理。视图(View) - 界面设计人员进行图形界面设计。模型(Model) - 程序员编写程序应有的功能(实现算法等等)、数据库专家进行数据管理和数据库设计(可以实现具体的功能)。MVVMMVVM是Model-View-ViewModel的简写,最早是由微软公司提出并运用,是MVP(Model-View-Presenter)模式与WPF结合的应用方式时发展演变过来的一种新型架构架构。MVVM有助于将图形用户界面的开发与业务逻辑或后端逻辑(数据模型)的开发分离开来,这是通过置标语言或GUI代码实现的。MVVM的视图模型是一个值转换器,这意味着视图模型负责从模型中暴露(转换)数据对象,以便轻松管理和呈现对象。在这方面,视图模型比视图做得更多,并且处理大部分视图的显示逻辑。视图模型可以实现中介者模式,组织对视图所支持的用例集的后端逻辑的访问。模型 模型是指代表真实状态内容的领域模型(面向对象),或指代表内容的数据访问层(以数据为中心)。视图 就像在MVC和MVP模式中一样,视图是用户在屏幕上看到的结构、布局和外观(UI)。视图模型 视图模型是暴露公共属性和命令的视图的抽象。MVVM没有MVC模式的控制器,也没有MVP模式的presenter,有的是一个绑定器。在视图模型中,绑定器在视图和数据绑定器之间进行通信。绑定器 声明性数据和命令绑定隐含在MVVM模式中。在Microsoft解决方案堆中,绑定器是一种名为XAML的标记语言。绑定器使开发人员免于被迫编写样板式逻辑来同步视图模型和视图。在微软的堆之外实现时,声明性数据绑定技术的出现是实现该模式的一个关键因素。MVVM的优点解决controller过于臃肿在MVC中很容易就会把一些业务逻辑,网络请求,数据IO都放在controller中 注意,这里不是说MVC的控制器一定很臃肿,而是「容易变得臃肿」 在我们新建一个工程的时候,苹果会自动帮我们生成一个ViewController,而在动手开始写代码的时候,往往控制不住就直接将逻辑写在Controller中。MVVM架构会要求我们把任何与非View的逻辑玻璃出来,Controller中除了绑定viewModel之外的代码只允许出现对View的操作。因为Controller对我们来说也只是一个View。逻辑分离就像上面说的,业务逻辑都会抽离出来放在viewModel中 ,这样可以在任何地方重用这一堆业务 除此之外,我们的代码将会更加易于测试,避免出现在MVC中可能出现的那种超长的方法、严重依赖全局状态导致难以测试的问题。View重用 (可拥有view单独的viewModel)在MVVM中,View只需要与ViewMode交互,不会收到其他的影响,所以不但vm、m 可以重用,view一样可以重复使用,修改的时候也更加方便。缺点BUG与传递由于在MVVM里面View和ViewModel是松耦合的,在测试出问题的时候就要排查各个地方的问题,有可能是vm中的也有可能是view中的。由于vm会传递数据,一个bug会很容易的传递到其他地方,引发更大的问题。并且其中一个地方出现问题的话,这个BUG就极有可能随着传递到其他的逻辑中,从而导致更严重的问题发生。需要维护Model,viewModel,和view的开销,controller相当于被抽象成View。额外的viewModel使用也并不是无代价的,有可能由于各种原因导致管理起来稍微复杂。而额外的,如果因为强引用或其他原因导致的循环引用等内存不能正确释放的情况下,有可能会内存疯涨,所以需要确保你的使用方式是无副作用的。使用方法上面说了这些只是一个大致的介绍,我们还是来看看应该怎样使用吧。你可以使用delegate的方式或者block的方式对view和viewModel进行桥接,在这里我们选择使用delegate,我认为这样看着比较直观,在代码中也更加明确。首先创建一个工程,选择singleViewApplication,我们就以最常见的的登陆功能作为示例新建用于由viewModel调用,view进行响应的protocol首先要起个名字,就叫LoginViewModelDelegateProtocol 吧@protocol LoginViewModelDelegateProtocol <NSObject>@end好,让我们想一想view会发送一些什么数据给VM ,VM都需要什么数据。对于简单登陆的VM来说,我们需要通知view的数据和方法登陆成功错误提示按钮状态改变(是否可以点击)那么我们可以在protocol中添加方法了@protocol LoginViewModelDelegateProtocol <NSObject>- (void)loginSuccess;- (void)showTips:(NSString *)tip;- (void)buttonEnable:(BOOL )enable;@end新建用于由view调用,viewModel进行响应的protocol同理,我们只要确认vm和v需要交换的数据就好了。用户名输入框的字符串密码输入框的字符串点击登陆事件@protocol LoginViewModelInterfaceProtocol <NSObject>- (void)inputUserName:(NSString *)uname;- (void)inputPwd:(NSString *)pwd;- (void)didTapLoginBUtton;@end实现代理方法下面我们新建一个viewModel叫做LoginViewModelLoginViewModel.h#import <Foundation/Foundation.h>#import “LoginViewModelDelegateProtocol.h”#import “LoginViewModelInterfaceProtocol.h”@interface LoginViewModel : NSObject<LoginViewModelInterfaceProtocol>@property (nonatomic ,weak) id<LoginViewModelDelegateProtocol> delegate;@endLoginViewModel.m#import “LoginViewModelDelegateProtocol.h”@interface LoginViewModel ()@property (assign, nonatomic) BOOL unameValid;@property (assign, nonatomic) BOOL pwdValid;@end@implementation LoginViewModel- (void)inputUserName:(NSString *)uname { self.unameValid = uname.length>0; [self judgeAllValid];}- (void)inputPwd:(NSString *)pwd { self.pwdValid = pwd.length>0; [self judgeAllValid];}- (void)didTapLoginBUtton { // 一些请求,这里忽略网络请求,直接模拟结果 [self.delegate loginSuccess];}- (void)judgeAllValid { BOOL v = [self isAllValid]; [self.delegate buttonEnable:v];}- (BOOL)isAllValid { return self.unameValid && self.pwdValid;}@end然后在controller初始化,并且实现全部的方法就可以了。viewController.m#import “viewController.h”…@interface viewController () <LoginViewModelDelegateProtocol>@property (nonatomic ,strong) LoginViewModel *vm;@end@implementation TDFSetPhoneNumController- (void)viewDidLoad { [super viewDidLoad]; self.vm.delegate = self;}#pragma mark - VMDelegate- (void)loginSuccess { [self.navigationController pushViewController:[SuccessVC new]] animated:true];}- (void)showTips:(NSString *)tip { [self showAlert:tip];}- (void)buttonEnable:(BOOL )enable { self.loginbutton.enable = enable;}#pragma mark - Getter- (LoginViewModel *)vm { if (!_vm) { _vm = [LoginViewModel new]; } return _vm;}@end大功告成这里全部的代码是我手写的,后面省略了一些UIKit相关的布局,想必以你的聪明才智应该已经能很轻松的将剩余的补全了吧。注意事项这里有几点我认为应该注意的:vm与v之间不论通过什么传递值和响应,都要保持数据的单向流动。代理用weak,内存回收时会自动置为nil,全部model与viewModel中不应包含任何UIKit框架下的类总结总而言之,我认为MVVM在我们的代码整体分工和应用架构的过程中应用还是十分优雅和安全的,不过话说回来什么架构也罢,还是要看我们怎么去用它,不是吗 ...

March 31, 2019 · 1 min · jiezi

iOS-International

原文链接随着项目越来越成熟,逐渐拓展到海外市场,我们就需要适配多种国际化和地区、需要对自己的产品进行国际化,让更多的用户可以使用我们的APP,这就需要对我们的产品进行国际化了。在这里就介绍一下自己在国际化项目里面踩过的一些坑。Project配置打开你的工程 , 在左侧栏选中project在打开的面板中选中project下的蓝色图标找到Localizations选项 , 添加你需要国家化的语言在主工程下command + N 新建Localizable.string文件选中你创建的string文件 , 打开右侧面板点击Localization组下的Localize按钮把你刚才配置在工程内的选项添加进去就好。调用方式在以前调用的地方我们需要替换为本地化调用方式比如以前我们是这么调用的:NSString tips = @“wait”;现在要换成NSString tips = NSLocalizedString(@“wait”, nil);(ps 第二个是为了方便翻译人员理解上下文语境使用的 。)然后在stirng文件内添加对应的字符串:“wait” = “别着急”;你可以在不同的文件添加对应不同语言的翻译 。让我们看下这个宏的定义 :#define NSLocalizedString(key, comment) \ [NSBundle.mainBundle localizedStringForKey:(key) value:@"" table:nil]#define NSLocalizedStringFromTable(key, tbl, comment) \ [NSBundle.mainBundle localizedStringForKey:(key) value:@"" table:(tbl)]#define NSLocalizedStringFromTableInBundle(key, tbl, bundle, comment) \ [bundle localizedStringForKey:(key) value:@"" table:(tbl)]#define NSLocalizedStringWithDefaultValue(key, tbl, bundle, val, comment) \ [bundle localizedStringForKey:(key) value:(val) table:(tbl)]可以看到实际上就是从mainBundle中取出了指定的String文件 , 然后根据我们在代码中定义的 ‘key’ 值取出value有的时候我们要指定我们的string文件名字而不使用上面的那个默认名字 。NSString tips = NSLocalizedStringFromTableInBundle(@“wait”, @“TDFOSLocalizable”, [NSBundle bundleForClass:[self class]], @“some tips to tell user wait”);这里面多出来两个参数,第一个是我们要指定的string文件名字,第二个就是要从哪个bundle中取,这个bundle的问题我们下面就会讲到。使用脚本替换工程内部调用方式那么在我们工程很大的情况下我们要把全部的字符串都替换为前缀为NSLocalizedString的形式人工手动替换肯定是不行的,又慢又不安全,没准复制粘贴的时候还把原来的key改错了,这里贴一段python的实例代码,可以稍加改动运行替换工程中的string前缀 。#!/usr/bin/env python3# -- coding: utf-8 --import osimport reimport sysimport datetimefolderPath = ‘/Users/felix/Documents/2dfire/TDFOpenShop/TDFOpenShop/Classes’stringToReplace = ‘NSLocalizedString’stringReplaceTo = ‘TDFOSLocalizedString’def scan_files(rootdir): files = [] for parent, dirnames, filenames in os.walk(rootdir): for filename in filenames: if len(filename.split(’.’,1))>1: if filename.split(’.’,1)[1] == ’m’: files.append(os.path.join(parent, filename)) return filesdef replaceStringIn(file): code = open(file,‘r+’) text = code.read() code.seek(0,0) code.write(text.replace(stringToReplace,stringReplaceTo)) code.close()def main(): files = scan_files(folderPath) for filePath in files: replaceStringIn(filePath)if name == ‘main’: main()这里面我是把以前的NS开头的宏替换为自定义的宏 , 大同小异 , 可以参照这个修改下即可 。编译运行测试当我们全部修改好以后肯定是要测试下显示对不对的、有两种方法可以快速切换App语言打开模拟器或真机 general -> setting -> lang ,不过这样比较繁琐。点击导航栏上的模拟器图标左边的schema选项,选择editSchema -> Run -> Options ->Application Language 直接改为你需要验证的语言运行就可以啦 。如何在模块内单独配置国际化文件很多时候我们的工程体量大到一定程度的时候都会模块化掉,几个人或者一个人负责一个模块,而有的模块是要作为SDK提供外部interface使用的 。当你把SDK提供出去的时候,我们期望的效果是在其他人(其他工程)内部运行的、我们的SDK也能够国际化显示 。但是在我们整个工程的string文件包含几千上万条的时候显然不需要全部移动到模块内 。所以在这里面我们要做的就是把需要的使用到的字符串和翻译好的value从主工程迁移到模块内部 。解决方案:使用cocoapods集成的模块化内部进行国际化我们先把主工程的国际化文件的文件夹复制到我们的模块路径下面,在模块内部有一个配置文件 x.podspec 里面添加这样一段配置s.resources = ‘xxx/.{xib,jpg,png,xcassets}’,‘xxx/.lproj’主要注意后面 , 这里就把我们刚复制来的国际化问价加入了模块的资源中 ,这时 ,如果我们不想用跟主工程一样的名字 ,Localizable.strings ,可以把这个名字也给改掉 , 比如 abc.strings调用这里为了有别于主工程的国际化 , 我们把strings文件改名为 abc.strings 然后在模块内新建一个头文件#ifndef TDFOSLocalizeMacro_h#define TDFOSLocalizeMacro_h#define TDFOSLocalizedString(key, comment) \NSLocalizedStringFromTableInBundle(key, @“abc”, [NSBundle bundleForClass:[self class]], comment)#endif / TDFOSLocalizeMacro_h */这里可以使用上面的脚本再次替换字符串前缀,然后运行下看看是否替换成功。这里注意要重新pod install下,否则cocoapods不会帮我们把资源引入包内 。ps: 别忘了删掉主工程中移除来的字符串 !!!总结基本用法暂时介绍到这里 , 关于模块化和国际化的东西要多了解一下NSbundle这个类 。 ...

March 31, 2019 · 1 min · jiezi

GCD(Swift)

原文链接这篇文主要想总结下多线程在swift中的使用,先看下基本概念进程进程指在系统中能独立运行并作为资源分配的基本单位,它是由一组机器指令、数据和堆栈等组成的,是一个能独立运行的活动实体线程线程是进程的基本执行单元,一个进程(程序)的所有任务都在线程中执行。队列队列,又称为伫列(queue),是先进先出(FIFO, First-In-First-Out)的线性表。在具体应用中通常用链表或者数组来实现。队列只允许在后端(称为rear)进行插入操作,在前端(称为front)进行删除操作。队列的操作方式和堆栈类似,唯一的区别在于队列只允许新数据在后端进行添加。同步/异步可以这么理解:(知乎)假如你要做两件事 , 烧水 、 刷牙同步 :你烧水 , 等水烧开了你再去刷牙异步 :你烧水 ,不等水烧开就去刷牙了 , 水烧开了会发出声音告诉你(callback) , 然后你再处理水烧开之后的事情只要你是个正常人 , 都会选择第二种 , 当然也有特殊情况 , 那个人喜欢用热水刷牙并发指两个或多个事件在同一时间间隔内发生。可以在某条线程和其他线程之间反复多次进行上下文切换,看上去就好像一个CPU能够并且执行多个线程一样。其实是伪异步。线程队列中并行/串行串行队列:串行队列的特点是队列内的线程是一个一个执行,直到结束。并行队列:并行队列的特点是队列中所有线程的执行结束时必须是一块的,队列中其他线程执行完毕后,会阻塞当前线程等待队列中其他线程执行,然后一块执行完毕。如何使用DispatchQueue.global().async { print(“do something in global (Thread.current)”) DispatchQueue.main.async { print(“do something in main (Thread.current)”) }}这里使用了全局的队列执行一些任务 , 然后切回主队列 , 这里要注意主队列是运行在主线程上的任务堆栈 。自定义队列除了使用全局队列外我们还可以使用自定义的队列let q = DispatchQueue(label: “com.felix.felix”)初始化一个队列最简单的方式就是声明它的标签 。async打开Xcode,新建一个commandLineTool工程、打开main.swiftlet q = DispatchQueue(label: “com.felix.felix”)q.sync { (1…5).forEach({ i in print("???? (Thread.current) + (i)") })}q.async { (6…10).forEach({ i in print("???? (Thread.current) + (i)") })}(11…15).forEach({ i in print("???? (Thread.current) + (i)")})先声明一个队列,使用sync添加一个同步的任务输出1到5,使用async异步输出6到10,同时在主线程打印11到15 。按下command+R运行project,???? <NSThread: 0x103103480>{number = 1, name = main} + 1???? <NSThread: 0x103103480>{number = 1, name = main} + 2???? <NSThread: 0x103103480>{number = 1, name = main} + 3???? <NSThread: 0x103103480>{number = 1, name = main} + 4???? <NSThread: 0x103103480>{number = 1, name = main} + 5???? <NSThread: 0x103103480>{number = 1, name = main} + 11???? <NSThread: 0x103007940>{number = 2, name = (null)} + 6???? <NSThread: 0x103103480>{number = 1, name = main} + 12???? <NSThread: 0x103007940>{number = 2, name = (null)} + 7???? <NSThread: 0x103103480>{number = 1, name = main} + 13???? <NSThread: 0x103007940>{number = 2, name = (null)} + 8???? <NSThread: 0x103103480>{number = 1, name = main} + 14???? <NSThread: 0x103007940>{number = 2, name = (null)} + 9???? <NSThread: 0x103103480>{number = 1, name = main} + 15???? <NSThread: 0x103007940>{number = 2, name = (null)} + 10Program ended with exit code: 0我们可以看到,????代表的任务全部是优先执行的,这说明sync添加的任务会阻塞当前线程,在看到????和????是均匀分部的,这是由于async添加的任务会默认加入由系统管理的线程池,异步执行 。优先级 QoS当多个队列同时执行的时候,系统需要知道哪个队列优先级更高,才能优先安排计算资源给他,我们可以这样定义优先级:let q = DispatchQueue(label: “com.felix.felix”, qos: DispatchQoS.background)初始化的时候加上qos参数 , qos(quality of service)从字面上理解就是「服务质量」,在swift中是这样定义的:public enum QoSClass { @available(OSX 10.10, iOS 8.0, *) case background @available(OSX 10.10, iOS 8.0, *) case utility @available(OSX 10.10, iOS 8.0, *) case default @available(OSX 10.10, iOS 8.0, *) case userInitiated @available(OSX 10.10, iOS 8.0, *) case userInteractive case unspecified @available(OSX 10.10, iOS 8.0, *) public init?(rawValue: qos_class_t) @available(OSX 10.10, iOS 8.0, *) public var rawValue: qos_class_t { get } }User Interactive: 和用户交互相关,比如动画等等优先级最高。比如用户连续拖拽的计算User Initiated: 需要立刻的结果,比如push一个ViewController之前的数据计算Utility: 可以执行很长时间,再通知用户结果。比如下载一个文件,给用户下载进度。Background: 用户不可见,比如在后台存储大量数据在选择优先级时可以参考如下判断 。这个任务是用户可见的吗?这个任务和用户交互有关吗?这个任务的执行时间有多少?这个任务的最终结果和UI有关系吗?并发队列默认情况下添加进Queue的任务会串行执行 , 先执行完一个再执行下一个:import Foundationlet q = DispatchQueue(label: “com.felix.felix”)q.async { (1…5).forEach({ i in print("???? (Thread.current) + (i)") })}q.async { (6…10).forEach({ i in print("???? (Thread.current) + (i)") })}(11…15).forEach({ i in print("???? (Thread.current) + (i)")})运行看下日志输出???? <NSThread: 0x102a081a0>{number = 2, name = (null)} + 1???? <NSThread: 0x100f046f0>{number = 1, name = main} + 11???? <NSThread: 0x102a081a0>{number = 2, name = (null)} + 2???? <NSThread: 0x100f046f0>{number = 1, name = main} + 12???? <NSThread: 0x102a081a0>{number = 2, name = (null)} + 3???? <NSThread: 0x100f046f0>{number = 1, name = main} + 13???? <NSThread: 0x102a081a0>{number = 2, name = (null)} + 4???? <NSThread: 0x100f046f0>{number = 1, name = main} + 14???? <NSThread: 0x102a081a0>{number = 2, name = (null)} + 5???? <NSThread: 0x100f046f0>{number = 1, name = main} + 15???? <NSThread: 0x102a081a0>{number = 2, name = (null)} + 6???? <NSThread: 0x102a081a0>{number = 2, name = (null)} + 7???? <NSThread: 0x102a081a0>{number = 2, name = (null)} + 8???? <NSThread: 0x102a081a0>{number = 2, name = (null)} + 9Program ended with exit code: 0我们可以看到直到????都输出完毕才会输出????,有时候我们想把任务并行执行,怎么办呢。可以设置queue的Attributes。let q = DispatchQueue(label: “com.felix.felix”, attributes: DispatchQueue.Attributes.concurrent)再运行下看看会怎样。DispatchWorkItem有的时候,对于同一个操作我们有可能会放在不同的线程中去执行,这样我们就可以把这个操作用DispatchWorkItem的形式包裹起来,在不同的线程中执行 。import Foundationlet group = DispatchGroup()let q = DispatchQueue(label: “com.felix.felix”, attributes: DispatchQueue.Attributes.concurrent)let item1 = DispatchWorkItem { (1…5).forEach({ i in print("???? (Thread.current) + (i)") })}let item2 = DispatchWorkItem { (6…10).forEach({ i in print("???? (Thread.current) + (i)") })}q.async(execute: item1)q.async(execute: item2)(11…15).forEach({ i in print("???? (Thread.current) + (i)")})Group 队列组DispatchGroup 可以用来管理一组队列,监听所有队列的所有任务都完成的通知,比较常用的就是在一个页面请求多个接口的时候,全部请求完再刷新UI 。总结总之,使用GCD一方面会提升我们应用的性能,给用户带来更好的体验,不过一定要注意线程安全问题。 ...

March 31, 2019 · 3 min · jiezi

Swift+Vapor开发一个简易区块链

原文链接Code In -> REPO最近身边的许多人都开始玩比特币,虽然本人不炒但是想稍微了解一下其中的原理,所以就练手写了一个简易版的区块链系统。So 、 What is the BlockChain (区块链) ?这里引用一下Google的结果所谓区块链技术 , 简称BT(Blockchain technology),也被称之为分布式账本技术,是一种互联网数据库技术,其特点是去中心化、公开透明,让每个人均可参与数据库记录。Base (基础概念)交易(Transaction):一次操作,导致账本状态的一次改变,如添加一条记录;区块(Block):记录一段时间内发生的交易和状态结果,是对当前账本状态的一次共识;链(Chain):由一个个区块按照发生顺序串联而成,是整个状态变化的日志记录。如果把区块链作为一个状态机,则每次交易就是试图改变一次状态,而每次共识生成的区块,就是参与者对于区块中所有交易内容导致状态改变的结果进行确认。简单理解就是:如果我们把数据库假设成一本账本,读写数据库就可以看做一种记账的行为,区块链技术的原理就是在一段时间内找出记账最快最好的人,由这个人来记账,然后将账本的这一页信息发给整个系统里的其他所有人。这也就相当于改变数据库所有的记录,发给全网的其他每个节点,所以区块链技术也称为分布式账本(distributed ledger)。Vapor (用来开发服务端的Swift框架)既然要用swift实现 , 我在这里就选择vapor作为服务端框架来使用 , vapor里面有意思的东西很多 , 这里只介绍基本的操作而不深究其原理 。Install前置条件 , 这里我们使用macOS进行开发部署 , 以下是需要软件和版本。Install Xcode 9.3 or greater from the Mac App Store.Vapor requires Swift 4.1 or greater.Vapor Toolbox: 3.1.7Vapor Framework: 3.0.8homeBrew接着使用homeBrew安装brew install vapor/tap/vapor如果一切输出正常的话我们就可以继续啦。Get Started现在vapor已经装好了 , 我们可以先把基本的准备工作弄好使用vapor初始化工程vapor new blockChainServer生成工程文件vapor xcode接着打开 blockChainServer.xcodeproj 文件 , 在导航上的schema上选择 run ,接着按下 Command + R这时你应能够在控制台看到输出的server地址了Practice Begin到现在我们一切准备工作都就绪了 , 那么开始鼓捣个区块链api出来吧 。Base Model区块链的基本概念上面介绍了,包含以下几类,我们使用oop可以抽象出以下一些classTransaction 交易Block 区块Blockchain 区块链BlockChainNode 区块链节点排列由下向上存在集合关系。 其实这里面最后应该加上一个区块网络不过这里我们暂时不需要实现全部网络,这里我们先搭建一个内网环境的来练练手下面分别来看下几个model的代码(ps:这里的框架添加的协议和扩展很多,不在此过多介绍,请把关注点放在class本身)Transaction.swiftimport Foundationimport FluentSQLiteimport Vaporfinal class Transaction: Codable,SQLiteModel { var id: Int? var from: String var to: String var amount: Double init(from: String, to: String, amount: Double) { self.from = from self.to = to self.amount = amount }}extension Transaction: Content { }extension Transaction: Migration { }extension Transaction: Parameter { }这里我们可以看到定义了几个property ,id是SQLiteModel协议需要实现的 , 这里只要记住是为了数据持久化就好to : 交易的接收方from : 交易的发起方amount : 金额Block.swiftimport FluentSQLiteimport Vaporfinal class Block: Codable,SQLiteModel { var id: Int? var index: Int = 0 var dateCreated: String var previousHash: String! var hash: String! var nonce: Int var message: String = "" private (set) var transactions: [Transaction] = Transaction var key: String { get { let transactionsData = try! JSONEncoder().encode(transactions) let transactionsJSONString = String(data: transactionsData, encoding: .utf8) return String(index) + dateCreated + previousHash + transactionsJSONString! + String(nonce) } } @discardableResult func addTransaction(transaction: Transaction) -> Block{ transactions.append(transaction) return self } init() { dateCreated = Date().toString() nonce = 0 message = “挖出新的区块” } init(transaction: Transaction) { dateCreated = Date().toString() nonce = 0 addTransaction(transaction: transaction) }}extension Block: Content { }extension Block: Migration { }extension Block: Parameter { }index : 区块序号dateCreated : 创建日期previousHash : 前一个区块的哈希值hash : 当前区块的哈希值nonce : 先记住和工作量证明有关message : 这里是为了我们看到输出Blockchain.swiftimport Foundationimport FluentSQLiteimport Vaporfinal class Blockchain: Codable,SQLiteModel { var id: Int? var blocks: [Block] = Block init() { } init(_ genesisBlock: Block) { self.addBlock(genesisBlock) } func addBlock(_ block: Block) { if self.blocks.isEmpty { // 添加创世区块 // 第一个区块没有 previous hash block.previousHash = “0” } else { let previousBlock = getPreviousBlock() block.previousHash = previousBlock.hash block.index = self.blocks.count } block.hash = generateHash(for: block) self.blocks.append(block) block.message = “此区块已添加至区块链” } private func getPreviousBlock() -> Block { return self.blocks[self.blocks.count - 1] } private func displayBlock(_ block: Block) { print("—— 第 (block.index) 个区块 ——–") print(“创建日期:(block.dateCreated)”) // print(“数据:(block.data)”) print(“Nonce:(block.nonce)”) print(“前一个区块的哈希值:(block.previousHash!)”) print(“哈希值:(block.hash!)”) } private func generateHash(for block: Block) -> String { var hash = block.key.sha1Hash() // 设置工作量证明 while(!hash.hasPrefix(“11”)) { block.nonce += 1 hash = block.key.sha1Hash() print(hash) } return hash }}extension Blockchain: Content { }extension Blockchain: Migration { }extension Blockchain: Parameter { }BlockChainNode.swiftimport FluentSQLiteimport Vaporfinal class BlockChainNode: Codable,SQLiteModel { var id: Int? var address :String init(addr:String) { address = addr }}extension BlockChainNode: Content { }extension BlockChainNode: Migration { }extension BlockChainNode: Parameter { }address : 节点地址Action这里涉及到的事件主要是计算hash , 我们在这里面给String 添加一个extensionextension String { func sha1Hash() -> String { let task = Process() task.launchPath = “/usr/bin/shasum” task.arguments = [] let inputPipe = Pipe() inputPipe.fileHandleForWriting.write(self.data(using: .utf8)!) inputPipe.fileHandleForWriting.closeFile() let outputPipe = Pipe() task.standardOutput = outputPipe task.standardInput = inputPipe task.launch() let data = outputPipe.fileHandleForReading.readDataToEndOfFile() let hash = String(data: data, encoding: .utf8)! return hash.replacingOccurrences(of: " -\n", with: “”) }}给date也添加一个便于我们输出区块创建时间extension Date { func toString() -> String { let formatter = DateFormatter() formatter.dateFormat = “yyyy-MM-dd HH:mm:ss” return formatter.string(from: self) }}Service在这里我们把所有对区块链的操作抽象为一个service类BlockchainService.swiftimport Foundationimport Vaporclass BlockchainService { private var blockchain: Blockchain = Blockchain() private var nodes = BlockChainNode init() { } func addBlock(_ block: Block) -> Block { self.blockchain.addBlock(block) return block } func getLastBlock() -> Block { guard let lastB = self.blockchain.blocks.last else { return addBlock(Block()) } return lastB; } func getBlockchain() -> Blockchain { return self.blockchain } func registerNode(_ node:BlockChainNode) -> BlockChainNode { self.nodes.append(node) return node } func getAllNodes() -> [BlockChainNode] { return nodes }}Controller & Router这里我们基本完成了所有基本模型的搭建 , 现在需要对我们的server进行操作 , 让我们可以方便的通过curl调用我们的区块链系统 。BlockChainController.swiftimport Foundationimport Vaporstruct BCError:LocalizedError { let name:String}final class BlockChainController { let bcService = BlockchainService() func addBlock(_ req: Request) throws -> Future<Block> { return bcService.addBlock(Block()).save(on: req) } func addTransaction(_ req: Request) throws -> Future<Block> { return try req.content.decode(Transaction.self).flatMap{ transation in return self.bcService.getLastBlock().addTransaction(transaction: transation).save(on: req) } } func findBlockChain(_ req: Request) throws -> Future<Blockchain> { return bcService.getBlockchain().save(on: req) } func registeNode(_ req: Request) throws -> Future<BlockChainNode> { return try req.content.decode(BlockChainNode.self).flatMap{ node in return self.bcService.registerNode(node).save(on: req) } } func allNodes(_ req: Request) throws -> Future<[BlockChainNode]> { return BlockChainNode.query(on: req).all() } func resolve(_ req: Request) throws -> Future<Blockchain> { let promise = req.eventLoop.newPromise(Blockchain.self) bcService.getAllNodes().forEach { node in guard let url = URL(string: “http://(node.address)/blockchain”) else {return promise.fail(error: BCError(name: “node error”))} URLSession.shared.dataTask(with: url, completionHandler: { (data, _, ) in if let data = data { guard let bc = try? JSONDecoder().decode(Blockchain.self, from: data) else {return promise.fail(error: BCError(name: “json error”))} if self.bcService.getBlockchain().blocks.count < bc.blocks.count { self.bcService.getBlockchain().blocks = bc.blocks } promise.succeed(result: self.bcService.getBlockchain()) } else { promise.fail(error: BCError(name: “data Error”)) } }).resume() } return promise.futureResult }}routes.swiftimport Vapor/// Register your application’s routes here.public func routes( router: Router) throws { // Basic “Hello, world!” example router.get(“hello”) { req in return “Hello, world!” } let bcc = BlockChainController() router.post(“block”, use: bcc.addBlock) router.post(“transaction”, use: bcc.addTransaction) router.get(“blockchain”, use: bcc.findBlockChain) router.post(“node”, use: bcc.registeNode) router.get(“node”, use: bcc.allNodes) router.post(“resolve”, use: bcc.resolve)}现在解释一下我们注册的api都是干啥的ps:API层遵守restfullpost(“block”, use: bcc.addBlock) 增加一个区块post(“transaction”, use: bcc.addTransaction) 新增一笔交易get(“blockchain”, use: bcc.findBlockChain) 查询区块链post(“node”, use: bcc.registeNode) 增加节点get(“node”, use: bcc.allNodes) 获取全部节点post(“resolve”, use: bcc.resolve) 解决冲突Example下面我们可以让服务运行起来 , 然后通过curl命令行来调用区块链系统 ,编译工程进入我们的工程目录 , 执行命令vapor build你应能看到以下输出Building Project [Done]这说明我们的工程可以运行了运行工程vapor run serve –port=8080我们在本地8080端口上开启我们的服务这时应能看到如下输出Running blockChainServer …[ INFO ] Migrating ‘sqlite’ database (/Users/felix/Documents/TestFolder/blockChainServer/.build/checkouts/fluent.git-6251908308727715749/Sources/Fluent/Migration/MigrationConfig.swift:69)[ INFO ] Preparing migration ‘Todo’ (/Users/felix/Documents/TestFolder/blockChainServer/.build/checkouts/fluent.git-6251908308727715749/Sources/Fluent/Migration/Migrations.swift:111)[ INFO ] Preparing migration ‘Block’ (/Users/felix/Documents/TestFolder/blockChainServer/.build/checkouts/fluent.git-6251908308727715749/Sources/Fluent/Migration/Migrations.swift:111)[ INFO ] Preparing migration ‘Blockchain’ (/Users/felix/Documents/TestFolder/blockChainServer/.build/checkouts/fluent.git-6251908308727715749/Sources/Fluent/Migration/Migrations.swift:111)[ INFO ] Preparing migration ‘BlockChainNode’ (/Users/felix/Documents/TestFolder/blockChainServer/.build/checkouts/fluent.git-6251908308727715749/Sources/Fluent/Migration/Migrations.swift:111)[ INFO ] Migrations complete (/Users/felix/Documents/TestFolder/blockChainServer/.build/checkouts/fluent.git-6251908308727715749/Sources/Fluent/Migration/MigrationConfig.swift:73)[Deprecated] –option=value syntax is deprecated. Please use –option value (with no =) instead.Server starting on http://localhost:8080现在我们只要用api调用以下 , 本机的区块链系统就会响应了 , 让我们试一下吧创建区块curl -s -X POST localhost:8080/block你会在一段时间后看到响应{ “dateCreated”: “2018-08-11 17:19:01”, “hash”: “1124aa2a5867abee8b9cc3a3f4051b6665f89e26”, “id”: 1, “index”: 0, “message”: “\u6b64\u533a\u5757\u5df2\u6dfb\u52a0\u81f3\u533a\u5757\u94fe”, “nonce”: 193, “previousHash”: “0”, “transactions”: []}我们的第一个block已经创建成功并且被加入区块链里了 , 他的previousHash为“0”是因为他是创世区块 。添加交易我们可以在这里添加几笔交易看看curl -s -X POST localhost:8080/transaction –data “from=Felix&to=mayun&amount=100” | python -m json.tooldata里面表示 Felix向mayun转账100 每次添加后你将会得到区块的最新信息{ “dateCreated”: “2018-08-11 17:19:01”, “hash”: “1124aa2a5867abee8b9cc3a3f4051b6665f89e26”, “id”: 1, “index”: 0, “message”: “\u6b64\u533a\u5757\u5df2\u6dfb\u52a0\u81f3\u533a\u5757\u94fe”, “nonce”: 193, “previousHash”: “0”, “transactions”: [ { “amount”: 100, “from”: “Felix”, “to”: “mayun” } ]}查询链我们可以重复以上操作几次,然后查看整个区块链信息curl -s -X GET localhost:8080/blockchain | python -m json.tool会看到如下输出{ “blocks”: [ { “dateCreated”: “2018-08-11 17:19:01”, “hash”: “1124aa2a5867abee8b9cc3a3f4051b6665f89e26”, “id”: 1, “index”: 0, “message”: “\u6b64\u533a\u5757\u5df2\u6dfb\u52a0\u81f3\u533a\u5757\u94fe”, “nonce”: 193, “previousHash”: “0”, “transactions”: [ { “amount”: 100, “from”: “Felix”, “to”: “mayun” }, { “amount”: 100, “from”: “Felix”, “to”: “mayun” } ] }, { “dateCreated”: “2018-08-11 17:27:39”, “hash”: “11bec5f7bf8226c62119adfbb03ad37d24267092”, “id”: 2, “index”: 1, “message”: “\u6b64\u533a\u5757\u5df2\u6dfb\u52a0\u81f3\u533a\u5757\u94fe”, “nonce”: 277, “previousHash”: “1124aa2a5867abee8b9cc3a3f4051b6665f89e26”, “transactions”: [ { “amount”: 100, “from”: “Felix”, “to”: “mayun” }, { “amount”: 100, “from”: “Felix”, “to”: “mayun” } ] } ], “id”: 1}我们可以看到Felix不停的向mayun转100块 , 真不要脸 。节点以及解决冲突区块链同时存在与多个主机上,也就是说会有很多的block-chain-server运行,而对于不同的运算会有多个解产生,这就是冲突问题了,那么我们看下冲突解决的过程首先,我们在8081端口同时开启一个服务vapor run serve –port=8081然后添加几个区块和交易,因为我们要验证解决冲突,所以应该比运行在8080端口上的区块多。curl -s -X POST localhost:8081/block | python -m json.tool重复几次操作 , 最后看下8081伤的blockchain{ “blocks”: [ { “dateCreated”: “2018-08-11 17:35:15”, “hash”: “1152cb1aac50abd803a4589f28c7e054db207e23”, “id”: 1, “index”: 0, “message”: “\u6b64\u533a\u5757\u5df2\u6dfb\u52a0\u81f3\u533a\u5757\u94fe”, “nonce”: 215, “previousHash”: “0”, “transactions”: [ { “amount”: 200, “from”: “mayun”, “to”: “Felix” }, { “amount”: 200, “from”: “mayun”, “to”: “Felix” }, { “amount”: 200, “from”: “mayun”, “to”: “Felix” } ] }, { “dateCreated”: “2018-08-11 17:37:18”, “hash”: “1127f8c712ae3205ccbab9788392bcd190b8b6b1”, “id”: 2, “index”: 1, “message”: “\u6b64\u533a\u5757\u5df2\u6dfb\u52a0\u81f3\u533a\u5757\u94fe”, “nonce”: 380, “previousHash”: “1152cb1aac50abd803a4589f28c7e054db207e23”, “transactions”: [ { “amount”: 200, “from”: “mayun”, “to”: “Felix” }, { “amount”: 200, “from”: “mayun”, “to”: “Felix” } ] }, { “dateCreated”: “2018-08-11 17:37:39”, “hash”: “11b5d293c3068081f1771f14f96a3e450f282171”, “id”: 3, “index”: 2, “message”: “\u6b64\u533a\u5757\u5df2\u6dfb\u52a0\u81f3\u533a\u5757\u94fe”, “nonce”: 64, “previousHash”: “1127f8c712ae3205ccbab9788392bcd190b8b6b1”, “transactions”: [ { “amount”: 200, “from”: “mayun”, “to”: “Felix” }, { “amount”: 200, “from”: “mayun”, “to”: “Felix” } ] }, { “dateCreated”: “2018-08-11 17:37:45”, “hash”: “11d97dfca7cc7a67c22b9df06017768fca0a193f”, “id”: 4, “index”: 3, “message”: “\u6b64\u533a\u5757\u5df2\u6dfb\u52a0\u81f3\u533a\u5757\u94fe”, “nonce”: 125, “previousHash”: “11b5d293c3068081f1771f14f96a3e450f282171”, “transactions”: [ { “amount”: 200, “from”: “mayun”, “to”: “Felix” }, { “amount”: 200, “from”: “mayun”, “to”: “Felix” } ] }, { “dateCreated”: “2018-08-11 17:38:03”, “hash”: “115a991bd5d2ebe6e232b421965ab852b97a4202”, “id”: 5, “index”: 4, “message”: “\u6b64\u533a\u5757\u5df2\u6dfb\u52a0\u81f3\u533a\u5757\u94fe”, “nonce”: 220, “previousHash”: “11d97dfca7cc7a67c22b9df06017768fca0a193f”, “transactions”: [ { “amount”: 200, “from”: “mayun”, “to”: “Felix” }, { “amount”: 200, “from”: “mayun”, “to”: “Felix” } ] } ], “id”: 1}然后我们要让8080知道有这么一个节点curl -s -X POST localhost:8080/node –data “address=localhost:8081” | python -m json.tool我们会看到节点已经注册成功了{ “address”: “localhost:8081”, “id”: 1}这时我们让8080去主动解决冲突curl -s -X POST localhost:8080/resolve | python -m json.tool再查看8080上的区块链,发现已经被替换为较长的8081上的blocks了。{ “blocks”: [ { “dateCreated”: “2018-08-11 17:35:15”, “hash”: “1152cb1aac50abd803a4589f28c7e054db207e23”, “id”: 1, “index”: 0, “message”: “\u6b64\u533a\u5757\u5df2\u6dfb\u52a0\u81f3\u533a\u5757\u94fe”, “nonce”: 215, “previousHash”: “0”, “transactions”: [ { “amount”: 200, “from”: “mayun”, “to”: “Felix” }, { “amount”: 200, “from”: “mayun”, “to”: “Felix” }, { “amount”: 200, “from”: “mayun”, “to”: “Felix” } ] }, { “dateCreated”: “2018-08-11 17:37:18”, “hash”: “1127f8c712ae3205ccbab9788392bcd190b8b6b1”, “id”: 2, “index”: 1, “message”: “\u6b64\u533a\u5757\u5df2\u6dfb\u52a0\u81f3\u533a\u5757\u94fe”, “nonce”: 380, “previousHash”: “1152cb1aac50abd803a4589f28c7e054db207e23”, “transactions”: [ { “amount”: 200, “from”: “mayun”, “to”: “Felix” }, { “amount”: 200, “from”: “mayun”, “to”: “Felix” } ] }, { “dateCreated”: “2018-08-11 17:37:39”, “hash”: “11b5d293c3068081f1771f14f96a3e450f282171”, “id”: 3, “index”: 2, “message”: “\u6b64\u533a\u5757\u5df2\u6dfb\u52a0\u81f3\u533a\u5757\u94fe”, “nonce”: 64, “previousHash”: “1127f8c712ae3205ccbab9788392bcd190b8b6b1”, “transactions”: [ { “amount”: 200, “from”: “mayun”, “to”: “Felix” }, { “amount”: 200, “from”: “mayun”, “to”: “Felix” } ] }, { “dateCreated”: “2018-08-11 17:37:45”, “hash”: “11d97dfca7cc7a67c22b9df06017768fca0a193f”, “id”: 4, “index”: 3, “message”: “\u6b64\u533a\u5757\u5df2\u6dfb\u52a0\u81f3\u533a\u5757\u94fe”, “nonce”: 125, “previousHash”: “11b5d293c3068081f1771f14f96a3e450f282171”, “transactions”: [ { “amount”: 200, “from”: “mayun”, “to”: “Felix” }, { “amount”: 200, “from”: “mayun”, “to”: “Felix” } ] }, { “dateCreated”: “2018-08-11 17:38:03”, “hash”: “115a991bd5d2ebe6e232b421965ab852b97a4202”, “id”: 5, “index”: 4, “message”: “\u6b64\u533a\u5757\u5df2\u6dfb\u52a0\u81f3\u533a\u5757\u94fe”, “nonce”: 220, “previousHash”: “11d97dfca7cc7a67c22b9df06017768fca0a193f”, “transactions”: [ { “amount”: 200, “from”: “mayun”, “to”: “Felix” }, { “amount”: 200, “from”: “mayun”, “to”: “Felix” } ] } ], “id”: 1}Summary (总结)可以看到大概区块链的设计还是比较有意思的,细节部分请不要在意(比如sha1和prefix“11” ????)基本的介绍就到这里,建议自己动手实践一遍,还是蛮有意思的。 ...

March 31, 2019 · 7 min · jiezi

程序员,金三银四该不该跳槽?

“金三银四”跳槽季,成了职场人跳槽旺季的代名词,同时也给了职场人一个极强的心理暗示:只要在这个旺季跳槽,那也大概率能比其他时间跳槽到一个更好的下家。然而职场规则比职场人想象的还要理性,一个岗位对于应聘者的要求并不会因为求职淡旺季有太大的区别,反而会因为招聘旺季提升选拔标准。就像一池鱼都想跳进一个筐的时候,织筐的人反而会把筐编得更高些,筛选掉那些弹跳力弱的鱼,从而选到最有活力的鱼。据《2018年春季白领跳槽指数调研报告》显示,在18年的“金三银四” 中12.9%的白领正在办理离职/入职手续,56.7%的白领已更新简历正在求职,也就是说在跳槽季积极行动的白领比例高达69.6%;有趣的是,55.8%的跳槽者在跳槽后明确表明对新工作比较失望。这个数据为职场人敲了警铃,“金三银四”为60%的优质人才提供了繁荣的氛围与充足的机会,而剩余40%的求职者在“金三银四”制造出的跳槽泡沫中,被折射的短暂斑斓晃了眼,乱了心,瞎跳比不跳更可怕。金三银四跳不跳:被动原因的跳槽忍则炼 主动原因的跳槽需慎重说到跳槽旺季,不得不说说职场人跳槽的心理,暂且不提被辞退后逼不得已的换工作,大多数跳槽的原因可以分为主动原因及被动原因两种。被动原因跳槽,指跳槽的原因大部分源于被动接收的某些情况,如与同事关系不融洽、在工作中受到了委屈等。大部分的被动原因都不建议贸然跳槽,每个企业的职场状况千奇百态,但糟心事的套路却大同小异。跳槽后,同样的状况未必不会发生,且可能还有其他不可预估的职场困难。而且,有时候不舒服的职场环境反而是职场人成长的加速器。当然,少部分挑战价值观的情况下,如被公司指派做违法职业道德的事等,还是当跳则跳。今天我们更多要聊的是主动原因跳槽的情况,主动原因跳槽最为普遍且重要的驱动因素不外乎三点:想要涨薪、寻求晋升、追求自我发展。在金三银四这样跳槽的活跃期,由这三点主动原因引发的跳槽想法,更需要多角度评估。以下针对涨薪、晋升、自我发展三个主动原因的跳槽tips,希望能够帮助职场人减少跳错的几率。Tip1:高薪3坑,跳槽勿踩2018年金三银四白领跳槽的原因中,排名第一的是薪酬水平,约占55.8%,薪资成了职场人衡量工作职位优劣的重要标准。在互联网行业,一般跳槽的薪资涨幅在20-30%左右,这个可观的数字对金三银四跃跃欲试的职场人来说,是个不小的诱惑,但这诱惑背后隐藏的坑,准备跳槽涨薪的职场人还需擦亮眼后再行动。第1坑,薪资涨幅小于等于其他成本的增加例如:朋友A在某二线城市互联网公司税前薪资10k,在跳槽旺季接到了某一线城市公司offer,允诺工资15k。心动于高薪的A在和一位资深HR朋友聊天时咨询朋友是否应该接受新offer,朋友给A算了一笔账,一线城市每个月房租比目前要增加2k,交通和吃饭等生活成本每月增加1k,往返回家的路费每月增加1k,社交费用每月增加0.5k,算下来和现在的工资并无差别。当跳槽会带来其他连锁成本的增加时,若增加成本>薪资涨幅,因为想涨薪而跳槽的你则要三思而后行了。第2坑,薪资构成里可能藏着圈套例如:朋友B春季跳槽到一家开出高薪的影视公司,待了三个月后怨声载道。原来新公司的高薪是由20%的基本工资+80%的绩效组成,签合同时HR再三口头强调平均每月绩效能拿90%。而实际情况是,无论工作完成多出色,员工的绩效都被各种理由扣去近一半。类似的情况时有发生,有的公司开出高价年薪,却有近一半是所谓的“年底绩效分红“,或使用各种理由进行克扣。所以,当收到一份高薪offer时,先把薪资构成和HR谈清楚后再开心也不迟。第3坑,公司状况不明朗导致降薪危机拿到高薪offer的职场人,在以上两个坑都排除的情况下,最好了解清楚公司现阶段业务及资金状况,特别是创业型公司,确保公司的状况能够长期支付高额薪资且不会发生转部门降薪等情况。Tip2:跳槽≠一定晋升除了涨薪之外,晋升也是职场人跳槽的强动力。对于在金三银四想通过跳槽来晋升的职场人,我们今天主要讨论两个词:职业轨迹和现有机会。例如:朋友C,含实习在内有3家知名互联网公司的工作经验,且每一份经验都在一年左右。当她再次跳槽到一家和前公司体量相似的公司时,依旧没能如愿以偿的升为管理岗。在和她的聊天中得知,她的每一份工作都是基础岗,做了一年没有得到晋升消息,也不愿再寻求内部晋升机会,便赶集似的找起了下家,而下家的评估多是因无管理岗经验,则需从基础岗做起。C的第一个问题在于,职业轨迹的停滞导致晋升的瓶颈。试想一下,如果B在某一份工作中耐住性子积累更多的能力和经验,从基础岗升到初级管理岗后再选择跳槽,有了管理岗经验的C通过跳槽从经理岗晋升到高级经理岗便容易的多。C的另一个问题在于,欠缺寻求现有机会的职场嗅觉。内部晋升的机会在某些方面也是有迹可循的,最为关键的一点是要看上级岗位是否有空缺,且同级的同事中自己是否具有竞争力。若经过评估,现有公司有升职机会且自己有获得晋升的能力,不妨在现有公司多待一段时间,也为下一次跳槽积累更多筹码。所以,当跳槽的最大诉求是晋升时,先回头审视一下自己的职业轨迹是否清晰可发展,再抬头看看现有公司是否藏着努力可得的升职机会。也许,目前的稳当是为了日后跳得更高。Tip3:自我定位与规划要先于跳槽的动作关于自我发展的问题,年后的种种跳槽诱惑也是陷阱颇多,而准确的自我职业发展定位就成了重中之重。一味的求变并不能解决问题,规划好自我职业定位与发展方向后再计划性谋动才是治本之道。很多职场人稀里糊涂就选择了一份工作,比如,自己好像不适合做技术,但偏偏选择了研发部门;自己好像不喜欢做销售,但为了尽快拿到offer,却选择了销售岗。之所以用“好像”这个词,是因为这些人中的绝大部分并不清楚自己想要什么样的工作,适合什么样的岗位,很多职场人把这种情况产生的焦虑感误以为是需要跳槽解决的自我发展需求,却忽略了在职场中自我发展的本质。不论身处职场哪个阶段,自我的审视是很有必要的,这也决定了你在后面长时间的职业规划。首先要自我判断和定位,按阶段制定自我发展的计划,拔高自己的格局,找到自我提升的动力,而盲目的跳槽可能适得其反,扰乱自己的思考。金三银四好似职场人一年一度的跳槽狂欢,更像是中国职场现状的缩影。战略布局的快速变化导致公司岗位需求波动频繁,适应力较弱的职场人被动选择跳槽,适应力强的职场人因为对自我职业要求的提升也总想往高处跳,而一些认知不清晰的职场人受到周围环境氛围的影响,也动起了跳槽的念头。跳槽,本应是在职业道路中往上攀登的助力工具,现在却好像成了大部分职场人不得不做的事。当一件事变得不得不做时,压迫感会阻碍思考的空间,从而出现盲目跳槽的情况。又是一年金三银四,愿每一个想要跳槽的职场人都明确初衷,擦亮眼睛,跳出一片似锦前程。想跳槽推荐一看BAT—最新iOS面试题总结iOS面试题大全(附答案)

March 13, 2019 · 1 min · jiezi

Cocopods基础使用

一、安装和使用Cocopods网上已有很多教程,参考示例:CocoaPods安装教程二、组件库支持Cocopods方式引入1.创建远程代码仓库创建远程代码仓库(并不是podspec文件的仓库),此仓库放的是源代码。可以在GitHub上创建仓库。2.创建远程podspec仓库如果要发布到Cocopods的官方spec仓库(公开的),那么就不需要创建。当然私有库是需要创建的,在这一步两者不一样。公开库参考示例:发布开源库到Cocopods官方仓库3.创建本地代码工程可以使用pod命令创建,得到一个工程模板,并且可以根据需要配置工程,如下:命令创建工程模板pod lib create <组件库名>工程配置选择选择平台What platform do you want to use?? [ iOS / macOS ] iOS选择语言What language do you want to use?? [ Swift / ObjC ] ObjC是否自动生成一个用来做demo测试的模板库,建议Yes,后面方便测试 Would you like to include a demo application with your library? [ Yes / No ] Yes是否集成测试框架Which testing frameworks will you use? [ Specta / Kiwi / None ] NoneUI 测试Would you like to do view based testing? [ Yes / No ] No指定类前缀What is your class prefix? WT4.编写podspec文件如果用第三步的命令创建工程模板,那么在Podspec Metadata目录下已经自动生成了。如果是已有的工程或者库文件目录,也可以利用Pod命令自己制作.podspec文件,命令如下:pod spec cretae <组件库名>参考链接:podspec文件的具体说明5.验证cocoaPods索引文件命令如下:pod lib lint (从本地验证你的pod能否通过验证) pod spec lint (从本地和远程验证你的pod能否通过验证) pod lib lint –verbose (加–verbose可以显示详细的检测过程,出错时会显示详细的错误信息) pod lib lint –allow-warnings (允许警告,用来解决由于代码中存在警告导致不能通过校验的问题) pod lib lint –help (查看所有可选参数,可选参数可以加多个)6.本地测试库是否可用新建工程,切换到工程目录,执行命令pod init修改podfile文件, 并添加上本地库路径pod ‘库名’, :path => ‘/Users/xxx/Documents/库名’拉取pod代码:成功后可看到我们的库并没有在pods里面,而是在Development Pods里面,可用先检测代码有没有问题。7.提交工程代码提交工程代码到远程代码仓库,可以利用git或者svn进行代码版本管理,提交代码到GitHub等8.提交podspec文件开源库提交podspec文件到Cocopods官方仓库, 当然需要现在ocopods官方仓库中注册账号,命令如下:pod trunk me (检查是否注册trunk) pod trunk register <邮箱> <注册名字> –verbose (注册命令)注册完成之后会给你的邮箱发个邮件,进入邮箱邮件里面有个链接,需要点击确认一下.之后开始提交,切换到有.podspec文件的组件工程根目录执行命令pod trunk push <组件库名>.podspec pod trunk push <组件库名>.podspec –allow-warnings私有库提交podspec文件到远程podspec仓库,和Cocopods官方库不同的是,私有仓库需要先添加到本地仓库,再push到远程仓库,因为Cocopods默认已经添加到了本地仓库(默认为master),Mac系统可以查看文件目录(/.cocoapods/repos), 私有库命令如下:添加到本地仓库, git@git.xxxx.git为远端podspec库的地址,成功之后目录(/.cocoapods/repos)除了master之外,新增了一个文件夹(<组件库名>)pod repo add <组件库名> git@git.xxxx.git查看是否添加成功pod repo listpush到远程podspec仓库pod repo push <podspec远端仓库名> <组件库名>.podspec9. 检查仓库是否发布成功pod搜索一下:pod search <组件库名>如果报错,搜索不到,建议更新下pod:pod update之后仍然搜索不到,那么进入CocoaPods缓存目录,删除缓存索引文件search_index.json:cd ~/Library/Caches/CocoaPods ls rm -f search_index.json10. pod库文件引入如果是开源库(公有的),修改podfile文件:pod ‘组件库名’如果是私有仓库,建议在podfile文件开头添加source源:source ‘https://github.com/CocoaPods/Specs.git' #官方仓库地址source ‘http://xxx/组件库.git’ #私有podspecs仓库地址最后执行命令进行安装:pod install三、Cocopods打包静态库 ...

March 12, 2019 · 1 min · jiezi

程序员—10条求职的黄金规律

来看一下金三银四的招聘旺季下,10条求职的黄金规律。可以说每一条都很有一定深度01:很多时候,HR不要你,不是因为你水平的问题,也不是因为你专业技能的问题。而是HR自己对自己没信心,HR没把握你这样的候选人,会不会踏实地在部门内做事。HR觉得你够聪明,够优秀,但不敢用你,因为他们担心花了很大的精力去培养你,最后你没花心思放在这份工作上,这对HR和用人部门都是很大的打击。【不要怀疑自己】02:薪水高是否意味着一份好工作,答案无疑是否定的。一般情况下,薪水和期待成正比,既然有人给了你更高的经济回馈,那就意味着对你的期待更高。而一个人创造的价值并不完全由自己决定,还依赖于客观的条件,比如团队、客户、同事、客户、周期等。如果你要先享受更大的收益,然后再去创造价值,往往翻车的概率会很大。03:手里攥着Offer 来谈更高条件的候选人,一般不会被待见。如果单从薪酬上看,永远都有可能比当下更高薪的工作在等着自己,拿着Offer 来谈条件的候选人往往会被认定稳定性存疑。收入不是不重要,但不应该是决定一个人是否加入一家公司的先决条件。特别是工作数年后还对薪酬非常纠结的话,可能压根就没有对自己和外部环境有一个清晰的认识。04:企业对外招聘的时候,大家都不要太在意招聘广告上的薪酬范围数值,这个数值往往并不是公司实际对这个岗位的定薪标准。确实,薪酬写的越高越能吸引人,但职位工作的内容和挑战,会因为薪资的关系被弱化甚至被忽视。像在阿里,看官网上的招聘,不会放出某个职位的薪资范围,销售岗位偶尔例外。05:如果你真的有两把刷子,学历限制、工作年限条件、专业背景要求都不是问题。公司的 JD 是 HR 部门写的,HR希望能够最大程度上用高效率的方式筛选到合适人才。但实际的用人部门的需求更现实,用人部门只在乎来的人能不能解决问题。在阿里也有大专甚至中专的同事,一点都不影响他们成为公司的优秀员工,在职场上的员工优秀与否和学历有时候并不是正向关系。06:如果你现在的领导,排斥异己,容不下不同的声音,搞裙带关系,专心培养自己的所谓派系,评定业绩的时候做不到看业绩说话,那就早点离开,不要把自己有限的人生浪费在无聊的蝇营狗苟上。而且离职也并不是一件坏事,离职在另外一方面有助于提升自己的认知,扩大自己的视野,机会也会更多,所以别总纠结着或依依不舍,成年人都懂得取舍。1条观点07:人有三观,企业也有。但三观约束自己还行,不能用它来界定他人。因为你不是对方,你不了解对方,你对其他一切知之甚少。不要因为局部而否定整体,每家公司都有自己的问题,我们是选择一个适合自己的平台,不是扮演企业的道德和伦理的警察,用自己的三观来判断一家公司的好坏,这很幼稚,所谓“三观正”其实是个简称啦,全称是:“三观正好和我一样”。08:如果真的想好好锻炼自己的能力,那一开始就不要先去环境特别稳定、管理特别健全的公司。我们以HR来举例,现在人力资源工作在一些超大型的企业里,已经分工的非常细,某些环节跟工厂的流水线差不多,流水线一多,就会让HR学习能力不够强,学习速度不够快,影响了个人发展。倘若你已经在超大公司的内部工作,那也尽量选择有挑战的事业部。09:求职受挫,简历被虐,面试碰壁… …这些都不是你可以气馁的理由。求职中的挫折在工作挑战面前有时候都不值得一提,失败的场景以后还会经常遇到,所以你还是提前让自己内心坚强一点,别总玻璃心,没人同情你的脆弱内心。受挫之余,抓紧学习,在别人玩的时候你在偷偷练级,这才是你应该做的事情。10:没有什么企业或单位是完美的,没有缺陷的,每个公司都会有一些自己的问题,就算公司很好,你也有很大概率会遇到一些不那么好的同事,上司或者合作伙伴。你不可避免会和自己不喜欢的人一起共事,但重要的是你的耐心,有耐心的人和任何人都能配合好工作,没耐心的人半年就换一份工作。推荐一看BAT—最新iOS面试题总结iOS面试题大全(附答案)参考原文地址

March 9, 2019 · 1 min · jiezi

iOS开发—音视频入门学习必看

音视频学习从零到整–(2)音视频学习从零到整–(3)音视频学习从零到整–(4)音视频学习从零到整–(5)音视频学习从零到整–(6)音视频学习从零到整–(7)一.音频基础复习1.1 声音的产生相对于视频,可观察这个现象.音频在学习过程,就缺乏了想象的空间.但是如果从原理出发,就不会那么难了.声音是什么?声音是波,靠物体的振动产生1.2 声波的3要素声波的三要素,是频率,振幅,波形.频率代表音阶的高低,振幅代表响度,波形则代表音色.频率越高,波长就会越短.而低频声响的波长则较长.所以这样的声音更容易绕过障碍物,能量衰减就越小.声音就会传播的越远.响度,就是能量大小的反馈.用不同的力度敲打桌面,声音的大小势必发生变换.在生活中,我们用分贝描述声音的响度.==小贴士==分贝(decibel),是度量声音的强度单位,常用dB表示.是由美国发明家亚历山大.格雷厄姆.贝尔 名字命名的.长期在夜晚接受50 分贝的噪音, 容易导致心血管疾病; 55 分贝, 会对儿童学习产生负面影响; 60分贝, 让人从睡梦中惊醒; 70 分贝,心肌梗死的发病率增加30%左右; 超过110 分贝, 可能导致永久性听力损伤.音色,在同样的频率和响度下,不同的物体发出的声音不一样.比如钢琴和古筝声音就完全不同.波形的形状决定了声音的音色.因为不同的介质所产生的波形不同.就会产生不一样的音色.1.3 声音传播声音的发生,来源于振动.人类说话,从声带振动发生声音之后,经过口腔,颅腔等局部区域的反射,在经过空气传播到别人耳朵中.这是我们说话到听到的过程.声音的传播,可以通过空气,液体,固定传播.介质不同,会影响声音的传播速度.吸音棉:通过声音反射而产生的嘈杂感,吸音材料选择使用可以衰减入射音源的反射能量,从而对原有声音的保真效果.比如录音棚墙壁上就会使用吸音材质隔音:主要解决声音穿透而降低主体空间的吵闹感,隔音棉材质可以衰减入射声音的透射能量.从而达到主体空间安静状态,比如KTV墙壁上就会安装隔音棉材料.二.数字音频2.1 模拟信号数字化过程将模拟信号转换为数字信号的过程,分别是采样,量化和编码.音频采样对模型信号进行采样,采样可以理解为在时间轴上对信号进行数字化.而,根据奈斯特定理(采样定理),按比声音最高频率高2倍以上的频率对声音进行采样.这个过程称为AD转换.比如,前面提到高质量音频信号,其频率范围是20Hz-20KHz.所以采样频率一般是44.1KHz.这样可以保证采样声音达到20KHz也能被数字化.而且经过数字化处理后的声音,音质也不会降低.44.1KHZ,指的是1秒会采样44100次奈斯特定理(采样定理) 资料量化量化,指的是在幅度轴上对信号进行数字化.简单的说,就是声音波形的数据是多少位的二进制数据,通常用bit做单位.比如16比特的二进制信号来表示声音的一个采样.它的取值范围[-32768,32767].一共有65536个值.如16bit、24bit。16bit量化级记录声音的数据是用16位的二进制数,因此,量化级也是数字声音质量的重要指标。我们形容数字声音的质量,通常就描述为24bit(量化级)、48KHz采样,比如标准CD音乐的质量就是16bit、44.1KHz采样.既然每个量化都是一个采样,那么声音这么多采样,该如何将这些数据存储起来?编码什么叫编码?按照一定格式记录采样和量化后的数据.音频编码的格式有很多种,而通常所说的音频裸数据指的是脉冲编码调制(PCM)数据.如果想要描述一份PCM数据,需要从如下几个方向出发:量化格式(sampleFormat)采样率(sampleRate)声道数(channel)举例:以CD音质为例,量化格式为16bite,采样率为44100,声道数为2.这些信息描述CD音质.那么可以CD音质数据,比特率是多少?44100 * 16 * 2 = 1378.125kbps那么一分钟的,这类CD音质数据需要占用多少存储空间?1378.125 * 60 /8/1024 = 10.09MB如果sampleFormat更加精确或者sampleRate更加密集,那么所占的存储空间就会越大,同时能够描述的声音细节就会更加精确.存储在这些二进制数据即可理解为将模型信号转化为数字信号.那么转为数字信号之后,就可以对这些数据进行存储播放复制获取其他任何操作.推荐文集* 抖音效果实现* BAT—最新iOS面试题总结* iOS面试题大全(附答案)原文作者:集才华美貌于一身的—C姐

March 7, 2019 · 1 min · jiezi

嵌套滚动效果实现讨论

本文要讨论的是类似于即刻、淘票票首页,抖音、简书个人主页这样的嵌套滚动效果,事实上网上已经有很多的相关的文章,比如:嵌套UIScrollview的滑动冲突解决方案iOS 嵌套UIScrollview的滑动冲突另一种解决方案多层 UIScrollView 嵌套滚动解决方案而且绝大多数的文章都是从如何解决手势冲突出发给出相应的解决方案,原因是他们大多数都采用了三级 Scrollview 的解决方案,如下图蓝色视图:一级 ScrollView红色视图:HeaderView绿色视图:MenuView橘色视图:二级 ScrollView黑色、深黑、浅黑:三级 ScrollView可以看到三级 ScrollView 和 一级 ScrollView都需要在纵向滚动,所以重点要解决的就是这里的滚动冲突,具体的细节我就不再赘述,大家还可以参考HGPersonalCenter这个项目,里面有详细的注释。之所以在前面给出了四个例子,是因为淘票票和简书采用的是上面提到的方案,而抖音和即刻两个则不是,并且即刻在体验上更完美,这个后面会讲到。简单粗暴的用越狱手机+Reveal验证下淘票票上层的 MVNestTableView:一级 ScrollView中间的 UIScrollView:二级 ScrollView下层的 MVNestTableView:三级 ScrollView当然通过点击状态栏看也可以粗略判断实现方式,比如淘票票在点击状态栏后视图只会滚动到子 ScrollView 的顶部而不是最外面 ScrollView 的,简书虽然滚动到最外层的顶部但效果明显不够自然,原因就是三级 ScrollView 在纵向没有延伸到顶部。抖音和即刻在点击状态栏返回到顶部的效果非常自然,所以有理由相信它们在实现上不同于上述方案,那么抖音和即刻的实现方式具体有什么不同?同样的用Reveal看下即刻的视图结构从整体结构上来看即刻只有二级 ScrollView,所以在纵向上 ChildScrollView 会完全接管手势,横向滚动时又由 MainScrollView 控制,这样子带来的好处在于无需关心手势冲突问题,但要实现前面提到的效果还必须处理是以下问题:HeaderView 和 MenuView 的位置需要根据 ChildScrollView 的滚动而改变在切换的 Tab 的时候需要同步下一个 ChildScrollView 的 offsetChildScrollView 必须在顶部留出 HeaderView 和 MenuView 高度总和的空白区域HeaderView 不能拦截滚动手势在这里就不给出具体的实现细节,文章后面最后有通过两种方案实现的开源库,欢迎 Star。前面提到的即刻和抖音采用的都是这种二级 ScrollView 的方案,但即刻在体验上更好,比如抖音的个人主页如果手指开始滚动的地方有可交互的控件(Tab栏),那么这时候滑动是会失效的,还有在切换Tab后将视图下拉滚动到顶部然后返回到之前的Tab页,抖音是直接返回到了原始的位置而即刻还是能保留之前进度。头部滚动失效解决方案即刻为了达到完美的效果,在每个 ChildScrollView 顶部都添加了 HeaderView 和 MenuView,这样子作为一个整体,即使开始触摸的地方有可交互控件也可以上下滚动。然后在左右滑动的时又让ChildScrollView 内的 HeaderView 和 MenuView 隐藏,当停止滚动的时让原本在外层 ScrollView 内的 HeaderView 和 MenuView 显示。保留进度解决方案关于保留进度首先要做的就是判断当前 ChildScrollView 是不是处于一种特殊状态,这种状态就是 offset.y的值是否大于 HeaderView 的偏移量,然后再通过判断 ChildScrollView 当前的滚动方向,来决定是否要调整 HeaderView 和 MenuView 的位置。对比两个方案最终的实现各有优缺点方案一优点:无障碍配合使用第三方下拉刷新库ChildViewController 无需额外设置缺点:实现较复杂滚动有细微的停顿感切换Tab不能保留进度点击状态栏不能返回到顶部方案二优点:实现简单滚动无停顿感切换Tab可保留进度点击状态栏可返回到顶部缺点ChildViewController 需要额外的设置(ChildScrollView 必须在顶部留出 HeaderView 和 MenuView 高度)下拉刷新只能在 ChildViewController 内实现这里要提的是,由于方案二中 MainScrollView 并不会在纵向有滚动,所以下拉刷新必须放在 ChildViewController 内实现,但又因为 HeaderView 和 MenuView 需要根据 ChildScrollView 的偏移而移动,在配合MJRefresh时它们的偏移有明显的Bug(在本文发布前我并没深究解决方案),或许即刻也是因为这个原因而采用上面提到的解决办法。方案一开源库:Aquaman方案二开源库:Shazam上面两个解决方案中的 MenuView 都设计成了交由开发者实现,因为即使集成各种样式的也难满足设计上的千奇百怪的要求,参考我的Demo就能很快实现一个自己想要的效果。 ...

March 6, 2019 · 1 min · jiezi

GitHub 源码,Framework 框架

https://github.com/CoderLN/Ap...Apple 译文、GitHub 源码,随原作者 (大版本) 迭代注解。— 不知名开发者https://github.com/CoderLN/Fr…iOS_12 Framework 归类整理,框架各分类文件注解,并对每一类中常用的功能进行案例渐进式解析。 — 不知名开发者

March 5, 2019 · 1 min · jiezi

腾讯—最新iOS面试题总结

关于面试题,可能没那么多时间来总结答案,有什么需要讨论的地方欢迎大家指教。主要记录一下准备过程,和面试的一些总结,希望能帮助到正在面试或者将要面试的同学吧。腾讯一面1、介绍一下实习的项目,任务分工,做了哪些工作?介绍实习内容2、网络相关的:项目里面使用到什么网络库,用过ASIHTTP库吗3、断点续传怎么实现?需要设置什么?4、在杭州HTTP请求服务器响应快,可能离服务器距离近,而在深圳访问就很慢很慢,会是什么原因?如果用户投诉,怎么分析这个问题?5、HTTP请求的哪些方法用过?什么时候选择get、post、put?6、TCP建立连接的过程,断开连接的过程,为什么是四次握手?7、项目里面的数据存储都用了哪些?知道iOS里面有哪些数据存储方法?什么时候该用哪些方法存储?8、MVVM如何实现绑定9、block和通知的区别,分别适用什么场景10、算法。连续问了好几个,都是数组,层层递进的,但是我忘了,只记得最后是找出数组11、中重复的数字12、进程和线程的区别13、程序在运行时操作系统除了分配内存空间还有什么14、进程间通信的方式15、如何检测应用是否卡顿16、发布出去的版本,怎么收集crash日志?不使用bugly等第三方平台或者这些第三方平台是怎么收集crash日志的?17、在block里面使用_property会造成循环引用吗?怎么解决?除了使用self->_property,可以使用valueforkey来访问吗 在block里面可以修改它的值吗setvalueforkey?可以修改它的值,可以用valueforkey来解决,显式的的使用self,block外先持有self的弱引用。二面1、OC中对象的结构2、多态3、Ping是什么协议4、知道MTU吗5、ARC和MRC的本质区别是什么?6、NSThread,GCD,NSOperation相关的。开启一条线程的方法?线程可以取消吗?7、子线程中调用connection方法,为什么不回调?因为没有加入runloop,执行完任务就销毁了,所以没有回调。8、MVC和MVVM的区别9、了解哪些设计模式10、存一个通讯录,包括增删改查,用什么数据结构11、autorelease变量什么时候释放?手动添加的是大括号结束的时候释放,系统自动释放是在12、当前runloop循环结束的时候13、那子线程中的autorelease变量什么时候释放?14、子线程里面,需要加autoreleasepool吗15、GCD和NSOperation的区别?16、项目里面遇到过死锁吗?怎么解决?数据库访问本来就是线程安全的,不会造成死锁啊。什么是死锁?17、Viewcontroller的生命周期?18、在init方法里面,设置背景颜色,会生效吗 会生效。为什么会?19、WWDC2016公布了哪些新特性?对苹果系列的最新特性有关注吗20、看过哪些源码,讲讲思路21、两个链表找第一个相同结点22、字符串旋转23、找链表的倒数第k个结点24、把一个链表比某个值大的放在左边,比它小的放在右边25、二叉树的中序遍历,非递归更多:iOS面试题(附答案)另外附上一份收集的各大厂面试题(附答案) ! 要的可加iOS高级技术群:624212887,群文件直接获取

February 28, 2019 · 1 min · jiezi

IOS常用框架集合

IDealist框架-IOS常用框架集合github部分截图要求iOS 8.0+swift 3.0+安装方式使用 CocoaPodspod ‘IDealist’设置所有组件的主题色IDealistConfig.share.id_setupMainColor(color: UIColor.red)项目中引用IDeal框架引用IDealist内部的框架有2种方法1.按需导入,每个文件中需要哪个框架就引用哪个,例如:import IDealist。优点:有提示。缺点:需要每次都导入-2.使用 @_exported 关键字导入一次即可。例如:@exported import IDealist优点:只要导入一次,缺点:可能没有代码提示注意:对于IDealist框架,其内部含有很多扩展方法,需要使用@exported import IDealist的方式导入一次即可,也有代码提示。使用详解IDToast调用id_show方法,可传入参数介绍如下// 默认纯文本、展示在window上、1.5秒消失、中间位置// onView: 可以指定显示在指定的view上// success=nil,展示纯文本,success=false展示错误的图片,success=true展示成功的图片// position: 展示的位置IDToastPosition.top、IDToastPosition.middle、IDToastPosition.bottomid_show(msg: String, onView:UIView? = nil,success: IDToastUtilsImageType? = nil,duration:CGFloat? = nil, position: IDToastPosition? = .middle)使用方式IDToast.id_show(msg: “展示纯文本”)IDToast.id_show(msg: “展示纯文本,在指定view上。指定3s”, onView: self.view, duration: 3)IDToast.id_show(msg: “登录成功”, success: IDToastUtilsImageType.success)修改toast样式IDToastManager.share.successImage = UIImage(named: “message_success”)IDToastManager.share.textFont = UIFont.boldSystemFont(ofSize: 20)IDToastManager.share.textColor = UIColor.redIDToastManager.share.bgColor = UIColor(white: 0, alpha: 0.5)IDToastManager.share.cornerRadius = 8使用注意1.IDToast 默认是支持多任务顺序异步执行的,如果连续调用多次id_show,toast会依次执行。通过以下属性可控制该功能IDToastManager.share.supportQuene = false2.修改toast样式是通过单例来设置的,所以建议在项目初始化时就统一toast的样式。IDDialog目前提供4种类型的弹框1.普通弹框,调用id_show方法IDDialog.id_show(title: “温馨提示”, msg: “确定要取消?”, leftActionTitle: “确定”, rightActionTitle: “取消”, leftHandler: {}) {}2.带有图片显示的弹框,调用id_showImg方法IDDialog.id_showImg(success: IDDialogUtilImageType.success, msg: “提交成功”, leftActionTitle: “知道了”, rightActionTitle: nil, leftHandler: nil, rightHandler: nil)3.带有输入文本框的弹框,调用id_showInput方法IDDialog.id_showInput(msg: “请输入取消原因”, leftActionTitle: “取消”, rightActionTitle: “确定”, leftHandler: { (text) in print(text)}) { (text) in print(text)}4.自定义内容的弹框,调用id_showCustom方法IDDialog.id_showCustom(msg: “标题”, leftActionTitle: nil, rightActionTitle: “确定”, customView: self.myView, leftHandler: { (customView) in}) { (customView) in}属性介绍/// 内容的对齐方式public var textAlignment = NSTextAlignment.center/// 设置主题色,2个按钮时只设置右边的主题色,1个按钮时显示主题色public var mainColor = UIColor.black/// 是否支持动画public var supportAnimate = true/// 自定义动画public var animate = CABasicAnimation()/// 成功图片public var successImage = UIImage(named: “ic_toast_success”, in: BundleUtil.getCurrentBundle(), compatibleWith: nil)/// 失败图片public var failImage = UIImage(named: “icn_icn_fail”, in: BundleUtil.getCurrentBundle(), compatibleWith: nil)/// 警告图片public var warnImage = UIImage(named: “icon_sign”, in: BundleUtil.getCurrentBundle(), compatibleWith: nil)/// 针对输入框类型的弹框,限制输入框的输入条件,文本框默认没有限制,在文本框消失时,自动重置文本框的属性/// 最多允许输入多少个字符public var maxLength: Int?/// 只允许输入数字和小数点public var onlyNumberAndPoint: Bool?/// 设置小数点位数public var pointLength: Int?/// 只允许输入数字public var onlyNumber: Bool?/// 禁止输入表情符号emojipublic var allowEmoji: Bool?/// 正则表达式public var predicateString: String?IDLoadingloading的类型public enum IDLoadingUtilLoadingType { case wait // 会阻止用户交互, 需要等待加载完成 case nav // 一条进度线条 case free // 不会阻止用户交互}1.展示不阻止用户交互的加载框 IDLoading.id_show() IDLoading.id_dismiss() // 取消2.展示阻止用户交互的加载框 IDLoading.id_showWithWait() IDLoading.id_dismissWait()3.展示网页加载进度 IDLoading.id_showProgressLine(onView: self.navView) IDLoading.id_dismissNav()4.自定义gif动画 IDLoading.id_showGif(gifName: nil, type: IDLoadingUtilLoadingType.free, onView: self.view) IDLoading.id_dismissGif()5.定制网页加载进度条的颜色IDLoading.id_showProgressLine(onView: self.navView, colors: [RGBAColor(0, 238, 0, 0.1).cgColor, RGBAColor(0, 238, 0, 0.5).cgColor, RGBAColor(0, 238, 0, 1).cgColor])IDImagePicker1.访问相册,外部弹出let picker = IDImagePicker()picker.cameraOut = truepicker.id_setupImagePickerWith(maxImagesCount: 10) { (assrtArr, img) in}2.访问相册,默认多选let picker = IDImagePicker()picker.id_setupImagePickerWith(maxImagesCount: 10) { (assrtArr, img) in}3.访问相册,单选let picker = IDImagePicker()picker.singleImageChooseType = .singlePicturepicker.id_setupImagePickerWith(maxImagesCount: 10) { (assrtArr, img) in}4.高度定制导航栏颜色let picker = IDImagePicker()picker.navColor = UIColor.purplepicker.navTitleColor = UIColor.whitepicker.statusBarType = .whitepicker.id_setupImagePickerWith(maxImagesCount: 10) { (assrtArr, img) in}5.高度定制主题色let picker = IDImagePicker()picker.tineColor = UIColor.greenpicker.id_setupImagePickerWith(maxImagesCount: 10) { (assrtArr, img) in}IDScanCode二维码与条形码扫描控件,而且能够实现快速的扫描二维码和条形码1.使用方法新建控制器继承IDScanCodeController即可2.可定制的一些提示语id_scanTitle // 扫描框顶部的提示语,默认无id_scanDetailTitle // 扫描框底部的提示语,默认是“将二维码/条码放入框内,即可自动扫描”IDEmptyView使用IDEmptyView 扩展了UIView,可以通过id_empty属性添加到目标视图当你需要展示或隐藏的时候可以利用rxcocoa的扩展绑定数据和isHidden属性view.id_empty = IDEmptyView.create().configStyle(style)创建并使用let emptyView = IDEmptyView.create().configStyle(style)view.addSubview(emptyView)style 可设置为public enum Style { case normal //有为什么为空状态说明 case normalDeail(String) //可操作空状态 case normalOperational //可操作并有说明的空状态 case normalDetailOperational(String) //网络加载失败可操作 case load //网络环境不良可操作 case loadDetail // 空状态的文字提示 case detail(String)}当你的style 为 normalOperational,normalDetailOperational,load,loadDetail时必须设置操作回调emptyView.setOperatorAction { /// 重新加载页面或者做其他事 }///也可以在创建的时候设置let emptyView = IDEmptyView.create().configStyle(style).setOperatorAction {}view.addSubview(emptyView)自定义图片,文本,按钮文字 emptyView.custom(image, title, detail, buttonText)IDUIKit1.IDButton封装IDButton的原因:解决Button的图片位置问题。当系统的Button的宽度自适应并且Button的样式是文字和图片的结合,图片可以在顶部,右侧,底部,此时如果使用imageEdgeInsets和titleEdgeInsets是达不到理想效果的,IDButton由此产生。IDButton继承自系统的Button。按钮的类型public enum IDButtonType { case normal case primary case error case loading}normal: self.layer.borderWidth = 1 self.setTitleColor(UIColor.black, for: UIControl.State.normal) self.backgroundColor = UIColor.white primary: // 背景色是主题色 self.backgroundColor = ColorConfig.share.mainColor self.layer.borderWidth = 0 self.setTitleColor(UIColor.white, for: UIControl.State.normal)error: // 背景色是红色 self.backgroundColor = RGBAColor(233, 80, 79, 1) // 写死的颜色 self.layer.borderWidth = 0 self.setTitleColor(UIColor.white, for: UIControl.State.normal)loading: // 可以展示一个loading动画 self.id_startLoading() // 开始动画 self.id_stopLoading() // 结束动画按钮图片的位置public enum IDButtonPosition { case left // 图片在文字左侧 case right // 图片在文字右侧 case top // 图片在文字上侧 case bottom // 图片在文字下侧}设置按钮渐变色public enum IDButtonGradientDirection { case horizontally case vertically}public func id_setupGradient(gradientColors: [UIColor], gradientDirection direction: IDButtonGradientDirection, gradientFrame: CGRect? = nil) { ….}部分属性/// 图片位置open var id_imagePosition = IDButtonPosition.left/// 图片和文字之间的间距open var id_imageTitleSpace: CGFloat = 0 /// loading视图和文字之间的间距open var id_loadingTitleSpace: CGFloat = 5 /// 按钮类型open var id_type: IDButtonType = .normal/// 加载框大小open var id_loadingSize: CGSize = CGSize.init(width: 15, height: 15)2.IDLabel开启长按复制功能label.id_canCopy = true富文本:图片插入的文字的指定位置label.id_setupAttbutImage(img: UIImage(named: “ic_new”)!, index: 0)3.IDTextView部分属性/// 占位文字public var id_placehoder: String? public var id_placehoderColor: UIColor? /// 文字改变的回调public var textChangeClouse: IDTextViewTextDidChangedClouse?/// 最多允许输入多少个字符public var id_maxLength: Int?/// 只允许输入数字和小数点public var id_onlyNumberAndPoint: Bool?/// 设置小数点位数public var id_pointLength: Int?/// 只允许输入数字public var id_onlyNumber: Bool?/// 禁止输入表情符号emojipublic var id_allowEmoji: Bool?/// 正则表达式public var id_predicateString: String?/// 是否支持文本高度跟随内容变化而变化public var id_supportAutoHeight = false4.IDPopView为了达到高度定制内容的需求,IDPopView只提供了一个盛放内容的容器,容器内的具体内容是需要外界传入的。基本使用如下/// self.contentView 是一个外界UIViewlet pop = IDPopView.init(contentView: self.contentView)/// 箭头的位置IDPopViewArrowPosition.left、center、right、custompop.id_arrowPosition = .left/// 箭头的位置pop.id_trianglePoint = CGPoint.init(x: KScreenWidth-30, y: 120)/// popview展示在的具体的位置pop.showInRect(rect: CGRect.init(x: KScreenWidth-160-10, y: 128, width: 160, height: self.contentView.frame.height + 16))5.IDProgressViewIDProgressView继承自UIProgressView,初始化的时候设置了主题色IDProgressCircleView展示的是圆形进度条、饼图进度条。部分属性:/// value的范围是0-100public var id_value: CGFloat = 0 /// 默认是2public var id_lineWidth: CGFloat = 2 /// 颜色public var id_fillColor: UIColor = (ColorConfig.share.mainColor ?? UIColor.white)/// 类型 .circle、.piepublic var id_type: IDProgressCircleViewType = .circle 6.IDSearchBarIDSearchBar的样式参考蓝湖部分属性如下/// 占位文字public var id_placeHolder: String = “请输入搜索关键字”/// 搜索图片public var id_searchImage: UIImage? = UIImage(named: “ic_ssearch”, in: BundleUtil.getCurrentBundle(), compatibleWith: nil)/// 搜索框背景颜色public var id_textFieldBgColor = UIColor.white/// 搜索框字体大小public var id_textFieldFont = UIFont.systemFont(ofSize: 12) /// 右边按钮的文字public var id_cancelButtonTitle = “取消” /// 右边按钮的文字颜色public var id_cancelButtonTitleColor = RGBAColor(95,95,95,1) /// 右边按钮的文字字体public var id_cancelButtonTitleFont = UIFont.systemFont(ofSize: 13) /// 是否显示右边的按钮public var id_showRightBtn = false /// 搜索容器的背景色public var id_containerBgColor = RGBAColor(239, 239, 244, 1) /// 搜索框的圆角大小public var id_cornerRadius: CGFloat = 2 7.IDSwitchIDSwitch继承自UIControl, 默认宽80,高度408.IDTextField部分属性/// 最多允许输入多少个字符public var maxLength: Int?/// 只允许输入数字和小数点public var onlyNumberAndPoint: Bool?/// 设置小数点位数public var pointLength: Int?/// 只允许输入数字public var onlyNumber: Bool?/// 禁止输入表情符号emojipublic var allowEmoji: Bool?/// 正则表达式public var predicateString: String?9.IDSelectViewIDSelectView目前提供3种类型的视图,详细参数较多,请参考demo事例。该视图对JXSegmentedView的再次封装,提取了常用的功能点。public enum IDSelectViewType { case normal // 标题 case number // 含有数字显示 case dots // 含有原点显示}IDUtils (扩展方法和工具类)String的扩展方法/// 字符串截取函数,截取到指定的位置public func id_subString(to index: Int) -> String { …}/// 字符串截取函数,从指定位置开始截取public func id_subString(from index: Int) -> String { …}/// 从指定位置开始截取, 截取到指定的位置public func id_subString(from index: Int, offSet: Int) -> String { … }UIView的扩展方法public var id_x: CGFloatpublic var id_y: CGFloatpublic var id_height: CGFloatpublic var id_width: CGFloatpublic var id_size: CGSizepublic var id_centerX: CGFloatpublic var id_centerY: CGFloat/// 设置渐变色public func id_addGradientLayer(gradientColors: [UIColor],gradientDirection direction: UIViewGradientDirection, gradientFrame: CGRect? = nil) {…}/// 设置圆角public func id_border(borderWidth: CGFloat?, borderColor:UIColor?,cornerRadius:CGFloat?) -> UIView{…}/// 设置指定部位的圆角public func id_borderSpecified( specified: UIRectCorner,cornerRadius:CGFloat) -> UIView {… }URL的扩展// 过滤字符串中的特殊字符public static func id_init(string:String) -> URL? {…}Array的扩展// 去重public func id_filterDuplicates<E: Equatable>( filter: (Element) -> E) -> [Element] {…}UIColor的扩展// 通过字符串设置colorpublic convenience init(hexString: String) {…}// 通过rgb值设置颜色convenience init(redValue: Int, green: Int, blue: Int, alpha: CGFloat) {..}UIDevice的扩展// 是否是iphonex 系列public static func id_isX() -> Bool {…}// 是否是ios11以上的系统public static func id_isIOS11() -> Bool {..}UIViewController的扩展// 获取当前控制器public func id_getCurrentViewcontroller() -> UIViewController?{…} ...

February 28, 2019 · 4 min · jiezi

iOS数字倍数动画

前言一个简单的利用 透明度和 缩放 实现的 数字倍数动画实现思路上代码 看比较清晰// 数字跳动动画- (void)labelDanceAnimation:(NSTimeInterval)duration { //透明度 CABasicAnimation *opacityAnimation = [CABasicAnimation animationWithKeyPath:@“opacity”]; opacityAnimation.duration = 0.4 * duration; opacityAnimation.fromValue = @0.f; opacityAnimation.toValue = @1.f; //缩放 CAKeyframeAnimation *scaleAnimation = [CAKeyframeAnimation animationWithKeyPath:@“transform.scale”]; scaleAnimation.duration = duration; scaleAnimation.values = @[@3.f, @1.f, @1.2f, @1.f]; scaleAnimation.keyTimes = @[@0.f, @0.16f, @0.28f, @0.4f]; scaleAnimation.removedOnCompletion = YES; scaleAnimation.fillMode = kCAFillModeForwards; CAAnimationGroup *animationGroup = [CAAnimationGroup animation]; animationGroup.animations = @[opacityAnimation, scaleAnimation]; animationGroup.duration = duration; animationGroup.removedOnCompletion = YES; animationGroup.fillMode = kCAFillModeForwards; [self.comboLabel.layer addAnimation:animationGroup forKey:@“kComboAnimationKey”];}利用一个透明度从 0 ~ 1之间的alpha,然后缩放 之后加到动画组实现一下就好了切记动画完成最好移除 否则可能引起动画内存问题这里设置斜体字体self.comboLabel.font = [UIFont fontWithName:@“AvenirNext-BoldItalic” size:50];看着比较明显最后按钮点击的时候调用- (IBAction)clickAction:(UIButton *)sender { self.danceCount++; [self labelDanceAnimation:0.4]; self.comboLabel.text = [NSString stringWithFormat:@"+ %tu",self.danceCount];}如果实现 dozen动画的话很简单, danceCount % 10 == 0 求模就行了.总结这个动画比较适合 有些直播场景的点击操作计数相关.iOS数字倍数动画Demo获取,可加iOS开发交流群:624212887,获取Demo,以及更多iOS技术资料 ...

February 27, 2019 · 1 min · jiezi

Block中可以修改全局变量,全局静态变量,局部静态变量吗?

原文:iOS面试题大全可以.深入研究Block捕获外部变量和__block实现原理全局变量和静态全局变量的值改变,以及它们被Block捕获进去,因为是全局的,作用域很广静态变量和自动变量,被Block从外面捕获进来,成为__main_block_impl_0这个结构体的成员变量自动变量是以值传递方式传递到Block的构造函数里面去的。Block只捕获Block中会用到的变量。由于只捕获了自动变量的值,并非内存地址,所以Block内部不能改变自动变量的值。Block捕获的外部变量可以改变值的是静态变量,静态全局变量,全局变量Block就分为以下3种_NSConcreteStackBlock:只用到外部局部变量、成员属性变量,且没有强指针引用的block都是StackBlock。 StackBlock的生命周期由系统控制的,一旦返回之后,就被系统销毁了,是不持有对象的_NSConcreteStackBlock所属的变量域一旦结束,那么该Block就会被销毁。在ARC环境下,编译器会自动的判断,把Block自动的从栈copy到堆。比如当Block作为函数返回值的时候,肯定会copy到堆上_NSConcreteMallocBlock:有强指针引用或copy修饰的成员属性引用的block会被复制一份到堆中成为MallocBlock,没有强指针引用即销毁,生命周期由程序员控制,是持有对象的_NSConcreteGlobalBlock:没有用到外界变量或只用到全局变量、静态变量的block为_NSConcreteGlobalBlock,生命周期从创建到应用程序结束,也不持有对象ARC环境下,一旦Block赋值就会触发copy,__block就会copy到堆上,Block也是__NSMallocBlock。ARC环境下也是存在__NSStackBlock的时候,这种情况下,__block就在栈上ARC下,Block中引用id类型的数据有没有__block都一样都是retain,而对于基础变量而言,没有的话无法修改变量值,有的话就是修改其结构体令其内部的forwarding指针指向拷贝后的地址达到值的修改

February 25, 2019 · 1 min · jiezi

如何优化 App 的启动耗时?

原文:iOS面试题大全iOS 的 App 启动主要分为以下步骤:打开 App,系统内核进行初始化跳转到 dyld 执行。这个过程包括这些步骤:1)分配虚拟内存空间;2)fork 进程;3)加载 MachO (自身所有的可执行 MachO 文件的集合)到进程空间;4)加载动态链接器 dyld 并将控制权交给 dyld 处理。在这个过程中内核会产生 ASLR(Address space layout randomization) 随机数值,这个值用于加载的 MachO 起始地址在内存中的偏移,随机的地址可防止 MachO 代码扫描并被 hack,提升安全性。通过 ASLR 虽然可随机化各内存区基地址,但无法将程序内的代码段和数据段随机化,如果绕过(bypass) ASLR 依然可进行篡改,就需要结合 PIE(Position Independent Executable) 共同使用。与之相似的还有 PIC(Position Independent Code),位置无关代码,作用于共享库代码。PIE/PIC 技术需要在编译阶段开启。顾名思义,PIC 可将程序代码装载到任意地址,这样就内部的指针不能靠固定的绝对地址访问,而通过相对地址指令如 adrp 来获取代码和数据。进入 dyld 动态链接器,它负责将一个 App 处理为一个可运行的状态,包含:加载 MachO 的依赖库(这些依赖库也是 MachO 格式的文件)。dyld 从可执行 MachO 文件的依赖开始, 递归加载所有依赖的动态库。 动态库包括:iOS 中用到的所有系统动态库:加载 OC runtime 方法的 libobjc,系统级别的 libSystem(例如 libdispatch(GCD) 和 libsystem_blocks(Block));其他 App 自己的动态库。根据 Apple 的描述,大部分 App 所加载的库在 100~400 个。不过 iOS 系统库已经被特殊优化过,如提前加入共享缓存,提前做好地址修正等。Fix-ups(地址修正),包括 rebasing 和 binding 等。ASLR + PIE 技术增强了程序的安全性,使得依赖固定地址进行攻击的方法失效,但也增加了程序自身的复杂度,MachO 文件的 rebase 和 bind info 等部分以及启动时的 fix-ups 地址修正阶段就是配合它而产生的。ObjC 环境配置。经过了 MachO 程序和依赖库的加载以及地址修正之后,dyld 所做的大部分事情已经完成了。在这一阶段,dyld 开始对主程序的依赖库进行初始化工作,而初始化的执行部分会回调到依赖库内部执行,如 ObjC 的运行时环境所在的 libobjc.A.dylib 以及 libdispatch.dylib 等。ObjC Setup 的过程,主要是对 ObjC 数据进行关联注册:1)dyld 将主程序 MachO 基址指针和包含的 ObjC 相关类信息传递到 libobjc;2)ObjC Runtime 从 __DATA 段中获取 ObjC 类信息,由于 ObjC 是动态语言,可以通过类名获取其实例,所以 Runtime 维护了一个映射所有类的全局类名表。当加载的数据包含了类的定义,类的名字就需要注册到全局表中;3)获取 protocol、category 等类相关属性并与对应类进行关联;4)ObjC 的调用都是基于 selector 的,所以需要对 selector 全局唯一性进行处理。以上步骤由 dyld 启动 libSystem.dylib 统一对基础库进行调用执行,这里面就包含了 libobjc 的 Runtime,同时 Runtime 会在 dyld 绑定回调,当 dyld 处理完相关数据后就会调用 ObjC Runtime 执行 Setup 工作。执行各模块初始化器。从这一步就开始接近上(业务)层:1)通过 ObjC Runtime 在 dyld 注册的通知,当 MachO 镜像准备完毕后,dyld 会回调到 ObjC 中执行 +load() 方法,包括以下步骤:a)获取所有 non-lazy class 列表;b)按继承以及 category 的顺序将类排入待加载列表;c)对待加载列表中的类进行方法判断并调用 +load() 方法。2)执行 C/C++ 初始化构造器,如通过 attribute((constructor)) 注解的函数。3)如果包含 C++,则 dyld 同样会回调到 libc++ 库中对全局静态变量、隐式初始化等进行调用。查找并跳转到 main() 函数入口。到了最后,dyld 回到 Load command,找到 LC_MAIN,拿到 entryoff 再加上 MachO 在内存的加载首地址(首地址就是内核传来的 slide 偏移)就得到了 main() 的入口地址,从而进入我们显式的程序逻辑。进入 main() -> UIApplicationMain -> 初始化回调 -> 显示UI。iOS 的 App 启动时长大概可以这样计算:t(App 总启动时间) = t1(main 调用之前的加载时间) + t2(main 调用之后的加载时间)。t1 = 系统 dylib(动态链接库)和自身 App 可执行文件的加载。t2 = main 方法执行之后到 AppDelegate 类中的 application:didFinishLaunchingWithOptions:方法执行结束前这段时间,主要是构建第一个界面,并完成渲染展示。在 t1 阶段加快 App 启动的建议:尽量使用静态库,减少动态库的使用,动态链接比较耗时。如果要用动态库,尽量将多个 dylib 动态库合并成一个。尽量避免对系统库使用 optional linking,如果 App 用到的系统库在你所有支持的系统版本上都有,就设置为 required,因为 optional 会有些额外的检查。减少 Objective-C Class、Selector、Category 的数量。可以合并或者删减一些 OC 类。删减一些无用的静态变量,删减没有被调用到或者已经废弃的方法。将不必须在 +load 中做的事情尽量挪到 +initialize 中,+initialize 是在第一次初始化这个类之前被调用,+load 在加载类的时候就被调用。尽量将 +load 里的代码延后调用。尽量不要用 C++ 虚函数,创建虚函数表有开销。不要使用 atribute((constructor)) 将方法显式标记为初始化器,而是让初始化方法调用时才执行。比如使用 dispatch_once(),pthread_once() 或 std::once()。在初始化方法中不调用 dlopen(),dlopen() 有性能和死锁的可能性。在初始化方法中不创建线程。在 t2 阶段加快 App 启动的建议:尽量不要使用 xib/storyboard,而是用纯代码作为首页 UI。如果要用 xib/storyboard,不要在 xib/storyboard 中存放太多的视图。对 application:didFinishLaunchingWithOptions: 里的任务尽量延迟加载或懒加载。不要在 NSUserDefaults 中存放太多的数据,NSUserDefaults 是一个 plist 文件,plist 文件被反序列化一次。避免在启动时打印过多的 log。少用 NSLog,因为每一次 NSLog 的调用都会创建一个新的 NSCalendar 实例。每一段 SQLite 语句都是一个段被编译的程序,调用 sqlite3_prepare 将编译 SQLite 查询到字节码,使用 sqlite_bind_int 绑定参数到 SQLite 语句。为了防止使用 GCD 创建过多的线程,解决方法是创建串行队列, 或者使用带有最大并发数限制的 NSOperationQueue。线程安全:UIKit只能在主线程执行,除了 UIGraphics、UIBezierPath 之外,UIImage、CG、CA、Foundation 都不能从两个线程同时访问。不要在主线程执行磁盘、网络、Lock 或者 dispatch_sync、发送消息给其他线程等操作。 ...

February 21, 2019 · 2 min · jiezi

思岚中小型机器人底盘Apollo怎么样?

思岚科技以zues为代表的机器人底盘产品在酒店、机场、银行、餐厅等领域已获得社会各界的高度肯定,为满足更多应用场景,思岚科技又推出中小型机器人底盘Apollo,为中小型机器人开发提供了完整可靠的机器人自主定位导航解决方案,降低了研发时间、精力和成本,实现快速开发。据了解,思岚中小型机器人底盘Apollo配备超声波传感器,防跌落传感器,深度摄像头传感器等多种传感器,采用激光SLAM技术,无需对环境进行修改,即可在未知场景中完成实时定位并测绘高精度地图。采用SharpEdgeTM精细化构图技术构建高精度、厘米级别地图,超高分辨率,不存在累加误差。同时,构建的地图规则、精细,直接使用,无需二次优化修饰,直接满足使用预期。在复杂多变,环境不受控制的应用场所(如商场、写字楼等人流量大场所)行走时,可实时动态识别环境中的人或者移动障碍物,并进行灵活规避和路线规划。遇到玻璃、镜面等高透材质障碍物时,不会像无头苍蝇一头撞上,超声波传感器能让Apollo及时识别、避让此类障碍物,继续行走。遇到边界、台阶等存在高度落差的情况时,不会“一脚踩空”,防跌落传感器可帮助Apollo及时识别并刹住“脚”,避免跌落。除上述核心功能以外,中小型机器人底盘Apollo的扩展接口还集成了网口,供电接口和各种控制接口以便用户快速进行开发扩展。中小型机器人底盘Apollo可通过有线网络或WIFI与外部通信,其本身自带的电池可以为自身与外接的扩展模块供电,用户可以通过各种控制接口对整个Apollo及其上层扩展模块进行控制。Apollo作为中小型机器人底盘,满足了基于完整可靠的底层应用,自定义开发上层应用。在技术和生产的研发上节省大量的时间、精力和成本,且价格优于市场的同类产品,具备超高性价比。

January 25, 2019 · 1 min · jiezi

iOS播放器、Flutter高仿书旗小说、卡片动画、二维码扫码、菜单弹窗效果等源码

iOS精选源码全网最详细购物车强势来袭一款优雅易用的微型菜单弹窗(类似QQ和微信右上角弹窗)swift, UITableView的动态拖动重排CCPCellDragger高仿书旗小说 Flutter版,支持iOS、AndroidNKAVPlayer 轻量级视频播放、控制,iOS AVPlayerRN 仿微信朋友圈SwiftScan 二维码/条形码扫描、生成,仿微信、支付宝Mac上解压Assets.car文件的小工具cartooltispr-card-stack - swift 卡片风格动画切换组件及完整交互示例。Flutter仿写单读App,同时支持iOS和AndroidiOS优质博客CAEmitterLayer 粒子动画最近有点时间,研究了一下CAEmitterLayer粒子动画效果,分享出来,以备自己以后使用,先看一下基本的效果吧:首先,说一下CALayer 经常使用到的一些类然后说一下管理CALayer内容的几个函数addSublayer: 添加子图层removeFromSuperlayer将自己从… 阅读原文线程安全: 互斥锁和自旋锁(10种)无并发,不编程.提到多线程就很难绕开锁.iOS开发中较常见的两类锁:1. 互斥锁: 同一时刻只能有一个线程获得互斥锁,其余线程处于挂起状态.2. 自旋锁: 当某个线程获得自旋锁后,别的线程会一直做循环,尝试加锁,当超过了限定的次数仍然没有成功获得锁时,线程也会被挂起.自旋锁较适用于锁的持有者保存时间较短的情况下,实际使… 阅读原文深入浅出iOS编译前言两年前曾经写过一篇关于编译的文章《iOS编译过程的原理和应用》,这篇文章介绍了iOS编译相关基础知识和简单应用,但也很有多问题都没有解释清楚:Clang和LLVM究竟是什么源文件到机器码的细节Linker做了哪些工作编译顺序如何确定头文件是什么?XCode是如何找到头文件的?Clang Module签名是什么?为什… 阅读原文iOS | 多态的实际运用一句话概括多态:子类重写父类的方法,父类指针指向子类。或许你对多态的概念比较模糊,但是很可能你已经在不经意间运用了多态。比如说:有一个tableView,它有多种cell,cell的UI差异较大,但是它们的model类型又都是一样的。由于这几种cell都具有相同类型的model,那么你肯定会先建一个基类cell,如:@… 阅读原文iOS开发之多种Cell高度自适应实现方案的UI流畅度分析本篇博客的主题是关于UI操作流畅度优化的一篇博客,我们以TableView中填充多个根据内容自适应高度的Cell来作为本篇博客的使用场景。当然Cell高度的自适应网上的解决方案是铺天盖地呢,今天我们的重点不是如何讨论Cell高度的自适应,而是给出几种Cell高度自适应的解决方案,然后对比起UI流畅度,从而得出一些UI优… 阅读原文更多源码更多博文

January 23, 2019 · 1 min · jiezi