MG–斯维夫特3.0 好用的归类

by admin on 2019年10月4日

老司机,又到周末啦,平时我们工作中会写很多的工具类,但是有一些使用工具类去使用又不是很方便,我们就把它抽取封装成分类。来
看一下今天要写的关于手势的干货。

    • 1.它是强类型的。这意味着你不能改变一个元组的类型一旦被创建,这样的代码不能编译:

    • 举个栗子:已经明确了是String类型,后面又加入Int类型,就会报错

      图片 1截图

    • 2.如果你不命名元素的元组,您可以访问它们使用数字计数从0,像这样:

      • 举个栗子:

        图片 2截图.png

    • 3.元祖中又有元祖可以用..快速访问,比如singer.2.1 和
      singer.address.1
      。通常应该通过名称元素去寻找,这样访问它们更合理:

      • 举个栗子:写法是一样的

        图片 3元祖中又有元祖


无限级分类注意,级分类

无限级分类主要救赎采用递归算法和特殊的数据表设计实现的

我的代码

 1 /**
 2      * 获取顺序的菜单信息
 3      * 使用递归方法
 4      */
 5     public function getHierarchicalMenus($pid , $condition=array() , $cid = 0){
 6         $data = array();
 7         if (!isset($condition['status'])){
 8             $condition['status'] = array('neq' , -1);
 9         }
10         $condition['parentid'] = $pid;
11         $menus = $this->_db->where($condition)->order("listorder desc")->select();
12         if ($menus){
13             foreach ($menus as $v){
14                 $res = $this->getHierarchicalMenus($v['menu_id'] ,$condition ,$cid+1);
15                 if ($res){
16                     $v['child'] = $res;
17                     $v['cid'] = $cid;
18                 }else{
19                     $v['child'] = 0;
20                 }
21                 $v['cid'] = $cid;
22                 $data[] = $v;
23             }
24         }
25         return $data;
26     }

老师的代码

 1 public function getTree()
 2     {
 3         $data = $this->select();
 4         return $this->_reSort($data);
 5     }
 6     private function _reSort($data, $parent_id=0, $level=0, $isClear=TRUE)
 7     {
 8         static $ret = array();
 9         if($isClear)
10             $ret = array();
11         foreach ($data as $k => $v)
12         {
13             if($v['parent_id'] == $parent_id)
14             {
15                 $v['level'] = $level;
16                 $ret[] = $v;
17                 $this->_reSort($data, $v['id'], $level+1, FALSE);
18             }
19         }
20         return $ret;
21     }

明显不同,我设计的无限级是使用array进行嵌套,那么前台的显示就有很大的问题了,

嵌套几层相应的显示就要几层判断加循环,然而老师的使用的是二维array,通过level

来判断层级,顺序由于持续深入不变,同时在递归之前先将全部数据先记录下来,减少

数据库操作次数,提高数据库并发操作。

