Ruby中的Hash(哈希),你可以理解为字典


以下代码在Ruby 2.5.1中编译通过

定义

myHash = Hash.new
myHash1 = Hash["key1" => 100, "key2" => 200]
myHash2 = Hash.new("defualtValue") # 创建带有默认值的Hash,当访问不存在的任意key,返回该值
myHash3 = Hash.new "defualtValue" # 创建带有默认值的Hash,当访问不存在的任意key,返回该值

puts myHash1["key1"] # defualtValue

赋值

myHash = {"key1" => "value1", "key2" => "value2"}

修改/添加元素

myHash["key3"] = 300 # key不存在添加则添加
myHash["key3"] = 200 # key存在则添加value

puts myHash["key3"] # 200

取值

value = myHash["key2"]

元素个数

count = myHash.count

key/value

keys = myHash.keys    # 获取所有key
values = myHash.values # 获取所有key

Hash遍历

方法1

myHash.each { |key, value|

  puts key
  puts value
}

方法2

for key in myHash.keys do

  value = myHash[key]
  puts value
end

方法3

for value in myHash.values do

  puts value
end

方法4

myHash.values.each { |value|

  puts value
}

方法5

myHash.keys.each { |key|

  puts key
}

Ruby 数组操作


以下代码在Ruby 2.5.1中编译通过

数组定义

myarray = Array.new(10) # 定义长度为10的数组
myarray1 = Array.new(4, "item") # 定义长度为4,并用 'item'填充
myarray2 = [12, 13, 14]

赋值

myarray = ['aa', 'bb', 'cc'] # 数组赋值
myarray1[0] = 10 # 对类型不敏感 # 修改元素的值

取值

value = myarray[2]

获取数组长度

length = myarray.length

追加元素

myarray << 30 # 在myarray后面追加一个元素

清空所有元素

myarray.clear

数组相加

myarray = myarray1 + myarray2 # 两个数组所有元素组成新数组

数组遍历

方法1

for item in myarray do

  puts item
end

方法2

myarray.each_index {|index|

  item = myarray[index]
  puts item
}

方法3

myarray.length.times do |index|
  
  item = myarray[index]
  puts item
end

方法4

myarray.each{|item| 
  
  puts item
}

Shell中的参数传递


我们先来定义一个方法

function methodName() {

paramName1=$1
paramName2=$2
paramName3=$3

# 依此类推,参数是从1开始标号,而不是0,切记

#
# do something
#

echo "${param1}${param2}${param3}"
}

上面我们定义了一个接受三个参数的方法,我们调用时需要传递三个参数,调用如下

result=`methodName "abc" "d" "ef"`
echo $result # abcdef

总结

  • Shell方法中参数不是放在()里的,而是在方法体中通过下标去获取
  • Shell在调用方法时,参数不是放在()传递,而是按方法名 (空格)参数1 (空格)参数2 (空格)参数3进行调用传递
  • Shell中方法的定义与调用,和其他语言有比较大的区别,特别调用方面

Shell 数组操作


数组定义

array=('ab' 'cd' 'dd')
array1=(1 2 3)

元素赋值

array[1]='dddd' # 第二个元素重新赋值

获取元素

value=${array[1]} # 获取第二个元素
echo $value #dddd

获取数组长度

count="${#array[*]}"
echo $count # 3

数组遍历

方式1

for item in ${array[*]}
do
echo $item
done

方式2

for index in ${!array[*]}
do
echo $index # 下标
echo ${array[$index]} # value
done

总结

  • Shell数组是用括号来表示,元素用"空格"来分开,这与其他编程语言(,)不同
  • 数组中涉及的 * 也可以用 @ 代替
  • 单引号用双引号代替也是可以的
  • Shell中赋值运算=的左右不能有空格,这是规定。

Swift 4.0 高级:自定义操作符


在Swift语言中,常见的操作符有+-*/><==&&||等等,如果不喜欢,你也可以定义自己喜欢的操作符。

