iOS知识点

2019-06-11 已阅 203 次

iOS中MVC、MVVM

  • MVC
在 iOS 开发中,MVC(Model View Controller)是构建iOS App的标准模式,M(Model)是指业务数据;V(View)指用户界面,即UIView;C(Controller)是指控制器,即UIViewController。Controller可以直接与 Model、View对话,控制数据传递与视图切换;Model 通过 Notification 和 KVO 机制与 Controller 间接通信;View 通过关联action、代理(delegate)与 Controller 间接通信;Model 和 View 不能直接通信,需通过 Controller 传递。

优点

  1. 耦合性低
  2. 重用性高
  3. 可维护性高

缺点
1.臃肿的 ViewController

Controller协调模型和视图之间的所有交互。比如: 业务逻辑、数据转化、视图状态与切换等

2.遗失(无处安放)的网络逻辑

在MVC中并没有用来负责网络逻辑的层,你可能试着把它放在Model对象里,但是也会很棘手,因为网络调用应该使用异步,比持有它的model生命周期更长,事情将变的复杂。显然View里面做网络请求那就更格格不入了,因此只剩下Controller了, 但无疑增加了Controller工作量。若不这样,何处才是网络逻辑的家呢

3.太过于轻量级的 Model

4.较差的可测试性

由于View Controller混合了视图处理逻辑和业务逻辑,分离这些成分的单元测试成了一个艰巨的任务。
  • MVVM
MVVM就是在MVC的基础上分离出业务处理的逻辑到VM(ViewModel)层,减少Controller的工作量,M(Model)是指业务数据;V(View)指用户界面,即UIView;C(Controller)是指控制器,隐式存在,即UIViewController;VM(ViewModel)是指视图模型,负责业务处理和数据转化。在MVVM中核心是ViewModel,Controller 和 View 都不能直接和 Model 通信,必须通过 ViewModel;ViewModel 直接和 Model 通信,不能直接与 View 通信;而 Controller 负责常规的UI逻辑处理、View和ViewModel之间的通信,不涉及业务逻辑,只需要知道结果,而这个过程由ViewModel去做。

优点
1.低耦合

视图(View)可以独立于Model变化和修改,一个ViewModel可以绑定到不同的"View"上,当View变化的时候Model可以不变,当Model变化的时候View也可以不变

2.可重用性

你可以把一些视图逻辑放在一个ViewModel里面,让很多view重用这段视图逻辑

3.独立开发

开发人员可以专注于业务逻辑和数据的开发(ViewModel),设计人员可以专注于页面设计

4.可测试

界面素来是比较难于测试的,而现在测试可以针对ViewModel来写

目的

MVVM模式和MVC模式一样,主要目的是分离视图(View)和模型(Model)。

iOS怎么检查内存问题

  1. 通过Xcode -> Product -> Analyze 来分析代码是否潜在内存泄露问题
  2. 使用Xcode自带工具Instrument

Swift中guard、defer

  • guard
可以解决 if 嵌套问题。在保证条件满足情况下,才会让你通过, 否则在else中返回
  • defer
用于推迟执行,适合用来做清理、资源回收工作。在一个作用域中的defer语句自下而上调用。

iOS中UITableView的性能优化

1.对cell进行复用,不要重复创建Cell的实例
2.使用不透明视图

不透明的视图可以极大地提高渲染的速度。可以将cell及其子视图的opaque(不透明)属性设为YES(默认值)

3.不要做多余的绘制工作

在实现drawRect:的时候,它的rect参数就是需要绘制的区域,这个区域之外的不需要进行绘制

4.预渲染图像

如果有图片

5.不要阻塞主线程

耗时操作放在子线程进行,比如网络请求

6.减少预加载时间

缓存图片数据,提前计算Cell的高度并缓存起来,减少初始化的过程

7.避免使用图形特效

在UIImage中使用复杂的图形特效(例如渐变), Layer添加阴影

8.避免动态UI排版

用户滑动时动态添加子/移除视图或修改视图属性

9.滑动时按需加载对应的内容


iOS代理(Delegate)、通知(Notification)、协议(Protocol)的应用场景

  • Delegate
注重的是过程,是一对一的,对于一个协议(Protocol)就只能用一个代理,所以单例不能用代理。
  • Notification
一个消息通知机制,类似广播, 一对多的。观察者只需要向消息中心注册感兴趣的东西,当有地方发出这个消息的时候,通知中心会发送给注册这个消息的对象。
  • Protocol
