1、ViewController生命周期
init
据经验,在同一个runloop运行循环
loadView
ViewDidLoad
ViewWillAppear
ViewDidAppear
ViewWillDisappear
ViewDidlDisappear
2、fastlane (小团队的福音)
- Ruby 2.0 以上
- Xcode Toolline(CLT)
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()<NSCopying,NSMutableCopying>
@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) 保证程序不退出
(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