操作符类型

  • 中置运算符(infix operator) e.g. +-/*
  • 前置运算符(prefix operator) e.g. --++
  • 后置运算符(postfix operator) e.g. --++
注意:在Swift初期(1.0,2.0)--++这些前(后)置运算符还是可以使用的,但是会有警告;但在Swift4.0已经不能使用了(编译无法通过),不过我们自己定义的前(后)置运算符是可以使用的。

自定义操作符

1.中置运算符

示例

/// 定义优先级组
precedencegroup MyPrecedence {
    
    // higherThan: AdditionPrecedence   // 优先级,比加法运算高
    lowerThan: AdditionPrecedence       // 优先级, 比加法运算低
    associativity: none                 // 结合方向:left, right or none
    assignment: false                   // true=赋值运算符,false=非赋值运算符
}

infix operator +++: MyPrecedence        // 继承 MyPrecedence 优先级组
// infix operator +++: AdditionPrecedence // 也可以直接继承加法优先级组(AdditionPrecedence)或其他优先级组
func +++(left: Int, right: Int) -> Int {
    
    return left+right*2
}

使用

let result = 2+++3
print(result) // 8

Precedence Group
Apple官方链接[Table 3 Infix operators]




2.前置运算符

定义前置运算符是不继承优先级组
示例

prefix operator ==+
prefix func ==+(left: Int) -> Int {
    
    return left*2
}

使用

print(==+2) // 4

3.后置运算符

定义后置运算符是不继承优先级组
示例

postfix operator +==
postfix func +==(right: Int) -> Int {
    
    return right*3
}

使用

print(2+==) // 6

iOS 原生库(AVFoundation)实现二维码扫描,封装的工具类,不依赖第三方库,可高度自定义扫描动画及界面(Swift 4.0)


Create QRScanner.swift file

//
//  QRScanner.swift
//  NativeQR
//
//  Created by Harvey on 2017/10/24.
//  Copyright © 2017年 Harvey. All rights reserved.
//

import Foundation
import AVFoundation
import UIKit

class QRScanner: NSObject {
    
    static let shared = QRScanner()
    
    private let captureSession = AVCaptureSession()
    private let videoPreviewLayer = AVCaptureVideoPreviewLayer()
    
    private var handleCompleted: ((String) -> ())? = nil
    
    private override init() {
        
        super.init()
        
        AVCaptureDevice.requestAccess(for: AVMediaType.video) { (isSuccess) in
        
            if isSuccess {
                
                self.prepare()
            }else {
                
                print("无权访问摄像机")
            }
        }
    }
    
    private func prepare() {
        
        guard let device = AVCaptureDevice.default(for: .video) else {
        
            print("获取摄像设备发生错误")
            return
        }
        
        guard let deviceInput = try? AVCaptureDeviceInput(device: device) else {
            
            print("创建设备输入流发生错误")
            return
        }
        
        // 创建数据输出流
        let metadataOutput = AVCaptureMetadataOutput()
        metadataOutput.setMetadataObjectsDelegate(self, queue: DispatchQueue.main)
        
        // 创建设备输出流
        let videoDataOutput = AVCaptureVideoDataOutput()
        videoDataOutput.setSampleBufferDelegate(self, queue: DispatchQueue.main)
        
        // 会话采集率: AVCaptureSessionPresetHigh
        captureSession.sessionPreset = .high
        
        // 添加数据输出流到会话对象
        captureSession.addOutput(metadataOutput)
        
        // 添加设备输出流到会话对象
        captureSession.addOutput(videoDataOutput)
      
        // 添加设备输入流到会话对象
        captureSession.addInput(deviceInput)
        
        // 设置数据输出类型
        metadataOutput.metadataObjectTypes = [
            .qr,        // 二维码
            .ean13,     // 条形码
            .ean8,      // 条形码
            .code128    // 条形码
        ]
        
        videoPreviewLayer.session = captureSession
        videoPreviewLayer.videoGravity = .resizeAspectFill
    }
    
    func scan(design: @escaping (_ previewLayer: CALayer)->()) -> Self {
        
        design(videoPreviewLayer)
        
        startRunning()
        
        return self
    }
    
    func completed(aCompleted: @escaping (_ value: String)->()) {
        
        self.handleCompleted = aCompleted
    }
    
    func startRunning() {
        
        captureSession.startRunning()
    }
    
    func stopRunning() {
        
        captureSession.stopRunning()
    }
}

extension QRScanner: AVCaptureMetadataOutputObjectsDelegate {
    
    func metadataOutput(_ output: AVCaptureMetadataOutput, didOutput metadataObjects: [AVMetadataObject], from connection: AVCaptureConnection) {
        
        guard metadataObjects.count>0 else{
            
            return
        }
        
        stopRunning()
        
        guard let stringValue = metadataObjects.first!.value(forKey: "stringValue") as? String else {
            
            return
        }
        
        handleCompleted?(stringValue)
    }
}

extension QRScanner: AVCaptureVideoDataOutputSampleBufferDelegate {
    
    func captureOutput(_ output: AVCaptureOutput, didDrop sampleBuffer: CMSampleBuffer, from connection: AVCaptureConnection) {
        
    }
}

使用示例

override func viewDidAppear(_ animated: Bool) {
    super.viewDidAppear(animated)
    
    QRScanner.shared
    .scan { (previewLayer) in
        
        /// 设置preview的frame
        let width = UIScreen.main.bounds.size.width - 100
        previewLayer.frame = CGRect(x: 50, y: 100, width: width, height: width)
        self.view.layer.insertSublayer(previewLayer, at: 0)
        
        // 你的扫描动画代码
        // ......
        // ......
    }
    .completed { (qrValue) in // 处理二维码
        
         print(qrValue)
        
        // 结束你的扫描动画代码
        // ......
        // ......
    }
}

代码及示例下载(有实现生成二维码)
Github
Coding


iOS 原生库(CoreImage)实现生成二维码,封装的工具类,不依赖第三方库,可自定义背景颜色,添加logo(Swift 4.0)


Ceate QRCreator.swift file

//
//  QRCreator.swift
//  NativeQR
//
//  Created by Harvey on 2017/10/24.
//  Copyright © 2017年 Harvey. All rights reserved.
//

import Foundation
import CoreImage
import UIKit

extension UIColor {
    
    var coreImageColor: CIColor {
        return CIColor(color: self)
    }
    
    var components: (red: CGFloat, green: CGFloat, blue: CGFloat, alpha: CGFloat) {
        
        let coreImageColor = self.coreImageColor
        return (coreImageColor.red, coreImageColor.green, coreImageColor.blue, coreImageColor.alpha)
    }
}

class QRCreateModel {
    
    /// 文本
    var text: String!
    
    /// 二维码中间的logo
    var logo: String?
    
    /// 二维码缩放倍数{27*scale,27*scale}
    var scale: Float = 10
    
    /// 二维码背景颜色
    var backgroundColor: UIColor = UIColor.white
    
    /// 二维码颜色
    var contentColor: UIColor = UIColor.black
}

class QRCreator {
    
    static let shared = QRCreator()
    
    private let qrFilter: CIFilter
    private let colorFilter: CIFilter
    
    private init() {
        
        /// 创建二维码滤镜
        qrFilter = CIFilter(name: "CIQRCodeGenerator")!
        
        /// 创建颜色滤镜
        colorFilter = CIFilter(name: "CIFalseColor")!
    }
    
    private func createBase(text: String, scale: Float) -> CIImage? {
        
        qrFilter.setDefaults()
        guard let data = text.data(using: String.Encoding.utf8) else {
            
            return nil
        }
        
        /// 给二维码滤镜设置inputMessage
        qrFilter.setValue(data, forKey: "inputMessage")
        guard var outputImage = qrFilter.outputImage else {
            
            return nil
        }
        
        outputImage = outputImage.transformed(by: CGAffineTransform(scaleX: CGFloat(scale), y: CGFloat(scale)))
        return outputImage
    }
    
    private func colourFilter(ciImage: CIImage, model: QRCreateModel) -> CIImage? {
        
        /// 颜色滤镜恢复默认值
        colorFilter.setDefaults()
        
        /// 设置颜色滤镜的inputImage
        colorFilter.setValue(ciImage, forKey: "inputImage")
        
        /// 设置inputImage的backgroundColor(key: inputColor1)
        colorFilter.setValue(model.backgroundColor.coreImageColor, forKey: "inputColor1")
        
        /// 设置inputImage的contentColor(key: inputColor0)
        colorFilter.setValue(model.contentColor.coreImageColor, forKey: "inputColor0")
        
        return colorFilter.outputImage
    }
    
    private func addLogo(ciImage: CIImage, model: QRCreateModel) -> UIImage? {
        
        guard let _ = model.logo,
            let logoImage = UIImage(named: model.logo!) else {
                
                return nil
        }
        
        let image = UIImage(ciImage: ciImage)
        let originX = (image.size.width - logoImage.size.width)/2.0
        let originY = (image.size.height - logoImage.size.height)/2.0
        
        UIGraphicsBeginImageContext(image.size)
        image.draw(in: CGRect(x: 0, y: 0, width: image.size.width, height: image.size.height))
        logoImage.draw(in: CGRect(x: originX, y: originY, width: logoImage.size.width, height: logoImage.size.height))
        
        let outPutImage = UIGraphicsGetImageFromCurrentImageContext()
        UIGraphicsEndImageContext()
        
        return outPutImage
    }
    
    func create(_ model: QRCreateModel) -> UIImage? {
        
        guard var outputImage = createBase(text: model.text, scale: model.scale) else {
            
            return nil
        }
        
        if let colorOutputImage = colourFilter(ciImage: outputImage, model: model) {
            
            outputImage = colorOutputImage
        }
        
        guard let qrImageWithLogo = addLogo(ciImage: outputImage, model: model) else {
            
            return  UIImage(ciImage: outputImage)
        }
        
        return qrImageWithLogo
    }
}

完整使用示例

let qrModel = QRCreateModel()
qrModel.text = "https://www.yaozuopan.top"
qrModel.contentColor = UIColor.yellow
qrModel.backgroundColor = UIColor.gray
    
/// 二维码的容错率最大为30%(即二维码被遮挡的部分不能大于30%,否则二维码无法被识别)
/// e.g.
/// 假设二维码大小为{100,100}, logo的大小最大为{30,30}
/// 在设置loog请注意
// qrModel.logo = "mylogo"
    
if let qrImage = QRCreator.shared.create(qrModel) {
    
    print(qrImage.size.width)
    print(qrImage.size.height)
    // imageView.image = qrImage
}

简洁使用

let qrModel = QRCreateModel()
qrModel.text = "https://www.yaozuopan.top"

if let qrImage = QRCreator.shared.create(qrModel) {

    // imageView.image = qrImage
}

代码及示例下载(有实现二维码扫描)
Github
Coding


调用iOS系统分享


Objective-C

/**
 调用iOS系统的分享

 @param path 文件路径
 @param aSourceView 源视图
 */