一个自定义方法的集合,由Delegate去实现这些方法

通知(Notification)与代理(Delegate)的优缺点

  1. 通知可以一对多通信,代理只能一对一。
  2. 代理的执行效率比较高。
  3. 通知的使用比较简单, 但是需要注意通知的移除、重复注册问题。
  4. 通知太多的情况下,代码比较难维护,建议选择性使用。

iOS中的KVC、KVO

  • KVC
键值编码,是一种间接访问实例变量的方法。允许开发者通过Key名直接访问对象的属性,或者给对象的属性赋值,而不需要调用明确的存取方法,在运行时可动态地访问和修改对象的属性。
  • KVO
键值观察,基于KVC实现的,它能够观察一个对象的某个属性值的变化。其基本思想是:对目标对象的某属性添加观察,当该属性发生变化时,通过触发观察者对象实现的KVO接口方法,来自动的通知观察者。

注意

纯Swift类和结构体不支持KVC、KVO

实现一个单列

  • Objective-C
+ (MyClass *)shared {
    
    static MyClass *sharedInstance = nil;
    static dispatch_once_t onceToken;
    
    dispatch_once(&onceToken, ^{
        sharedInstance = [[MyClass alloc] init];
    });
    
    return sharedInstance;
}
  • Swift
class MyClass {
    
    static let shared = MyClass()
    private init() { }
}

iOS实现多线程技术

  • NSThread
Objective-C的线程对象,一个NSThread对象就是一条线程.

创建线程的方式有:

1. 创建线程不启动,需调用start方法启动
initWithTarget:selector:object:

2、创建并启动线程
detachNewThreadSelector:toTarget:withObject:

3、隐式创建并启动线程
performSelectorInBackground:withObject:
  • GCD
Apple 开发的一个多核编程的较新的解决方法。它主要用于优化应用程序以支持多核处理器以及其他对称多处理系统。它是一个在线程池模式的基础上执行的并发任务。

有以下优点:

1. GCD 可用于多核的并行运算
2. GCD 会自动利用更多的 CPU 内核(比如双核、四核)
3. GCD 会自动管理线程的生命周期(创建线程、调度任务、销毁线程)
4. 程序员只需要告诉 GCD 想要执行什么任务,不需要编写任何线程管理代码
  • NSOperation/NSOperationQueue
NSOperation/NSOperationQueue是基于GCD更高一层的封装,完全面向对象。NSOperation是个抽象类,并不具备封装操作的能力,需要子类化或者使用系统提供子类:NSInvocationOperation、NSBlockOperation

有以下优点:

1.可添加完成的代码块,在操作完成后执行
2.添加操作之间的依赖关系,方便控制执行顺序
3.设定操作的优先级
4.可以很方便的取消一个操作的执行
5.使用KVO观察对操作执行状态的更改:isExecuteing,isFinished,isCancelled

NSThread、GCD、NSOperation比较

  1. NSThread 轻量级,使用简单,难以管理多个线程,另外开发者需要管理线程的生命周期、线程同步等
  2. GCD不需要开发者管理线程(创建线程、调度任务、销毁线程),重心放在功能实现,与Block配合,使用起来非常灵活,代码简洁
  3. NSOperation 完全面向对象,不需要关心线程管理,数据同步的事情,可控制最大并发数量,可控制任务优先级,可设置任务间的依赖关系,可观察任务状态变化

栈和堆的区别

堆:动态分配内存,需要程序员自己申请,程序员自己管理
栈:自动分配内存,自动销毁,先入后出,栈上的内容存在自动销毁的情况


OC的类可以多继承吗?如果没有,有什么办法可以实现

OC不支持多继承(Swift也不支持多继承),但可以通过实现多协议来间接实现多继承。比如:在A类中定义协议A,B类中定义协议B;然后在C类中实现协议A、协议B。


strong,weak,retain,assign,copy,nonatomic,atomic的作用与区别

  • 作用

strong:强引用,引用计数增加1,当retainCount=0时,该对象才会被销毁
weak:弱引用,不改变引用计数, 指针指向的地址一旦被释放,这些指针都将被赋值为ni(这样的好处能有效的防止野指针)
retain:对参数进行release旧值再retain新值,引用计数增加1(对其他NSObject和其子类)
copy:建立一个引用计数为1的对象,然后释放旧对象(NSString)
assign:简单赋值,不改变引用计数,主要用于非对象类型
readwrite:属性可读可写
readonly:属性是只读
nonatomic:禁止多线程,变量保护,提高性能
atomic:提供多线程安全,避免变量的读写不同步的问题

  • 区别

