iOS 小知识点(三)

1、ViewController生命周期

init

据经验,在同一个runloop运行循环
loadView
ViewDidLoad
ViewWillAppear

ViewDidAppear
ViewWillDisappear
ViewDidlDisappear

2、fastlane (小团队的福音)

iOS中使用Fastlane实现自动化打包和发布
官方文档

  • Ruby 2.0 以上
  • Xcode Toolline(CLT)
iOS 小知识点(三)_第1张图片
屏幕快照 2019-01-25 下午12.19.03.png

3、Singleton 单例模式

  • Objective-c单例模式的几种写法有什么不同
  • Swift 单例的实现与解析
  • What is a Singleton and How to create one in Swift
  • Files and Initialization - Swift Blog - Apple Developper
  • Avoiding singletons in Swift - John Sundell - Medium

简单来说, 就是保证在你不主动销毁这个单例对象的情况下, 整个项目中都始终拥有这个单例对象, 并且这个单例对象在内存中都是同一个内存地址。 所以, 单例很重要的两个特点:

  • (1) app生命周期中一直存在(除主动销毁外)
  • (2) 在整个生命周期中, 都是同一个内存地址

iOS 中的单例

  • UIApplication.shard :每个应用程序有且只有一个UIApplication实例,由UIApplicationMain函数在应用程序启动时创建为单例对象。
  • NotificationCenter.defualt:管理 iOS 中的通知
  • FileManager.defualt:获取沙盒主目录的路径
  • URLSession.shared:管理网络连接
  • UserDefaults.standard:存储轻量级的本地数据
  • SKPaymentQueue.default():管理应用内购的队列。系统会用 StoreKit framework 创建一个支付队列,每次使用时通过类方法 default() 去获取这个队列。

Objective-C 单例的标准实现

#import "Singleton.h"
@interface Singleton()
@end
@implementation Singleton
static Singleton* _instance = nil;
+(instancetype) shareInstance
{
    static dispatch_once_t onceToken ;
    dispatch_once(&onceToken, ^{
        _instance = [[super allocWithZone:NULL] init] ;
        //不是使用alloc方法,而是调用[[super allocWithZone:NULL] init]
        //已经重载allocWithZone基本的对象分配方法,所以要借用父类(NSObject)的功能来帮助出处理底层内存分配的杂物
    }) ;
    
    return _instance ;
}
+(id) allocWithZone:(struct _NSZone *)zone
{
    return [Singleton shareInstance] ;
}
-(id) copyWithZone:(NSZone *)zone
{
    return [Singleton shareInstance] ;//return _instance;
}
-(id) mutablecopyWithZone:(NSZone *)zone
{
    return [Singleton shareInstance] ;
}
@end

Swift 单例的标准实现

class MyManager {
    // 全局变量
    static let shared = MyManager(string: someString)

    // Properties
    let string: String

    // private  Initialization
    private init(string: String) {
        self.string = string
    }
}

⚠️为什么如此简洁的代码就能实现单例?其实要归功于Swift的新特性:

  • 关于 Swift 中全局变量的懒加载
  • 关于 Swift 中的 dispatch_once 和 原子性
  • static关键字,用于限定变量的作用域为“全局变量”。
  • 构造器使用了 private 关键字,所以也保证了单例的原子性。

全局变量的懒加载在初始化时会使用 dispatch_once 以确保初始化的原子性。所以这是一个很酷地使用 dispatch_once 的方式:仅在定义全局变量时将其构造器标志为 private 就行。

3、触摸事件传递过程

控件能接收事件的基本条件
(1) view.userInteractionEnabled == YES;
(2) view.hidden == NO;
(3) view.alpha > 0.01
(4) 该触摸点是否落在该控件上
(5) hitTest它只负责找最合适的view来接受这个事件
(6) 如果要拦截事件必须实现touch方法,因为父类的默认处理是把事件抛给上一个响应者
 /**
 * 从该View的层次结构中寻找最佳的事件接收者,如果没有则返回nil
 *
 *  @param point 在该view上的坐标系的点
 *  @param event 事件对象
 *
 *  @return 最佳接收该事件的view nil 则没有最佳接收该事件的人选,事件往上抛
 */
- (UIView *)hitTest:(CGPoint)point withEvent:(UIEvent *)event{
    //     1. 判断它 userInteractionEnabled == YES hidden == NO alpha > 0.01
    if ( self.userInteractionEnabled == NO || self.hidden == YES || self.alpha <= 0.01 ) return nil;
    //     2. 判断触摸点是否落在该控件上(在该控件的坐标系中: x > 0 && y > 0)
    if ( [self pointInside:point withEvent:event] == NO ) return nil;
    //    3. 从后往前遍历它的子控件,看它的子控件是否满足以上条件
    NSInteger count = self.subviews.count;
    for (NSInteger i = count - 1; i >= 0; i--) {
        UIView *subView = self.subviews[i];
        CGPoint subP = [self convertPoint:point toView:subView];
        UIView *target = [subView hitTest:subP withEvent:event];
        if ( target != nil ) {
            return target;
        }
    }
    // 如果子控件没有合适人选,来到这里就足以证明控件自身满足接收该事件的人选
    return self;
}

4、RunLoop( 运行循环)

  1. 在应用程序启动的时候,系统首先会启动一个主运行循环来给应用程序与用户交互,在这个过程中系统不断重复这个运行循环来跟用户交互,当用户的与手机产生交互到手机作出相应地反应为一次运行循环

1.1 运行循环的作用 :
(1) 保证程序不退出
(2) 监听所有事件,例如:手势触摸,时钟触发,网络加载数据完成等

  1. 一个完整的运行循环如下图
    iOS 小知识点(三)_第2张图片
    1377227-48a8ece4925f266d.png
  • (1) 当用户触摸屏幕时候,CocoaTouch会产生一个触摸事件对象
  • (2) 接着CocoaTouch会产生一个自动释放池,然后会把此触摸事件对象发送给应用来处理这个事件,通常我们要在这个环节来处理我们的事件
  • (3) 当我们处理完事件之后,给用户做出了反应,那么此自动释放池就会被销毁,销毁前它会遍历此释放池中的所有对象给他们各发送一条release消息
  • (4) 注意如果在子线程中需要用到autorelease的对象,需要我们自己创建自动释放池

注意:子线程中不会帮我们自动创建自动释放池,特别在自定义 NSOperation 的 main 方法中,如果我们不自己添加 @autoreleasepool 的话,很容易会发生内存泄露

5、简述@selector的作用

参考文章

Selector 是什么???

Selector/SEL又叫方法选择器,SEL在objc.h中是这样声明的,而“@selector()”是取得一个SEL指针。说白了,方法选择器仅仅是一个char *指针,表示它所代表的是方法的名字。 简单来说: “@Selector 就是用字符串表示某个类的某个方法。” 更加专业的说法是: “Selector就是OC的虚拟表(virtual table)中指向实际执行的函数指针(function pointer)的一个C字符。”

我们一般用它来“因为method可以用字符串表示,因此,某个method就可以变成用来传递的参数。” 再说的透明一点, 因为 selector 可以看做是函数的另一个名字,所以很多需要调用函数或者建立连接的地方,都可以用到,以下是一些具体的使用场景:

  • Target/Action 模式
  • 检查 method 是否存在
  • Timer
  • 在线程中执行方法
  • 数组排序
  • 代替 if else / switch
  • 调用私有 API

你可能感兴趣的:(iOS 小知识点(三))