- (void)shareWithPath: (NSURL *)path sourceView:(UIView *)aSourceView {

    // 把不需要的UIActivityType都写到excludedActivities中
    NSArray *excludedActivities = @[UIActivityTypePostToTwitter,  // 发送到Twitter
                                    UIActivityTypePostToFacebook, // 发送到Facebook
                                    UIActivityTypePostToWeibo,    // 发送到新浪微博
                                    UIActivityTypePostToFlickr,   // 发送到Flickr
                                    UIActivityTypePostToVimeo,    // 发送到Vimeo
                                    UIActivityTypePostToTencentWeibo,// 发送到腾讯微博
                                    
                                    UIActivityTypeMail,           // 邮件
                                    UIActivityTypeAirDrop,        // AirDrop传输
                                    UIActivityTypeMessage,        // 短信
                                    UIActivityTypePrint,          // 打印
                                    UIActivityTypeCopyToPasteboard,// 拷贝到粘贴板
                                    UIActivityTypeAssignToContact, // 指定到联系人
                                    UIActivityTypeSaveToCameraRoll,// 保存到相机胶卷
                                    UIActivityTypeAddToReadingList,// 添加到阅读列表
                                    UIActivityTypeOpenInIBooks     // 使用Books打开
                                    ];

    // 初始化分享控制器
    UIActivityViewController *controller = [[UIActivityViewController alloc] initWithActivityItems:@[path] applicationActivities:nil];
    
    // 设置排除类型
    controller.excludedActivityTypes = excludedActivities;
    
    // 处理分享回调, 如果不需要可以设置可以为nil
    controller.completionWithItemsHandler = ^(UIActivityType  _Nullable activityType, BOOL completed, NSArray * _Nullable returnedItems, NSError * _Nullable activityError) {
        
        NSLog(@"activityType=%@", activityType);
        NSLog(@"completed=%d", completed);
        NSLog(@"returnedItems=%@", returnedItems);
        NSLog(@"activityError=%@", activityError);
    };
    
    // 适配iPad,否则会carsh
    if ([UIDevice currentDevice].userInterfaceIdiom == UIUserInterfaceIdiomPad) {
        
        controller.popoverPresentationController.sourceView = aSourceView;
    }
    
    // 显示分享控制器
    [self presentViewController:controller animated:YES completion:nil];
}