无限级分类主要救赎采用递归算法和特殊的数据表设计实现的 我的代码 1 /* *
2 * 获取顺序的菜单信息 3 * 使用递归…

图片 4OFO.png图片 51.png图片 62.png图片 73.png

图片 8OFO.gif图片 9手动输入车牌.gif图片 10计费.gif

    • 图片 11导航栏的透明和颜色渐变.gif
self.view.addGestureRecognizer(UITapGestureRecognizer(target: self, action: #selector(tapClickfunc tapClick(tap: UITapGestureRecognizer) { // 手势点击后要执行的代码 }

图片 12

图片 13扫一扫,关注我.jpg

  • 注意:这些闭包不能访问兄弟元素,这意味着这样的代码不工作:
    print("\替换为print("My name is \: \之后

  • ### 2.Returning multiple values

    • 举个栗子

self.messageLabel.addGestureRecognizer(UITapGestureRecognizer(actionBlock: { [unowned self] in // 要执行的代码 }))
func fetchWeather() -> (type: String, cloudCover: Int, high: Int, low: Int) { return ("Sunny", 50, 32, 26)}let weather = fetchWeather()print(weather.type)
import UIKit// MARK: - ⚠️方法一extension UINavigationBar { // MARK: - RunTime private struct NavigationBarKeys { static var overlayKey = "overlayKey" } var overlay: UIView? { get { return objc_getAssociatedObject(self, &NavigationBarKeys.overlayKey) as? UIView } set { objc_setAssociatedObject(self, &NavigationBarKeys.overlayKey, newValue as UIView?, objc_AssociationPolicy.OBJC_ASSOCIATION_RETAIN_NONATOMIC ) } } // MARK: - 接口 func Mg_setBackgroundColor(backgroundColor: UIColor) { if overlay == nil { self.setBackgroundImage, for: UIBarMetrics.default) overlay = UIView.init(frame: CGRect.init(x: 0, y: 0, width: bounds.width, height: bounds.height+20)) overlay?.isUserInteractionEnabled = false overlay?.autoresizingMask = UIViewAutoresizing.flexibleWidth } overlay?.backgroundColor = backgroundColor subviews.first?.insertSubview(overlay!, at: 0) } func Mg_setTranslationY(translationY: CGFloat) { transform = CGAffineTransform.init(translationX: 0, y: translationY) } func Mg_setElementsAlpha(alpha: CGFloat) { for (_, element) in subviews.enumerated() { if element.isKind(of: NSClassFromString("UINavigationItemView") as! UIView.Type) || element.isKind(of: NSClassFromString("UINavigationButton") as! UIButton.Type) || element.isKind(of: NSClassFromString("UINavBarPrompt") as! UIView.Type) { element.alpha = alpha } if element.isKind(of: NSClassFromString("_UINavigationBarBackIndicatorView") as! UIView.Type) { element.alpha = element.alpha == 0 ? 0 : alpha } } items?.forEach in if let titleView = item.titleView { titleView.alpha = alpha } for BBItems in [item.leftBarButtonItems, item.rightBarButtonItems] { BBItems?.forEach({ (barButtonItem) in if let customView = barButtonItem.customView { customView.alpha = alpha } }) } }) } /// viewWillDisAppear调用 func Mg_reset() { setBackgroundImage(nil, for: UIBarMetrics.default) overlay?.removeFromSuperview() overlay = nil }}// MARK: -// MARK: -// MARK: - ⚠️方法二extension UINavigationBar { // MARK: - 接口 /** * 隐藏导航栏下的横线,背景色置空 viewWillAppear调用 */ func star() { let shadowImg: UIImageView? = self.findNavLineImageViewOn(view: self) shadowImg?.isHidden = true self.backgroundColor = nil } /** 在func scrollViewDidScroll(_ scrollView: UIScrollView)调用 @param color 最终显示颜色 @param scrollView 当前滑动视图 @param value 滑动临界值,依据需求设置 */ func change(_ color: UIColor, with scrollView: UIScrollView, andValue value: CGFloat) { if scrollView.contentOffset.y < -value{ // 下拉时导航栏隐藏,无所谓,可以忽略 self.isHidden = true } else { self.isHidden = false // 计算透明度 let alpha: CGFloat = scrollView.contentOffset.y / value > 1.0 ? 1 : scrollView.contentOffset.y / value //设置一个颜色并转化为图片 let image: UIImage? = imageFromColor(color: color.withAlphaComponent self.setBackgroundImage(image, for: .default) } } /** * 还原导航栏 viewWillDisAppear调用 */ func reset() { let shadowImg = findNavLineImageViewOn(view: self) shadowImg?.isHidden = false self.setBackgroundImage(nil,for: .default) } // MARK: - 其他内部方法 //寻找导航栏下的横线 (递归查询导航栏下边那条分割线) fileprivate func findNavLineImageViewOn(view: UIView) -> UIImageView? { if (view.isKind(of: UIImageView.classForCoder && view.bounds.size.height <= 1.0) { return view as? UIImageView } for subView in view.subviews { let imageView = findNavLineImageViewOn(view: subView) if imageView != nil { return imageView } } return nil } // 通过"UIColor"返回一张“UIImage” fileprivate func imageFromColor(color: UIColor) -> UIImage { //创建1像素区域并开始图片绘图 let rect = CGRect(x: 0, y: 0, width: 1, height: 1) UIGraphicsBeginImageContext(rect.size) //创建画板并填充颜色和区域 let context = UIGraphicsGetCurrentContext() context!.setFillColor(color.cgColor) context!.fill //从画板上获取图片并关闭图片绘图 let image = UIGraphicsGetImageFromCurrentImageContext() UIGraphicsEndImageContext() return image! }}

相比较来说:不再需要繁琐地使用 selector
去实现,也解决了代码分离的问题。就好像把delegate封装成了闭包,比如很多人使用的蓝牙框架第三方BabyBluetooth就是这么干的,它对系统的基于CoreBlueTooth的封装,内部把代理实现都转变成了Block形式。这样做的目的是代码封装性好,可读性强,易于维护。

图片 14返回多个值

  • ### 方法一: 简单使用

    func scrollViewDidScroll(_ scrollView: UIScrollView) { let color = UIColor(red: CGFloat(0 / 255.0), green: CGFloat(175 / 255.0), blue: CGFloat(240 / 255.0), alpha: CGFloat let offsetY: CGFloat = scrollView.contentOffset.y if offsetY < 50 { let alpha: CGFloat = max(0, 1 - ((50 + 64 - offsetY) / 64)) self.navigationController?.navigationBar.Mg_setBackgroundColor(backgroundColor: color.withAlphaComponent titleLabel.textColor = UIColor.white } else { self.navigationController?.navigationBar.MgsetBackgroundColor(backgroundColor: color.withAlphaComponent titleLabel.textColor = UIColor.black } } override func viewWillAppear( animated: Bool) { super.viewWillAppear self.navigationController?.navigationBar.setBackgroundImage, for: .default) self.scrollViewDidScroll(collectionView) } override func viewWillDisappear(_ animated: Bool) { super.viewWillDisappear navigationController?.navigationBar.Mg_reset() }

  • ### 方法二:简单使用

    /// 方法二:简单使用 func scrollViewDidScroll( scrollView: UIScrollView) { self.navigationController?.navigationBar.change(UIColor.orange, with: scrollView, andValue: 64) } override func viewWillAppear( animated: Bool) { super.viewWillAppear self.navigationController?.navigationBar.star() scrollViewDidScroll(collectionView) } override func viewWillDisappear(_ animated: Bool) { super.viewWillDisappear self.navigationController?.navigationBar.reset() }

func inspect<T> { print("Received \(type(of: value)) with the value \}
// 封装的手势闭包回调import UIKitextension UIGestureRecognizer { typealias MGGestureBlock = -> // MARK:- RuntimeKey 动态绑属性 // 改进写法用枚举实现绑定的key 写的时候会有提示 fileprivate struct RuntimeKey { static let mg_GestureBlockKey = UnsafeRawPointer.init(bitPattern: "mg_GestureBlockKey".hashValue) /// ...其他Key声明 } // 便利构造方法 convenience init(actionBlock: @escaping MGGestureBlock) { self.init() addActionBlock(actionBlock) addTarget(self, action: #selector(invoke } // 内部方法 加上fileprivat防止外界直接调用 fileprivate func addActionBlock(_ block: MGGestureBlock?) { if (block != nil) { objc_setAssociatedObject(self, UIGestureRecognizer.RuntimeKey.mg_GestureBlockKey, block!, .OBJC_ASSOCIATION_RETAIN_NONATOMIC) } } // 内部方法 @objc fileprivate func invoke(_ sender: Any) { let block = objc_getAssociatedObject(self, UIGestureRecognizer.RuntimeKey.mg_GestureBlockKey) as? MGGestureBlock if (block != nil) { block!; } }}
  • 传入的参数是String类型:

| 项目 | 简介 || : | : || MGDS_Swif | 逗视视频直播 || MGMiaoBo |
喵播视频直播 || MGDYZB | 斗鱼视频直播 || MGDemo | n多小功能合集 ||
MGBaisi | 高度仿写百思 || MGSinaWeibo | 高度仿写Sina || MGLoveFreshBeen
| 一款电商App || MGWeChat | 小部分实现微信功能 || MGTrasitionPractice |
自定义转场练习 || DBFMDemo | 豆瓣电台 || MGPlayer | 一个播放视频的Demo
|| MGCollectionView | 环形图片排布以及花瓣形排布 || MGPuBuLiuDemo |
瀑布流–商品展 || MGSlideViewDemo | 一个简单点的侧滑效果,仿QQ侧滑 ||
MyResume | 一个展示自己个人简历的Demo || GoodBookDemo | 好书 |

inspect(value: "明哥")

图片 15Snip20161026_15.png图片 16Snip20161026_16.png图片 17Snip20161026_35.png

  • ### 开发过程中遇到了的坑。

  • 我一开始在类方法里面进行了动态绑定,错误代码如下:

图片 18FF77A052-62A1-42A6-A098-C3F15052CA36.png

  • 传入的参数是Int类型

图片 19逗视介绍1.gif图片 20逗视介绍2.gif

//以下是错误代码:convenience init(actionBlock block: MGGestureBlock) { return self.init(target: self.gestureRecognizerBlockTarget, selector: #selector(self.invoke))}class func gestureRecognizerBlockTarget(_ block: MGGestureBlock) -> MGGestureRecognizerBlockTarget { var target: MGGestureRecognizerBlockTarget? = objc_getAssociatedObject(self, target_key) if target == nil { target = MGGestureRecognizerBlockTarget(block: block) objc_setAssociatedObject(self, target_key, target, OBJC_ASSOCIATION_RETAIN_NONATOMIC) } return target!}

这样导致的结果就是,变量被绑到了这个类对象上,因为在类方法里面 self
指的是这个类对象,而类对象是常驻内存直到程序退出才释放的,这也就导致了这个类上始终绑着第一次的
�target,之后无论怎样都不会改变。如果恰好你在 block 中有强制持有了 block
外的其他对象,那就会导致这些对象都不会释放,造成内存泄露。在实例方法中动态绑定即可解决。

inspect(value: "123")
  • 如果不使用动态绑定,使用如下的代码会产生怎样的结果?

图片 21FE471D99-57C8-4714-AE3D-E078D27710BE.png

//错误代码convenience init(actionBlock block: MGGestureBlock) { return self.init(target: MGGestureRecognizerBlockTarget(block: block), selector: #selector(self.invoke))}

结果就是出了这个作用域 target 对象释放。通过查阅文档,我在《OC
编程概念》的 Target-Action 一节中,看到苹果有提到这么一句: Control
objects do not (and should not) retain their targets.
按照惯例,如果有特殊情况,苹果会特别提醒(比如 NSTimer 的描述中就写到
target The timer maintains a strong reference to this object until it
(the timer) is invalidated. )。所以 UIGestureRecognizer 是不会对 target
强引用。一旦出了作用域,target
对象就释放了。所以,需要使用动态绑定强制持有。

extension Float: Numeric {}extension Double: Numeric {}extension Int: Numeric {}protocol Numeric { static func *(lhs: Self, rhs: Self) -> Self // 平方 static func +(lhs: Self, rhs: Self) -> Self // 相加}func squareSomething<T: Numeric> -> T { return value * value}func addSomething<T: Numeric> -> T { return value + value}squareSomething(value: 1.2)addSomething(value: 1.2)

类似的UIButton也一样的

图片 22

extension UIButton { typealias MGButtonBlock = -> // MARK:- RuntimeKey 动态绑属性 // 改进写法 fileprivate struct RuntimeKey { static let mg_BtnBlockKey = UnsafeRawPointer.init(bitPattern: "mg_BtnBlockKey".hashValue) /// ...其他Key声明 } convenience init(actionBlock: @escaping MGButtonBlock) { self.init() addActionBlock(actionBlock) addTarget(self, action: #selector(invoke, for: .touchUpInside) } fileprivate func addActionBlock(_ block: MGButtonBlock?) { if (block != nil) { objc_setAssociatedObject(self, UIButton.RuntimeKey.mg_BtnBlockKey, block!, .OBJC_ASSOCIATION_RETAIN_NONATOMIC) } } @objc fileprivate func invoke(_ sender: Any) { let block = objc_getAssociatedObject(self, UIButton.RuntimeKey.mg_BtnBlockKey) as? MGButtonBlock if (block != nil) { block!; } } convenience init(imageName: UIImage, title: String,actionBlock: @escaping MGButtonBlock) { self.init(actionBlock: actionBlock) // 1.设置按钮的属性 setImage(imageName, for: .normal) setTitle(title, for: UIControlState.normal) titleLabel?.font = UIFont.systemFont(ofSize: 14) setTitleColor(UIColor.darkGray, for: UIControlState.normal) sizeToFit() } convenience init(title: String,actionBlock: @escaping MGButtonBlock) { self.init(actionBlock: actionBlock) // 1.设置按钮的属性 setTitle(title, for: UIControlState.normal) titleLabel?.font = UIFont.systemFont(ofSize: 14) setTitleColor(UIColor.darkGray, for: UIControlState.normal) sizeToFit() } convenience init(norImage:UIImage,pressImage: UIImage,actionBlock: @escaping MGButtonBlock) { self.init(actionBlock: actionBlock) // 1.设置按钮的属性 setImage(norImage, for: .normal) setImage(pressImage, for: .highlighted) } convenience init(norImage:UIImage,selectedImage: UIImage,actionBlock: @escaping MGButtonBlock) { self.init(actionBlock: actionBlock) // 1.设置按钮的属性 setImage(norImage, for: .normal) setImage(selectedImage, for: .selected) }}

图片 13扫一扫,关注我.jpg

struct deque<T> { var array = [T]() mutating func pushBack { array.append } mutating func pushFront { array.insert(obj, at: 0) } mutating func popBack() -> T? { return array.popLast() } mutating func popFront() -> T? { if array.isEmpty { return nil } else { return array.removeFirst() } }}// 创建一个dequevar testDeque = deque<Int>(array: [2313,321,32,24])testDeque.pushBacktestDeque.pushFronttestDeque.pushFronttestDeque.pushFronttestDeque.pushFronttestDeque.popFront()testDeque.popBack()print(testDeque)

图片 24

struct CountedSet<T: AnyObject> { var internalSet = NSCountedSet() func addObject { internalSet.add } func removeObject { internalSet.remove } func countForObject -> Int { return internalSet.count }}var countedSet = CountedSet<NSString>()countedSet.addObjectcountedSet.addObjectcountedSet.addObjectcountedSet.countForObjectcountedSet.countForObjectprint(countedSet)var countedSet2 = CountedSet<NSNumber>()countedSet2.addObjectprint(countedSet2)

图片 25

  • ##### 通过学习我们自己知道,在Swift中,布尔值、数字、字符串、数组、字典、结构等等都是值类型。类是引用类型,但也闭包

  • ##### 闭包是引用,我们从截图可以看到值是往上递增的

  • 举个实际的🌰:下面的代码是一个createIncrementer()函数,它不接受参数并返回一个闭包:

![](https://upload-images.jianshu.io/upload_images/1429890-706728018cc6e263.png)incrementer和incrementerCopy指向同一个指针.png![](https://upload-images.jianshu.io/upload_images/1429890-9051a667e51face4.png)incrementer和incrementerCopy指向不是同一个指针
  • ##### 最简单的操作符:”=”

  • 举个🌰: 这会打印Match!

![](https://upload-images.jianshu.io/upload_images/1429890-38e291002f2cb6a6.png)操作符:"=".png
  • 但是我对操作符进行重写之后,请看👇的代码

    图片 26对操作符进行重写.png

  • 操作符进行重写。求立方

    图片 27图片 28操作符重写

  • ##### 介绍一个~=操作符。~=这个关系运算符实现的功能和包含函数一样

let test1 = .containslet test2 = 1...100 ~= 42let dictString = "Danger, Will Robinson: mathematics failure!"dictString ~= "计算机"dictString.contains

图片 29~=这个关系运算符实现的功能和包含函数一样

  • 普通写法

let namees = ["Michael Paul", "Keybe brinte", "Michael Cofe", "ming ming", "Michael Jordan"]let result = names.filter({ name in if name.hasPrefix("Michael") { return true } else { return false; }})print // 返回符合要求的结果,打印如图
  • 第一种写法。可以省略 return关键字

let result = namees.filter({ name in return name.hasPrefix("Michael")})print // 返回符合要求的结果,打印如图
  • 第二种写法。可以省略 return关键字

let result = namees.filter { return $0.hasPrefix("Michael") }print // 返回符合要求的结果,打印如图
  • 打印结果:

    图片 30三种方法打印结果是一样的.png

  • 一个字符串包含某个数组中的单词

let words = ["Swift","iOS","cocoa","OSX","tvOS"]let tweet = "This is an example tweet larking about Swift"words.contains(where: tweet.contains) // Print// 或者这样写tweet.characters.split(separator: " ").lazy.map(String.init).contains(where: Set.contains) // Print// 再或者这么写words.filter({ tweet.contains // 打印的是Swift
    • 第一种写法:

let mg_numbers = [1, 3, 5, 7, 9]let number_result = mg_numbers.reduce { (int1, int2) -> Int in int1 + int2} // 累加数组中的元素print(number_result)
  • 第二种写法:

let number_result = mg_numbers.reduce { $0 + $1} // 累加数组中的元素
  • 第三种写法:

let result = reduceNumbers.reduce
  • 打印结果:

    图片 31打印结果.png

  • 扩展:不仅限于Int类型,字符串也是可以的。补充一个注意点:除法的时候,必须要有浮点型或者Double类型,否则结果是0图片 32图片 33看图

  • 例子1:
    然后依次取出原来序列中的元素,根据过滤结果将它放到第一个或第二个部分中。数组变成了元祖,元祖中包含两个数组。图片 3497684EDF-D7DB-46AE-AFB1-DA61C169E0DD.png
  • 例子2:

let scores = [100, 90, 95]let result = scores.reduce { $0 + String }print;

图片 35打印结果.png

let scores = [100, 8, 6,"欢迎你的来电"] as [Any]let result = scores.reduce { $0 + String(describing: $1) }print;

图片 36打印结果.png

  • 例子3:找出数组中字符最长的元素
    • reduce的写法

let names = ["Taylor", "Paul", "Adele", "mingmming", "Swift","Michael Jordan"]let longest = names.reduce { $1.characters.count > $0.characters.count ? $1 : $0 }
  • 系统给的写法

names.max { $1.characters.count > $0.characters.count } // 最长names.min(by: {$1.characters.count > $0.characters.count}) // 最短

图片 37找出数组中字符最长的元素.png

  • 例子4:reduce统计数组所有字符的个数,下面我们会学到统计数组中每一个元素的个数

let names = ["Taylor", "Paul", "Adele"]let count = names.reduce { $0 + $1.characters.count }print

图片 38打印结果.png

  • 例子5: reduce的多个数组转一个数组
    • reduce的写法

let numbers = [ [1, 1, 2], [3, 5, 8], [13, 21, 34] ]let flattened: [Int] = numbers.reduce { $0 + $1 }print(flattened)
  • flatMap一句代码就搞定了

numbers.flatMap { $0 }

图片 39reduce的多个数组转一个数组.png

  • 例子6:
    reduce的并或的关系。第一个&&:所有名字长度大于4;第二个||:只要有一个名字的长度大于4即是真。

let names = ["Taylor", "Paul", "Adele"]let longEnough = names.reduce { $0 && $1.characters.count > 4 }let longEnough1 = names.reduce { $0 || $1.characters.count > 4 }

图片 40reduce的并或的关系.png

• false && false == false``• true && false == false``• false && true == false``• true && true == true

• false || false == false
• true || false == true``• false || true == true
• true || true == true

    • 举个🌰1:

let π = Doublelet max = 8.0 * πlet ds = stride(from:0.0, to:max, by:π / 6.0).map({1 - cos

图片 41Sina

  • 举个🌰2:

let moneyArray = [213,432,54,123,321]let stringsArray = moneyArray.map明明MG"})print(stringsArray)

图片 42类型转换.png

    • 使用
      Split()方法可以分解字符串或者其他序列,追加一个任意类型的分隔符,来决定具体在什么位置进行分割。可以把字符串分割成数组
    • Split

let lipsum = "This is an example tweet larking about Swift."let words1 = lipsum.characters.split{$0 == " "}.map(String.init)print
  • components

let splitedArray = lipsum.components(separatedBy: " ")print("拆分后的数组:\(splitedArray)")

图片 43拆分后的数组

  • 我们来举个🌰 ,密码提示错误,抛出异常
  • 异常枚举

enum PasswordError: Error { case Empty // 密码为空 case Short(minChars: Int) // 密码不能少于 case Obvious(message: String) // 明显的错误}
  • 校验方法

func encryptString(str: String, withPassword password: String) throws -> String { if password.isEmpty { throw PasswordError.Empty } if password == "12345" { throw PasswordError.Obvious(message: "I have the same number on my luggage") } if password.characters.count < 4 { throw PasswordError.Short(minChars: 4) } let encrypted = password + str + password return String(encrypted.characters.reversed}

func testCatch() { do { let encrypted = try encryptString(str: "secret information!", withPassword: "1234567") print("You password is: \(encrypted)") } catch PasswordError.Empty { print("You must provide a password.") } catch PasswordError.Obvious(let message) { print("Your password is obvious: \") } catch PasswordError.Short(let minChars) where minChars < 4 { print("We have a lax security policy: passwords must be at least \") } catch PasswordError.Short(let minChars) where minChars < 8 { print("We have a moderate security policy: passwords must be at least \") } catch PasswordError.Short(let minChars) { print("We have a serious security policy: passwords must be at least \") }catch { print }}
  • 测试:

 let encrypted = try encryptString(str: "secret information!", withPassword: "")

图片 44密码是空.png

let encrypted = try encryptString(str: "secret information!", withPassword: "123")

图片 45密码长度不够.png

let encrypted = try encryptString(str: "secret information!", withPassword: "12345")

图片 46密码已经存在.png

let encrypted = try encryptString(str: "secret information!", withPassword: "1234567")

图片 47显示新创建的密码.png

  • ### Swift的错误处理有三种形式,所以三个有其用途。所有调用调用标记的函数时都使用,但有细微的不同含义:

    • ##### 1.使用try时,必须有一个catch块来处理发生的任何错误。

    • ##### 2.使用时try?如果抛出任何错误,您调用的函数将自动返回nil。您不需要捕捉它们,但您需要知道您的返回值成为可选的。

    • ##### 3.使用时try!如果抛出任何错误,函数将崩溃你的应用程序。

  • ### 如何选择:

    • 使用try?是比较安全的,系统会帮你处理异常,当明智地使用,会提高您的代码的可读性;使用try!一般是抛出一个错误是不太可能的
      或者说
      强制,我可以接受崩溃”,如果你确定它不会抛出一个错误,就用try!;使用try的话,一般和do/catch
      这些闭包一起代用,自己处理异常,这个代码量是最大的。
  • ### 断言允许您声明某些条件必须是真的。条件是给你的,可以是复杂的,但如果它为false的话您的程序将立即停止。

  • 简单举个栗子:

![](https://upload-images.jianshu.io/upload_images/1429890-a181c6fc2e288e2e.png)![](https://upload-images.jianshu.io/upload_images/1429890-e9126f9d92805dd3.png)Assertions种植程序.png
    • 举个栗子。我们来写一个数组中,对用位置的元素相乘的算法。这个示例的先决条件是两个数组的个数必须相等,不相等的话就终止程序。

func *(lhs: [Int], rhs: [Int]) -> [Int] { precondition(lhs.count == rhs.count, "Arrays were not the same size") var result = [Int]() for (index, int) in lhs.enumerated() { result.append(int * rhs[index]) } return result}[1, 2] * [4, 5, 2][12, 5] * [5, 6]

图片 48数组个数不对应,报错.png
图片 49数组个数对应,数组相乘结果.png

  • ### 我们通过一个例子学习👇的东西

    // 统计一个数组中每一个字符个数的函数func lengthOfStrings(strings: [String]) -> [Int] { var result = Int for string in strings { result.append(string.characters.count) } return result}

  • 通过Map简化后统计一个数组中每一个字符个数的函数

func lengthOfStrings(strings: [String]) -> [Int] { return strings.map { $0.characters.count }}
  • 经过测试两者打印的记过一样的

    图片 50测试结果

    • 通过Map,可以快速对数组中的每一个元素进行一些操作

    • 例子:

let fruits = ["Apple", "Cherry", "Orange", "Pineapple"]let upperFruits = fruits.map({ $0.uppercasedprint(upperFruits)

图片 51转化成大写.png

  • 对数组中的元素进行过滤
    • 数组的元素添加东西

let mg_scores = [100, 80 ,43, 48, 85, 70, 60, 55]let formatted = mg_scores.map { "Your score was \" }

- 数组的元素是否及格

let passOrFail = mg_scores.map { $0 > 60 ? "Pass" : "Fail" }print(passOrFail)

- 数组的元素是否在某一个范围之内

let position = [50, 60, 40]let averageResults = position.map { 45...55 ~= $0 ? "Within average" : "Outside average" }print(averageResults)

 - 打印结果截图

图片 52数组的元素添加东西.png图片 53数组的元素是否及格.png图片 54数组的元素是否在某一个范围之内.png图片 55开平方.png

  • 返回可选值的方法

func fetchUsername -> String? { if id == 1989 { return "明哥" } else{ return nil }}
  • 普通写法

let username = fetchUsernamelet formattedUsername: Stringif let username = username { formattedUsername = "Welcome, \!"} else { formattedUsername = "Unknown user"}
  • Map写法

let username = fetchUsernamelet formattedUsername = username.map { "Welcome, \!" } ?? "Unknown user"
    • ##### 它跟Map有密切的联系,也遍历数组和执行的每一项功能。区别在于返回值:map()返回新数组中的项目,而foreach()什么也不返回–只是功能性的方式遍历每个项目(这样子的话速度稍微快一点点点点)。除了返回值,foreach是一样的。

    • ##### Map()已经很完善,但是flatMap()也有它的使用场景。当一个数组包含数组的数组。 你得到一个新的方法称为flatten()数组,数组的数组,它转换成一个数组,像这样:
      >

    let manyNumbers = [[1, 2], [3, 4], [5, 6]]let flattened = Array(manyNumbers.joined // 2.x语法使用flatten() 函数

图片 56多个数组变成一个数组

let albums: [String?] = ["Fearless", nil, "Speak Now", nil, "Red"]let albumsResult = albums.map { $0 }print("map: \(albumsResult)") // 打印的是可选类型print("flatMap: \(albums.flatMap { $0 })") // 会自动过滤空值,打印的是确定类型

图片 57👀👀结果.png

到了这里,如果你还不是很清楚flatmap()是否有用,那么继续👀下面- 再看一个🌰

let flatMapscores = ["100", "90", "Fish", "85"]let flatMapResult = flatMapscores.flatMap { Int }let mapResult = flatMapscores.map { Int }print("flatMapscores: \(flatMapscores)")print("flatMapResult: \(flatMapResult)") // flatMapResult: [100, 90, 85]print("mapResult: \(mapResult)") // mapResult: [Optional, Optional, nil, Optional]

图片 58打印结果.png


这个技术可以用于当你需要区分成功与失败之间的代码使用的可选性。任何有抛异常的函数都可用于try?去处理,它在函数转换为失败时返回nil。


图片 59测试代码以及结果.png


解释:这个测试的代码将加载到一个数组中的somefile-1.txt、somefile-2.txt、somefile-3.txt内容等等。不存在的任何文件都会抛出一个异常,try?会转换为nil和flatmap()将忽略,所有执行的操作只需要一行代码!

  • 通过学习了上面这么多函数,我想你已经晕😲了,这需要一段时间去熟悉之后,才会理清思路。如果你足够清醒,那就继续往下看。

  • filter()函数,如果你还不知道和上面的map()、flatMap()等有什么区别,那么我们来看下面的例子。(附上代码和打印结果,才有说服力)

let fibonacciNumbers = [1, 2, 3, 5, 8, 13, 21]let evenFibonacci = fibonacciNumbers.filter { $0 % 2 == 0 }print("filter: \(evenFibonacci)")print("map: \(fibonacciNumbers.map { $0 % 2 == 0 })")print("flatMap: \(fibonacciNumbers.flatMap { $0 % 2 == 0 })")

图片 60三者之间的区别图片 61再👀一个🌰.png

  • 解释:
    filter返回了符合要求的元素。而map和flatMap返回的是条件是否是符合要求的,而且它把所有的元素判断的结果都返回在了一个数组中
  • 再看一个我们之前看过的例子:

let words = ["ming", "Fearless", "Red"]let input = "ming favorite Color is Red"let result = words.filter { input.contains}

图片 62不包含返回为空数组图片 63返回包含的字符数组.png

  • 再看一个例子:

let words: [String?] = ["1989", nil, "Fearless", nil, "Red"]let input = "My favorite album is Fearless"let result = words.filter { $0 != nil }printlet result1 = words.flatMap { $0 }print

图片 64结果截图.png

  • ##### 最后,filter()表现奇怪的是当一个字典:你返回来的结果是元组数组,而不是一个字典。因此,你可以使用数组下标访问到一个项目,项目,0。1的键和值

let scores = ["Paul": 100, "Taylor": 95, "Adele": 90, "Michael": 85, "Justin": 60]let goodScores = scores.filter { $1 > 85 }goodScoresprint(goodScores[0].1)print(goodScores[1].1)print(goodScores[2].1)

图片 65打印结果

  • ### 结论:filter英文就是过滤的意思。所以它是带条件的筛选,它的运用场景就是数组中需要判断某个条件是否成立时经常用到

图片 13扫一扫,关注我.jpg

发表评论

电子邮件地址不会被公开。 必填项已用*标注

网站地图xml地图