1.copy与retain的区别

copy其实是建立了一个相同的对象,而retain不是;
copy是内容拷贝,retain是指针拷贝;
copy是内容的拷贝 ,对于像NSString,的确是这样,如果拷贝的是NSArray这时只是copy了指向array中相对应元素的指针.这便是所谓的"浅复制"。

2.weak和strong的区别

weak和strong不同的是: 当一个对象不再有strong类型的指针指向它的时候,它会被释放,即使还有weak型指针指向它。一旦最后一个strong型指针离去 ,这个对象将被释放,所有剩余的weak型指针都将被清除。

3.copy与strong区别

当源字符串是NSString时,由于字符串是不可变的,所以,不管是strong还是copy属性的对象,都是指向源对象,copy操作只是做了次浅拷贝。当源字符串是NSMutableString时,strong属性只是增加了源字符串的引用计数,而copy属性则是对源字符串做了次深拷贝,产生一个新的对象,且copy属性对象指向这个新的对象。另外需要注意的是,这个copy属性对象的类型始终是NSString,而不是NSMutableString,因此其是不可变的。

Objective-C中类别(Category)与扩展(Extension)的区别

Extension能为某个类附加额外的属性,成员变量,方法声明
Category只能扩充方法,不能扩展属性和成员变量


iOS本地存储数据有哪些方式

  • NSUserDefaults
  • plist存储
  • 归档(NSKeyedArchiver/NSKeyedUnarchiver,实现NSCoding协议)
  • SQLite
  • CoreData

Swfit类和结构体的区别

相同点:

  • 可以定义属性用于储值
  • 可以定义方法实现功能
  • 可以定义构造器(构造函数)用于生成初始化值
  • 可以通过扩展增加默认实现的功能
  • 可以继承协议以提供某种标准功能

不同点:

  • 类有析构函数,结构体没有析构函数
  • 类的对象是引用类型,而结构体是值类型(Copy)
  • 类可以继承某个类,结构体不可以
  • 类中引用计数允许对一个类的多次引用, 结构体不使用引用计数

用Swfit实现一个泛类型函数用于交换2个变量的值

func valueSwitch<Base>(a: inout Base, b: inout Base) {
    
    let t = a
    a = b
    b = t
}

用Swfit定义一个block

let myBlock: (() -> Void) = {
  // do something
}

__block和__weak的区别

__weak: 使用block时为了避免循环引用,用来弱引用某个对象
__block: 可以改变变量的作用域,使得变量的作用域扩展到block内部,在block内部就可以对外部的变量进行操作了


block是什么

Block对象是一个C级别的语法和运行机制, 与标准的C函数类似,但它包含了与堆、栈内存绑定的变量。因此,Block对象包含着一组状态数据, 这些数据在程序执行时用于对行为产生影响。


实现冒泡排序(从大到小排序)

  • C++
int nums[] = {8, 5, 1, 6, 9, 100, 2, 88, 77};
int len = sizeof(nums) / sizeof(nums[0]);
for (int index = 0; index<len; index++) {

    int current = nums[index];
    for (int i = index+1; i<len; i++) {

        int next = nums[i];
        if (current < next) {

            nums[index] = next;
            nums[i] = current;
            current = next;
        }
    }
}
  • Swift
var nums: [Int] = [8, 5, 1, 6, 9, 100, 2, 88, 77]
let len = nums.count

for index in 0..<len
{
    var current = nums[index]
    for i in index+1..<len {
        
        let next = nums[i]
        if current < next {
            
            nums[index] = next
            nums[i] = current
            current = next
        }
    }
}
  • Objective-C
NSMutableArray<NSNumber *> *nums = [NSMutableArray arrayWithArray:@[@8, @5, @1, @6, @9, @100, @2, @88, @77]];
NSInteger len = nums.count;
    
for (int index = 0; index<len; index++) {
    
    NSNumber *current = nums[index];
    for (int i = index+1; i<len; i++) {
        
        NSNumber *next = nums[i];
        if (current.intValue < next.intValue) {
            
            nums[index] = next;
            nums[i] = current;
            current = next;
        }
    }
}

还不快抢沙发

添加新评论