使用

- (IBAction)share:(UIButton *)sender {
    
    NSURL *path = [[NSBundle mainBundle] URLForResource:@"bkg" withExtension:@"png"];
    [self shareWithPath:path sourceView:sender];
}

Swift

/// 调用iOS系统的分享
///
/// - Parameters:
///   - withPath: 文件路径
///   - sourceView: 源视图
func share(withPath: URL, sourceView: UIView) {
    
    /// 把不需要的UIActivityType都写到excludedActivities中
    let excludedActivities: [UIActivityType] = [
        .postToTwitter,      // 发送到Twitter
        .postToFacebook,     // 发送到Facebook
        //.postToWeibo,        // 发送到新浪微博
        .postToFlickr,       // 发送到Flickr
        .postToVimeo,        // 发送到Vimeo
        //.postToTencentWeibo, // 发送到腾讯微博
        
        //.mail,               // 邮件
        .airDrop,            // AirDrop传输
        //.message,            // 短信
        .print,              // 打印
        .copyToPasteboard,   // 拷贝到粘贴板
        .assignToContact,    // 指定到联系人
        //.saveToCameraRoll,   // 保存到相机胶卷
        .addToReadingList,   // 添加到阅读列表
        .openInIBooks        // 使用Books打开
    ]
    
    /// 初始化分享控制器
    let controller = UIActivityViewController(activityItems: [withPath], applicationActivities: nil)
    
    /// 设置排除类型
    controller.excludedActivityTypes = excludedActivities
    
    /// 处理分享回调, 如果不需要可以设置可以为nil
    controller.completionWithItemsHandler = {
        (activityType: UIActivityType?, completed: Bool, returnedItems: [Any]?, activityError: Error?) in
        
        }
    
    /// 适配iPad,否则会carsh
    if UIDevice.current.userInterfaceIdiom == .pad {
        
        controller.popoverPresentationController?.sourceView = sourceView
    }
 
    /// 显示分享控制器
    present(controller, animated: true, completion: nil)
}

使用

@IBAction func share(_ sender: UIButton) {
    
    let path = Bundle.main.url(forResource: "myImage", withExtension: "png")!
    share(withPath: path, sourceView: sender)
}

效果图


iOS开发:十六进制颜色转UIColor


Objective-c 版本

UIColor * __nullable UIColorFromHexValue(NSUInteger hexValue) {
    
    CGFloat red = (hexValue & 0xFF0000) >> 16;
    CGFloat green = (hexValue & 0x00FF00) >> 8;
    CGFloat blue = hexValue & 0x0000FF;
 
    return [UIColor colorWithRed:red/255.0 green:green/255.0 blue:blue/255.0 alpha:1.0];
}

使用

// 复制上面代码放在任意头文件中,并引用该头文件,在需要的地方调用如下
UIColor *color = UIColorFromHexValue(0xFFFFFF);

Swift 版本

extension UIColor {

    static func hex(_ hexValue: UInt) -> UIColor {
        
        let red = (hexValue & 0xFF0000) >> 16
        let green = (hexValue & 0x00FF00) >> 8
        let blue = hexValue & 0x0000FF
        
         return UIColor(red: CGFloat(red)/255.0, green: CGFloat(green)/255.0, blue: CGFloat(blue)/255.0, alpha: 1.0)
    }
}

使用

let color = UIColor.hex(0xFFFFFF);

iOS核心动画(CoreAnimation)之CABasicAnimation的使用


了解CoreAnimation

  • CoreAnimation是苹果提供的一套基于绘图的动画框架。


上图是苹果给出的体系结构,最低层是硬件层(GPU),上层是OpenGL和CoreGraphics,再上层CoreAnimation,最上层就是UIKit.
所以CoreAnimation是基于OpenGL和CoreGraphics封装的,并不是直接接触硬件层.

  • CoreAnimation不是一个独立框架,而是封装在QuartzCore框架中.
  • CoreAnimation是作用于layer,不是View,这点要注意.

CABasicAnimation使用

CoreAnimation包含了许多的高级动画效果,CABasicAnimation只是其中一小部分,也是最基础动画.
CABasicAnimation的常用属性详解

  • timingFunction 可选

控制动画的运行节奏;有以下5种类型

kCAMediaTimingFunctionLinear均速(线性运行)
kCAMediaTimingFunctionEaseIn先缓慢进入,再加速离开
kCAMediaTimingFunctionEaseOut全速开始,慢慢减速
kCAMediaTimingFunctionEaseInEaseOut先慢慢加速,再慢慢减速。
kCAMediaTimingFunctionDefault默认值.先慢慢加速,再慢慢减速,但是它加速减速速度略慢
  • fromValue 必选

动画初始值(状态).类型:NSValue NSNumber

  • toValue 必选

动画结束值(状态).类型:NSValue NSNumber

  • duration 必选

每次动画时间

  • autoreverses 可选

默认为NO,当设置为YES时,表示启用回退动画,即由toValue到fromValue做回退动画

  • removedOnCompletion 可选

默认为YES,动画结束后从layer移除,当设置为NO时,不移除

  • fillMode 可选

控制动画开始之前或者动画结束之后layer的行为.有以下4种类型

kCAFillModeForwards在动画开始前,只需要将动画加入了一个layer,layer便立即进入动画的初始状态并等待动画开始
kCAFillModeBackwards当动画结束后,layer会一直保持着动画最后的状态
kCAFillModeBoth动画加入之后在开始之前,layer便处于动画初始状态,动画结束后layer保持动画最后的状态
kCAFillModeRemoved默认值.动画结束后,不改变layer的状态,layer会恢复到之前的状态
  • repeatCount 可选

动画重复次数.默认为0,不可与repeatDuration同时使用.(非重复动画不用关注此参数)

  • repeatDuration 可选

动画重复时间.不可与repeatCount同时使用,(系统会根据 repeatDuration/duration 计算出要做多少次动画).(非重复动画不用关注此参数)

动画实现

- (void)myAnimation {
    
    NSString *keyPath = @"position";
    CABasicAnimation *animation = [CABasicAnimation animationWithKeyPath:keyPath];
    
    // 速度控制函数,控制动画运行的节奏
    animation.timingFunction = [CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionEaseInEaseOut];
    
    // YES=启用回退动画,即由toValue到fromValue做回退动画
    animation.autoreverses = YES;
    
    // YES=动画结束时从layer移除
    animation.removedOnCompletion = YES;
    
    // 控制动画开始之前或者动画结束之后`layer`的行为
    animation.fillMode = kCAFillModeForwards;

    // 动画初始值(状态),必须设置
    animation.fromValue = [NSValue valueWithCGPoint:self.interactView.center];

    // 动画结束值(状态),必须设置
    animation.toValue = [NSValue valueWithCGPoint:self.targetView.center];
    
    // 重复次数
    animation.repeatCount = INT_MAX;
 
    // 每次动画时间,必须设置
    animation.duration = 1.8;
    
    // 添加到layer,并开始动;必须设置
    [arrowView.layer addAnimation:animation forKey:keyPath];
}

上面代码的动画效果

KeyPath 列表

苹果官方链接:KeyPath列表

注意: CoreAnimation是作用于layer,layer并没有frame属性,所以对frame是无效的,可通过positionbounds间接实现

Xcode-跑模拟器(Simulator)出现Pseudo Terminal Setup Error错误的解决方法


今天在做项目,运行到模拟器上突然出现如下错误,

做iOS那么多年,第一次遇过此错误,最初想重置Simulator应该能解决该问题,结果发现是我想多了,还是乖乖去百度一下,还是找到解决方法了。

在终端执行以下命令

sudo sysctl -w kern.tty.ptmx_max=999

Enter后输入电脑密码再按Enter

Swift4.0 中实现单例的三种方式


方式1(静态常量)

class MyClass {

    static let shared = MyClass()
    private init() { }
}
简洁的不要不要,我最喜欢使用此方式实现单例

方式2(内部结构体)

class MyClass {

    static var shared: MyClass {

        struct Static {
            static let sharedInstance = MyClass()
        }

        return Static.sharedInstance;
    }

    private init() { }
}
看起来是方式1的复杂版(变异版),在开发中我基本上不使用过此方式

方式3(全局变量)

fileprivate let sharedInstance = MyClass()
class MyClass {

    static var shared: MyClass {
        return sharedInstance
    }

    fileprivate init() { }
}

iOS开发之 NSLog 重定向


NSLog 重定向

不在控制台输出,而是定向到指定的地方统一处理。

为什么要对NSLog重定向

在开发中,我可能需要对日记保存,但在NSLog调用地方都调用保存操作,很不方便也不方便维护;当然你也可以使用第三方框架(比如CocoaLumberjack),但是你可能仅仅想保存日记这个操作,而不需要其他操作,并不想使用第三方框架,这时对NSLog重定向是一个很好的选择(毕竟习惯了NSLog)。

代码实现

我已封装成一个工具类,可以拿去使用。

Objective-C 版本

NSLogRedirect.h 代码

#import <Foundation/Foundation.h>

@interface NSLogRedirect : NSObject

+ (void)enableRedirect;

@end

NSLogRedirect.m 代码

#import "NSLogRedirect.h"

@implementation NSLogRedirect

/**
 在 application:didFinishLaunchingWithOptions: 方法中最先调用此方法即可对NSLog重定向
 */
+ (void)enableRedirect {
    
    static NSLogRedirect *sharedInstance;
    static dispatch_once_t onceToken;
    dispatch_once(&onceToken, ^{
        
        sharedInstance = [[NSLogRedirect alloc] init];
    });
}

- (instancetype)init
{
    self = [super init];
    if (self) {
        
        [self redirectToPostNotification:STDERR_FILENO];
    }
    return self;
}

- (void)redirectToPostNotification:(int)fileno {
    
    NSPipe *pipe = [NSPipe pipe];
    NSFileHandle *pipeReadHandle = [pipe fileHandleForReading];
    int pipeFileHandle = [[pipe fileHandleForWriting] fileDescriptor];
    
    dup2(pipeFileHandle, fileno);
    [[NSNotificationCenter defaultCenter] addObserver:self
                                             selector:@selector(redirectNotificationHandle:)
                                                 name:NSFileHandleReadCompletionNotification
                                               object:pipeReadHandle];
    [pipeReadHandle readInBackgroundAndNotify];
}

- (void)redirectNotificationHandle:(NSNotification *)notification {
    
    /** ==== 请不要在此函数中使用NSLog ====*/
    /** ==== 请不要在此函数中使用NSLog ====*/
    /** ==== 请不要在此函数中使用NSLog ====*/
    /** ==== 会导致死循环,会导致死循环,会导致死循环 ====*/
    
    NSData *data = [[notification userInfo] objectForKey:NSFileHandleNotificationDataItem];
    NSString *messageString = [[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding];
    NSArray<NSString *> *messages = [messageString componentsSeparatedByCharactersInSet: [NSCharacterSet characterSetWithCharactersInString:@"\n]"]];

    for (NSString *messageItem in messages) {
        
        if (!messageItem.length || [messageItem containsString:@"["]) {
            
            continue;
        }
        
        // 在此处理每一条log,messageItem为log的内容
        /** 比如:统一保存到指定文件中 */
        /** 比如:统一输出格式,使用printf打印出来 */
        /** ........... */
        printf("%s\n", messageItem.UTF8String);
    }

    [[notification object] readInBackgroundAndNotify];
}

- (void)dealloc {
    
    [[NSNotificationCenter defaultCenter] removeObserver:self];
}

@end

Swift 版本

comeing soon