ios知识点_ios uikit知识点 - CSDN
  • 以下都是自己在iOS开发的过程中遇到的问题,自己总结出来的小知识点。 1.UITableViewCell的contenView的superView在iOS8以上是UITableViewCell,在iOS8以下则不是。所以访问contentView的superView的时候需要注意...

    以下都是自己在iOS开发的过程中遇到的问题,自己总结出来的小知识点。


    1.UITableViewCell的contenView的superView在iOS8以上是UITableViewCell,在iOS8以下则不是。所以访问contentView的superView的时候需要注意区分iOS系统版本。


    2.程序中一个UITabBarController控制了四个UINavigationController,在某个页面上点击按钮要让tabbar选中某一个baritem,需要先设置tabbar的selectedIndex再将当前的nav popToRootViewController。顺序相反会导致tabbar隐藏。


    3.在某个界面上有个UITableView,需要在滑动的时候改变另外一个UIView,所以在代码中添加了如下代理方法:

    -(void)scrollViewDidScroll:(UIScrollView *)scrollView

    实现了需求。但是如果当UITableView正在滑动的时候pop了当前视图,程序会奔溃。

    原因:想已经释放的对象发送了消息。。。。

    就是在返回之后,当前视图对象释放了,但是又调用了委托方法。

    应该在dealloc方法中将UITableView的委托设置为nil,避免UITableView正在滑动的时候返回会奔溃。

    -(void)dealloc

    {

        tableView.delegate = nil;

        tableView.dataSource = nil;

    //这里要注意,如果项目使用了arc的话,不要调用super dealloc方法。

    }


    4.十进制数转十六进制数的命令:

    echo 'obase=16;1104129754’|bc   其中1104129754是十进制数

    这个在分享功能中添加URL Scheme的时候有需要。


    5.UITableViewCell自动布局,在cell的最下面自己用UILbel加一个横线。那么添加约束的时候,这个UILabel的宽度要添加跟self的宽度一样。因为如果cell设置了cell.accessoryType =UITableViewCellAccessoryDisclosureIndicator;并且添加UILabel的宽度跟self.contentView的宽度一样的话,就会出现这个UILabel的右边缘到cell上面的向右的箭头的位置就结束了。这说明contentView的右边界是到向右箭头的左边为止的。

    像这样的:



    6.在自动布局的UITableViewCell向下复现的时候(即UITableView向下滚动的时候,已经出现过的Cell重现出现。)带有一跳一跳的效果。检查发现是因为设置tableView.estimatedRowHeight = 40;导致的。因为我这里有两个高度不同的cell,一个高度是10(作为分隔线的),一个高度根据自适应计算的。如果设置了预估高度的话,高度本来应该为10的cell出现的时候会先设置为40然后又变为10,这就导致了跳动效果。

    因此,当UITableView有多个不同高度的UITableViewCell的时候,不应该设置UITableView的estimatedRowHeight属性来只设置一个预估高度值。否则会导致UITableView向下滑动,UITableViewCell复现的时候有一跳一跳的效果。

    不过可以通过委托方法- (CGFloat)tableView:(UITableView *)tableView estimatedHeightForRowAtIndexPath:(NSIndexPath *)indexPath来设置不同样式的UITableViewCell的预估高度。


    7.自动布局中多行UILabel,需要设置其preferredMaxLayoutWidth属性才能正常显示多行内容。


    8.可以使用NSArray或者NSSet的

    - (void)makeObjectsPerformSelector:(SEL)aSelector;  

    - (void)makeObjectsPerformSelector:(SEL)aSelector withObject:(id)argument;  

    这两个相当于for循环的方法来依次让NSArray或者NSSet中的对象执行aSelector方法。


    9.给手势添加新的目标对象和方法:- (void)addTarget:(id)target action:(SEL)action

    在当前视图上面添加了一个自定义的菜单视图menu,menu上面添加了一个单击手势UITapGestureRecognizer(不是menu的公开属性)来展开和收起菜单,具体事件是写在menu里面的.

    现在当前视图上面有文本框,需求是在不改动menu的前提下,点击菜单的时候,释放键盘。

    想过给menu新添加一个UITapGestureRecognizer手势来解决这个问题,但是想想它既然已经有了一个单击手势,何必再新添加一个呢。经查阅文档,发现有这么个解决方案:

    NSArray *gestureArray = menu.gestureRecognizers;

        for(UIGestureRecognizer *gesturein gestureArray)

        {

            if([gestureisKindOfClass:[UITapGestureRecognizerclass]])

            {

                UITapGestureRecognizer *tapGesture = (UITapGestureRecognizer *)gesture;

                [tapGestureaddTarget:selfaction:@selector(menuTaped)];

                break;

            }

        }

    在当前视图中获取自己创建的这个menu上面添加的UITapGestureRecognizer手势,然后给它新增一个目标对象和方法,在方法中处理释放键盘的逻辑。


    10.

    设置导航的返回按钮标题  需求:只显示返回图标,不显示返回标题

    //!!!!:设置导航返回按钮文字的正确姿势!

        UIBarButtonItem *item = [[UIBarButtonItemalloc] initWithTitle:@""style:UIBarButtonItemStylePlaintarget:nilaction:nil];

        self.navigationItem.backBarButtonItem = item;


    网上有人说可以用下面的方法,但是实践发现下面这种方法在VC的标题很短的时候不会出现问题,一旦标题变长,这种方法会导致标题向右偏移,看起来很不好。。。。

    [[UIBarButtonItemappearance] setBackButtonTitlePositionAdjustment:UIOffsetMake(0, -60)

                                                                forBarMetrics:UIBarMetricsDefault];


    偏移效果图如下:



    11.页面下拉刷新箭头有时候在加载过程中push进新页面然后再返回的时候收不起来的问题

    self.edgesForExtendedLayout=UIRectEdgeNone;是因为self.view addSubView:添加的第一个子视图不是UIScrollView的子类,而没有设置这个导致的。

    12.在当前页面A中有一个UITextField文本框,并添加了UITextFieldTextDidChangeNotification通告,当文本框内容改变时进行动态搜索匹配,在收到通知之后改变这个文本框的内容。但是,如果现在push进来了一个新的带有文本框的页面B,当在B中文本框内输入内容之后再返回A,这时候发现A中的文本框的内容是B中文本框所填的内容。

    这是因为通告的问题,在监听通告的地方没有判断是不是当前A中的文本框触发的,只需在方法中判断一下即可。

    13.tabaritem的标题默认跟它对应的nav的rootviewcontroller的self.title是一样的。如果要tbbaritem的文字跟self.title不一样,可以在rootviewcontroller中设置self.naigationItem.title@“标题”;而不是直接用self.title属性。

    14.当给一个UIImageView添加固定宽高约束,但是当加载的网络图片比约束的宽高大很多的时候,显示的图片内容会撑出UIImageView的边界,设置了UIImageView的contentMode也不行。这个给UIImage添加一个Resize分类,并添加一个新的方法:

    -(instancetype)resizeToSize:(CGSize)size

    {

    UIGraphicsBeginImageContext(size);

    [selfdrawInRect:CGRectMake(0,0, size.width, size.height)];

    UIImage*newImage =UIGraphicsGetImageFromCurrentImageContext();

    UIGraphicsEndImageContext();

    returnnewImage;

    }

    对UIImageView的imag进行重绘就不会出现这种情况了。

    15.在自定义单元格上面,左边一个UILabel右边对齐显示标题,右边一个UITextField供用户输入内容。给UILabel添加了固定宽度约束,给UITextFiled添加了边距约束。但是当UITextField中输入内容很长的时候会发现UITextFiled会把左边的UILabel挤压到左边(比如UILabel显示的文字是@“姓名”给它加的宽度约束是100,但是实际显示的时候会显示到单元格的最左边,只有刚刚好能显示全@“姓名”的宽度,剩下的单元格宽度都给了UItextFiled。)这种效果不是我们想要的效果,我们想要UILabel固定宽度。只需要将右边的UITextField也添加一个宽度约束即可,添加lessThanOrEqualTo(这里加上cell的宽度减去左边UILabel的宽度以及它们之间的间隔)这样当文本框输入内容很长的时候就不会挤压左边的UILabel了。



    展开全文
  • 整理了些iOS相关的基础问题,每个问题可能会再写些扩展,需要具体了解可以看题目下方的链接 如有错漏,欢迎指出,谢谢 一.Swift 1.给一个数组,要求写一个函数,交换数组中的两个元素(swift可用元组) func swap&...

    整理了些iOS相关的基础问题,每个问题可能会再写些扩展,需要具体了解可以看题目下方的链接
    如有错漏,欢迎指出,谢谢

    一.Swift

    1.给一个数组,要求写一个函数,交换数组中的两个元素(swift可用元组)

    func swap<T>(_ arr: inout [T],  a: Int, b: Int) {
    	(arr[a], arr[b]) = (arr[b], arr[a])
    }
    

    2.Any / AnyObject

    // Any 的定义
    typealias Any = protocol<>
    
    // AnyObject 定义
    @objc protocol AnyObject {
    }
    
    
    • Any 是空协议集合的别名,用于所有类型
    • AnyObject 是一个空协议,用于所有class实例, 所有类隐式遵守的协议

    3.@objc / dynamic

    OC 对象基于运行时,方法或属性使用动态派发,在运行调用时再决定实际调用的具体实现,而Swift为了追求性能,如无特殊需要,是不会在运行时再来决定这些,即Swift类型的成员/方法在编译时已经决定。

    OC中基本所有类继承自NSObject,Swift类如要供OC调用,必须也继承自NSObject

    • @objc
    1. 根本目的:暴露接口给OC的运行时
    2. 添加@objc并不意味着这个方法或属性会采用OC的方式变成动态派发,Swift仍有可能将其优化为静态调用
    3. @objc可修改Swift接口暴露到OC后的名字
    • dynamic
      用于表明使用runtime机制,可动态调用

    加了@objc标识的方法、属性无法保证都会被运行时调用,因为Swift会做静态优化。要想完全被动态调用,必须使用dynamic修饰。使用dynamic修饰将会隐式的加上@objc标识。

    swift中的函数是静态调用,静态调用的方式会更快,但是静态调用的时候就不能从字符串查找到对于的方法地址,这样 与OC交互的时候,OC动态查找方法就会找不到,这个时候就可以通过使用 dynamic 标记来告诉编译器,这个方法要被动态调用的

    4.OC与Swift区别

    OC与Swift区别

    • 大的方面:
      Swift是静态类型语言,OC是动态类型语言。
      OC堆类型要求不严格,而Swift格外严格,是类型安全的语言,他不仅对类型是否匹配严格,也对一个类型是否有值严格
      编程思想上,用Swift编程的时候不能套用OC的编程思想,着重于函数式编程/面向协议编程

    当然,Swift和OC可以混编,从OC过渡Swift比较简单,有很多地方是相通的

    Swift优势:
    1.语法易读,文件结构更简单
    2.更安全,强类型语言(默认类型安全)
    3.代码更少,省去大量冗余代码
    4.速度更快,运算性能更高

    • 小细节的方面:
    1. Swift 类型,值类型/引用类型
    2. Swfit get/set方法,引入存储属性、计算属性
    3. 严格的初始化
    4. 面向协议编程,Swift对Protocol有更好的支持,比如继承、
    5. 泛型
    6. 抛弃了C语法的 ++ --, switch的break
    7. struct和class类型很像
    8. 重载运算法
    9. 函数式编程
    扩展:
    • 强/弱类型:指语言类型系统的类型检查严格程度,强类型偏向不容忍隐式类型转换,而弱类型不行
    • 静态/动态类型:指变量与类型的绑定方法,前者是编译器在编译阶段执行类型检查,而后者编译器在运行时执行类型检查,即声明一个变量后,不能改变它的类型的语言,是静态语言,能随时改变它类型的语言是动态语言

    5.protocol 与 extension

    protocol

    Swift 协议(protocol)详解

    Protocol小结

    定义某种约定,来表示共性,而不是类型,相比OC,不仅用做代理,也可用作对接口的抽象,代码的复用

    1. 协议内定义属性/方法/构造器

      • 定义属性 {get set} / 类型, 不能设置默认值
      1. 定义方法(参数不能有默认值,没有实现)
      2. 构造器
    2. 协议是一种类型

      • 作为函数/构造器中的参数或返回值类型
      • 作为常量/变量/属性类型
      • 作为数组/字典或其他容器中的元素类型
    3. 用于委托模式

      • 委托模式允许某个类型的部分指责转移到另一个类型的实例来实现
      • 可通过协议来实现委托模式
    4. protocol-extension

      • 对实例使用,令已有类型遵循某个协议
      • 对协议使用,可遵循其他协议
      • 提供默认实现,相当于变相设定成了optional
      • 搭配where使用,可增加限制条件,限制类型
    5. 协议的继承

      • 可继承一个或多个其他协议
      • 使用&关键字同时遵循多个协议
      • 协议通过继承AnyObject,使其被限制只适用于class类型
    6. 检查一致性

      • 使用is检查某个实例是否符合协议
      • 使用as?返回协议类型的可选值
      • 使用as!强制转换为协议类型
    7. 可选协议

      • 关键字optional
      • 协议及可选项使用@objc标记
      • 结构体/枚举不能使用,只能由继承OC类或@objc类使用
    8. 关联类型

    面向协议编程

    依赖倒置原则:
    高层模块不依赖于低层模块,二者依赖于抽象
    抽象不依赖于细节,细节依赖于抽象

    1. 面向对象编程呈现的是金字塔结构,面向协议编程提倡的是扁平化和去嵌套的代码
    2. Swift中,协议定义了方法、属性的蓝图,然后类、结构体或枚举都能够使用协议,使用继承的思想,模拟了多继承关系,不存在is-a关系
    3. 将与类型无关的共性从类型定义上移出去,用一个协议封装好,让它成为这个类型的一种修饰
    4. 如果某个类型有多个修饰,那么使用多个协议对其修饰,大大降低了代码的耦合度
    5. 依赖反转/接口分离

    extension

    Swift - 基础之extension

    1. 为class/struct/enum或protocol添加新特性(计算属性、方法、初始化方法)
      注意,添加新的,但不能覆盖已有的特性
    • 添加计算属性(),但不能添加存储属性,也不能添加属性观察者
    • 添加构造器,但需保证该类型的初始化方法结束时,每一个属性都被完全初始化了
    • 添加实例方法/类方法
    • 添加mutating方法(如果struct和enum定义的方法想改变自身或自身的属性,那么实例方法必须标记为突变的)
    • 添加附属脚本-subscripts(一种访问的对象/集合的快捷方式,如array[index])
    • 添加嵌套类型-nested types,如给结构体嵌套枚举类型
    1. 可让某个类型实现一个或多个协议

    6. struct 和 class 什么区别

    Swift 浅谈Struct与Class
    理解Swift中struct和class在不同情况下性能的差异

    深拷贝&浅拷贝本质
    1. 是否开启新的内存地址
    2. 是否影响内存地址的引用计数
    • struct是值类型,class是引用类型
    • struct不能继承,class可以继承
    • struct比class更"轻量级",前者分配在栈上,后者分配在堆上
    • struct有默认的带参数的构造函数,class无
    • struct无析构,class有
    struct作为数据模型注意事项
    1. 安全性:值类型,没有引用计数,不会导致内存泄漏
    2. 速度:以栈的形式分配(编译时分配空间),而不是堆(运行时分配),速度更快
    3. 拷贝:引用类型拷贝需注意深拷贝/浅拷贝,值类型拷贝更轻松
    4. 线程安全:值类型是自动线程安全的

    缺点:

    1. 与OC混编时,OC无法调用Swift的struct(因为OC调用的对象需继承NSObject)
    2. 不能相互继承
    如何抉择

    选择值类型:

    1. 要用==运算符来比较实例的数据时
    2. 希望实例的拷贝能保持独立的状态
    3. 数据被多个线程使用

    选择引用类型:

    1. 需要使用NSObject相关功能时,必须用引用类型class
    2. 要用==运算符比较实例身份时
    3. 希望创建一个共享、可变对象

    模型较小,无需继承、无需OC使用,建议使用Struct

    值类型与引用类型的嵌套

    Swift 中的嵌套类型

    • 值类型嵌套值类型: 内部值是外部值的一部分,拷贝外部值到新的空间,也会拷贝内部值
    • 值类型嵌套引用类型:外部值被拷贝到新的内存区域,但内部的引用类型只被拷贝了内部值的引用
    • 引用类型嵌套引用类型:复制时只是拷贝了引用,新/原变量都指向同一个实例
    • 引用类型嵌套值类型:与引用类型嵌套引用类型一样

    7. copy on write 写时复制

    Swift Copy-On-Write 写时复制
    只有当这个值需要改变时才进行复制行为

    在结构体内部存储了一个指向实际数据的引用reference,在不进行修改操作的普通传递过程中,都是将内部的reference的引用计数+1,在进行修改时,对内部的reference做一次copy操作,再在这个复制出来的数据进行真正的修改,防止和之前的reference产生意外的数据共享

    值类型嵌套引用类型的写时复制
    1. 私有化,让外部无法对引用类型进行修改
    2. 另提供一个接口控制这个引用类型写入操作(使用isKnownUniquelyReferenced检查类的实例是不是唯一的引用,来决定setter时是否需要复制)

    8.在一个app中间有一个button,在你手触摸屏幕点击后,到这个button收到点击事件,中间发生了什么

    1.Runloop
    2.事件传递与响应

    响应链大概有以下几个步骤:

    设备将touch到的UITouch和UIEvent对象打包, 放到当前活动的Application的事件队列中

    单例的UIApplication会从事件队列中取出触摸事件并传递给单例UIWindow

    UIWindow使用hitTest:withEvent:方法查找touch操作的所在的视图view

    RunLoop这边我大概讲一下:

    主线程的RunLoop被唤醒

    通知Observer,处理Timer和Source 0

    Springboard接受touch event之后转给App进程

    RunLoop处理Source 1,Source1 就会触发回调,并调用_UIApplicationHandleEventQueue() 进行应用内部的分发。

    RunLoop处理完毕进入睡眠,此前会释放旧的autorelease pool并新建一个autorelease pool

    9.闭包/逃逸闭包/自动闭包

    非逃逸/逃逸闭包
    闭包捕获语义第一弹:一网打尽!

    闭包

    1. 闭包会自动捕获外部变量的引用
    2. 可在闭包内修改变量的值(声明为var)
    3. 可通过捕获列表来获取变量中的内容,存储到本地常量中
    4. 默认闭包行为更像是在OC中使用__block

    逃逸闭包/非逃逸闭包/自动闭包

    • 逃逸闭包:

      一个接受闭包为参数的函数,逃逸闭包可能会在函数返回之后才被调用,即闭包逃离了函数的作用域(例如:网络请求在请求结束后才调用闭包,并不一定是在函数作用域内执行)

    • 非逃逸闭包:

      一个接受闭包为参数的函数,闭包在这个函数结束前内被调用

    闭包会强引用它捕获的所有对象,比如在闭包中访问了当前控制器的属性、函数,这样闭包会持有当前对象,容易导致循环引用

    非逃逸闭包不会产生循环引用,它会在函数作用域内释放,编译器可保证在函数结束时闭包会释放它捕获的所有对象,非逃逸闭包它的上下文的内存可保存在栈上而不是堆上


    • 自动闭包:简化参数传递,延迟执行时间

      1. 一种自动创建的闭包,包装传递给函数作为参数的表达式,不接受任何参数,被调用时,返回被包装在其中的表达式的返回值
      2. 普通表达式与@autoclosure的区别:前者传入参数时,会马上被执行,然后将执行结果作为参数传递给函数,而后者不会立马执行,而是由调用的函数内来决定它具体执行时间

    weak & unowned 处理循环引用

    弱引用:不会被ARC计算,引用计数不会增加

    • unowned

      1. 捕获的原始实例永远不会为nil,闭包可直接使用它,并直接定义为显示解包可选值,如原始实例被析构后,在闭包中使用这个捕获值将导致奔溃
      2. 闭包和捕获对象的生命周期相同,所以对象可以被访问,也意味着闭包也可以被访问[unonwed self]
    • weak

      1. 捕获的实例在使用过程中可能为nil,必须将引用声明为weak,并在使用之前验证这个引用的有效性;
      2. 闭包的生命周期和捕获对象的生命周期相互独立,当对象不能再使用时,闭包依然能够被引用

    使用unowned引用不会去验证引用对象的有效性,weak引用添加了附加层,间接得把unowned引用包裹到了一个可选容器里面,在指向的对象析构之后变成空的情况下,处理更清晰,而这附加的机制需要正确处理可选值

    弱应用的实现原理

    OC 和 Swift 的弱引用源码分析
    Swift 4 弱引用实现

    Swift4之前旧实现:

    Swift对象有两个引用计数:强引用计数和弱引用计数,当强引用计数为0而弱引用计数不为0时,对象会销毁,但内存不会被立即释放,内存中会保留弱引用指向的僵尸对象,在加载弱引用时,运行时会对引用对象进行检查,如果是僵尸对象,则会弱引用计数进行递减操作,一旦弱引用计数为0,对象内存将会被释放。

    问题:

    如果对象的弱引用数一直不为零,那么对象占用的剩余内存就不会完全释放。这些死而不僵的对象还占用很多空间的话,累积起来也是对内存造成浪费

    Swift4后:

    SideTable机制:与OC不同的是,系统不再把它作为全局对象使用

    1. 针对有需要的对象创建,为目标对象分配一块新的内存来保存该对象额外的信息(SideTable可有可无),对象会有一个指向SideTable的指针,同时SideTable也有一个指回原对象的指针
    2. 为了不额外多占用内存,只有创建弱引用时,会把对象的引用计数放到新创建的SideTable,在把空出来的空间存放SideTable的地址,而runtime会通过一个标志位来区分对象是否有SideTable
    3. 弱引用从指向对象本身改为指向SideTable

    10.map、flatMap/compactMap、filter、reduce

    1. map/flatMap都可以用在Optional和SequenceTypes上
    2. compactMap是Swift4加入的新特性,其实是把之前的flatMap改了个名字
    • map: 每个元素根据闭包中的方法进行转换,然后按转换后的元素输出

    • Optional map/flatMap

      • map是闭包内return为非可选项,但最终返回值为可选项
      • flatMap是闭包内return为可选项,最终返回值也为可选项
    • Sequence.map/flatMap/compactMap

      • flatMap数组降纬度
      • compactMap过滤nil+可选解包
    • filter: 过滤,筛选出满足闭包条件的元素

    • reduce: 组合计算

    11.try? 和 try!是什么意思

    try 出现异常处理异常
    try? 不处理异常,返回一个可选值类型,出现异常返回nil
    try! 不让异常继续传播,一旦出现异常程序停止,类似NSAssert()

    12.associatedtype 的作用

    • 在协议定义里声明关联类型,相当于给需要用到的类型一个占位符名称,直到采纳协议时,再指定用于该关联类型的实际类型
    • 可以给关联类型添加约束

    13.什么时候使用 final/ class与static区别

    类不想被继承,函数、属性不想被重写(只能修饰类)

    class 和 static 都可表示类方法,前者子类可重写,后者不能重写,static自带final class性质

    14.Optional(可选型) 是用什么实现的

    public enum Optional<Wrapped> {
        case none
        case some(Wrapped)
    }
    

    泛型枚举

    15. ?/ !/ ?? 的作用

    1.声明时添加?,告诉编译器是可选值(表示一个变量可能有值,也可能没有值为nil),自动初始化为nil

    2.对变量操作前加?,判断如果变量为nil,则不响应后面的方法

    1.声明时添加!,告诉编译器是可选值,并且之后对变量操作时,都隐式在操作前添加!
    2.对变量操作前加!,表示默认为非nil,直接解包处理

    ??

    设置默认值,判断变量是否为nil,如果不为nil,则对该变量解包,否则用??后面的默认值

    16.lazy / inout 的作用

    • lazy: 延迟初始化,当变量在用到的时候才加载(全局变量不用lazy也是懒加载)

    • inout:

      1. 方法的参数默认不可改变类型,方法内部改变参数值会导致编译错误,需要改变参数值时,需要使用inout关键字(传递的参数不能为let,不能有默认值)
      2. 传递过程:方法调用->参数值被拷贝->方法体内部,被改变的是拷贝的值->方法结束,拷贝的值重新分配给原来的参数

    17.什么是高阶函数

    其参数或者返回值是闭包的函数,如sort、map、filter

    18.mutating 的作用是什么

    对结构体、枚举,mutating用于表示某个实例方法可以改变自身实例或者实例中的属性的函数
    对协议,用于那些会改变遵循该协议的类型的实例的函数

    19.一个 Sequence 的索引是不是一定从 0 开始?

    不是
    ArraySlice是Sequence的子类,ArraySlice就不是

    20.defer使用场景

    作用:提供一种延时调用的方式,defer内的代码块会在当前作用域结束之后执行,代码块会被压入栈中,待函数结束时弹出栈运行。
    其目的就是进行资源清理和避免重复的返回前需要执行的代码

    注意:前提是必须执行到defer才会触发,多个defer,按栈的后进先出顺序执行

    1. try catch结构:相当于finally

    2. 清理、回收资源,例如:加解锁

      lock.lock()
      defer {
      	lock.unlock()
      }
      
      
    3. 调super方法:override一些方法时,需要在super方法前写,比如autolayout的约束写动画,重写updateContaints方法,可以用defer将super方法调用写在前面

    4. completion闭包调用:有些函数分支较多,遗漏调用completion

    21.Self / self

    • self: 在实例方法中表示当前实例,在类方法中表示当前类
    • Self: 可用于协议中限制相关的类型,类中来充当方法的返回值类型

    例如:

    protocol Copyable {
    	func copy() -> Self
    }
    
    class A: Copyable {
    
    	var num = 1
    	required init() { } // 保证当前类和其子类都能响应这个init方法
    	
    	func copy() -> Self {
    	   // type(of: self)获取当前对象的类型
    		let copy = type(of: self).init()
    		copy.num = num
    		return copy
    	}
    }
    
    

    22. .Type / .self

    理解 Swift 中的元类型:.Type 与 .self

    • 元类型:类型的类型
    • let intMetatype: Int.Type = Int.self .Type是类型,.self是元类型的值
    • AnyClass: typealias AnyClass = AnyObject.Type 任意类型的元类型的别名

    获得元类型后可以访问静态变量和静态方法,例子:

    func register(AnyClass?, forCellReuseIdentifier: String)
    
    tableView.register(UITableViewCell.self, forCellReuseIdentifier: "cell")
    
    • type(of:) : type(of:)获取的是运行时的元类型,也就是这个实例的类型,而.self获取的是静态的元类型,声明时是什么类型就是什么类型

    23.一个类型表示选项,可以同时表示有几个选项选中(类似 UIViewAnimationOptions ),用什么类型表示

    Swift选项集合(OptionSet)

    OptionSet 选项集合

    Swift使用struct来遵循OptionSet协议,引入选项集合

    创建与使用

    一个类型为整型的原始值(rawValue)+ 一个初始化构造器(struct有默认的,不需要写)

    struct Sports: OptionSet {
    	let rawValue: Int
    	
    	static let running = Sports(rawValue: 1 << 0)
    	static let cycling = Sports(rawValue: 1 << 1)
       static let swimming = Sports(rawValue: 1 << 2)
    }
    
    let sports = [.running, .swimming]
    
    

    24.给集合中元素是字符串的类型增加一个扩展方法,应该怎么声明

    Swift 中的 Sequence(一)

    extension Set where Iterator.Element == String {
    	func test() { }
    }
    
    ["", ""].test()
    

    25.不通过继承,代码复用(共享)的方式有哪些

    1. extension
    2. protocol

    使用继承可能的问题:

    1. 代码在多个子类中重复
    2. 很难知道所有子类的全部行为
    3. 子类很多时候会有不同的行为
    4. 改变父类牵一发而动全身
      (又要扯到依赖倒置原则了)

    26.如何让自定义对象支持字面量初始化

    swift定义了一些协议,可通过使用赋值运算符,来用文字值初始化类型,采用相应的协议并提供公共初始化允许特定类型的文本初始化

    • ExpressibleByArrayLiteral 数组形式初始化
    • ExpressibleByDictionaryLiteral 字典形式初始化
    • ExpressibleByNilLiteral 由nil值初始化
    • ExpressibleByIntegerLiteral 整数值初始化
    • ExpressibleByFloatLiteral 浮点数初始化
    • ExpressibleByBooleanLiteral 布尔值初始化
    • ExpressibleByUnicodeScalarLiteral ExpressibleByExtendedGraphemeClusterLiteral ExpressibleByStringLiteral 以上三种由字符串初始化,上面两种包含Unicode自负和特殊字符

    例子:

    struct TestFloat {
        var value: Float
    }
    
    //一般情况下,初始化
    var test = TestFloat(value: 4.5)
    
    // 遵循ExpressibleByFloatLiteral协议
    extension TestFloat: ExpressibleByFloatLiteral {
        typealias FloatLiteralType = Float
        
        init(floatLiteral value: TestFloat.FloatLiteralType) {
            self.init(value: value)
        }
    }
    
    // 
    var testt: TestFloat = 4.5
    
    

    27.访问修饰符

    • private: 只能在当前类访问
    • fileprivate: 在当前Swift文件可访问
    • internal(默认):在源代码所在的整个模块可访问(在app内,整个app都可以访问,在框架/库中,则整个框架内部访问,框架外代码不能访问)
    • public:被任何人访问,其他模块中不可被重写和继承
    • open:被任何人访问,包括重写和继承

    28.多线程

    iOS多线程全套
    iOS 多线程:『pthread、NSThread』详尽总结
    iOS GCD
    iOS Swift GCD 开发教程
    iOS 多线程:『NSOperation、NSOperationQueue』详尽总结

    概念

    • 并发&并行:
      前者指多个任务交替占用CPU,后者指多个CPU同时执行多个任务

    • 同步&异步:

      • 同步:同步添加任务到指定队列,必须执行完队列中的任务才能继续执行,只能在当前线程执行任务,不具备开启新线程的能力
      • 异步:异步添加任务到指定队列,无需等待,可继续执行,可在新的线程中执行任务,具备开启新线程的能力(但不一定开启新线程)

    pthread

    跨平台、C语言编写,需要自己管理线程的生命周期,难度大

    NSThread(swift为 Thread)

    比pthread简单,可直接操作线程对象,但也需要自己管理线程的生命周期

    performSelector

    GCD

    • 优点:
    1. 可用于多核的并行运算
    2. 自动利用更多的CPU内核
    3. 自动管理线程的生命周期(创建、调度任务、销毁线程)
    • 任务: 执行操作,即线程中执行的那段代码
    队列(Dispatch Queue)

    指执行任务的等待队列,即用来存放任务的队列(FIFO)

    • 串行队列:每次只有一个任务执行,一个任务执行完毕后再执行下一个任务
    • 并发队列:让多个任务同时执行(并发队列的并发只有在异步有效)
    • 主队列(串行):所有放在主队列的任务都会在主线程中执行
    • 全局队列:并发队列
    区别 串行队列 并发队列 主队列
    同步 当前线程执行,不开启新线程,串行执行任务 当前线程执行,不开启新线程,串行执行任务 主线程调用:死锁卡住不执行;其他线程调用:不开启新线程,串行执行任务
    异步 开启新线程,串行执行任务 可开启多个线程,并发执行任务(无序执行,多条线程) 不开启新线程,串行执行任务(任务在同一线程)
    GCD 栅栏

    异步执行一组任务 -> barrier任务 -> 异步执行另一组任务

    // OC
    dispatch_barrier_async
    
    // Swift
    let item = DispatchWorkItem(qos: .default, flags: .barrier) {
        
    }
    
    队列组 group
    1. 多个任务并发无序执行
    2. 完成上述任务后在回到主线程执行任务
    // OC
    dispatch_group_t group =  dispatch_group_create();
        
        dispatch_group_async(group, dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
        });
        
         dispatch_group_notify(group, dispatch_get_main_queue(), ^{
        });
        
        dispatch_group_enter、dispatch_group_leave
    
    // Swift
    DispatchQueue.global().async(group: group, execute: <#T##DispatchWorkItem#>)
    
    group.enter()
    group.leave()
    
    group.notify(queue: queue) {
    }
    

    group enter/leave

    延迟执行

    并不是在指定时间之后才开始执行处理,而是在指定时间之后才将任务添加到队列中

    // OC
    dispatch_after
    
    // Swift
    asyncAfter
    
    GCD 快速迭代方法 apply

    按照指定的次数将指定任务追加到指定队列中, 添加的任务并发异步执行,这些任务全部执行完毕后再继续往下执行

    // OC
    dispatch_apply
    
    // Swift
    DispatchQueue.concurrentPerform(iterations: 100) { (index) in
    }
    
    信号量 semaphore

    持有计数的信号,计数为0时等待,不可通过,计数>=1时,计数减1且不等待,可通过

    OC:

    1. dispatch_semaphore_create: 创建一个Semaphore并初始化信号的总量
    2. dispatch_semaphore_signal: 发送一个信号,让信号总量+1
    3. dispatch_semaphore_wait: 当信号总量为0时,就会一直等待(阻塞所在线程),否则就可以正常执行, 并使总信号量-1

    Swift:

    1. DispatchSemaphore(value: 1) 初始化信号量的总量
    2. wait()使信号量减1,如果信号量大于0则返回.success,否则返回timeout
    3. signal()使信号量+1,返回当前信号量

    应用:

    1.保持线程同步,将异步执行任务转换成同步执行任务

    func semaphoreSync() {
        print("current thread: \(Thread.current)")
        
        print("semaphore begin")
        
        let se = DispatchSemaphore(value: 0)
        
        var num = 0
        
        DispatchQueue.global().async {
            Thread.sleep(forTimeInterval: 2)
            print("----> \(Thread.current)")
            num = 100
            se.signal()
        }
        
        se.wait()
        
        print("---> num: \(num)")
    }
    
    

    2.保证线程安全,为线程加锁

    例子:多个窗口卖票,票源总数固定,
    wait相当于加锁,signal相当于解锁

    NSOperation/NSOperationQueue(swift没有前缀NS)

    优点:

    1. 基于GCD的更高一层封装,易用,代码可读性高
    2. 可添加完成的代码块,在操作完成后执行
    3. 添加操作之前的依赖关系,方便控制执行顺序
    4. 设定操作执行的优先级
    5. 很方便地取消一个操作的执行
    6. 使用KVO观察
    • Operation(操作):

      • 执行操作,即在线程中执行的那段代码
      • GCD放在block中,在Operation中我们使用其子类NSInvocationOperation,NSBlockOperation或自定义子类来封装
    • OperationQueue(操作队列):

      • 存放操作的队列,不同于GCD的调度队列是FIFO,操作队列对于添加到队列的操作,首先进入准备就绪的状态(取决于操作之间的依赖关系),然后进入就绪状态的操作的开始执行顺序(非结束执行顺序)由操作之间相对的优先级决定
      • 通过设置maxConcurrentOperationCount最大并发操作数控制并发、串行
      • 有两种队列:主队列和自定义队列,主队列运行在主线程上,自定义队列在后台执行

    步骤:

    1. 创建操作
    2. 创建队列
    3. 将操作加入到队列中

    之后,系统自动将操作队列中的操作取出,在新线程中执行操作

    操作 Operation
    1. 使用子类 NSInvocationOperation(只有OC有,swift没有)
    2. 使用子类 NSBlockOperation
    3. 自定义继承 NSOperation子类,通过实现内部相应方法来封装操作
    不使用OperationQueue的情况
    1. 在没使用OperationQueue,在主线程中单独使用子类NSInvocationOperation、NSBlockOperation或自定义继承Operation子类执行一个操作的情况下,操作是在当前线程执行,没有开启新线程

    2. BlockOperation与InvocationOperation类似,但还多提供了一个方法addExecutionBlock来添加额外的操作,额外操作是在不同线程中异步执行的

    3. 一般情况下,如果BlockOperation封装多个操作,是否开启新线程,取决于操作个数,开启的线程数由系统决定

    使用OperationQueue的情况

    OperationQueue 有两种队列:主队列、自定义队列(包含串行、并发功能)

    • 主队列:凡是添加到主队列的操作,都会放到主线程中执行(不包括addExecutionBlock添加的额外操作,可能在其他线程执行)

    • 自定义队列:在自定队列中会自动放到子线程中执行,包含串行、并发功能

    • maxConcurrentOperationCount:控制的不是并发线程的数量,是一个队列中同时能并发执行的最大操作数,开启线程数量由系统决定

      • 默认为-1,表示不限制,可进行并发执行
      • =1,队列为串行队列,只能串行执行
      • 1, 队列为并发队列,操作并发执行,其值为min(自己设定的值,系统设定默认最大值)

    依赖/优先级

    29.线程安全

    【Swift】iOS 线程锁
    iOS-线程安全

    • 线程安全:当一段代码被多个线程执行,执行后的结果和多个线程依次执行后的结果一致,那么这段代码就是线程安全

    • 互斥锁: 当新线程访问,发现有线程正在执行锁定代码,新线程进入休眠,避免占用CPU资源,锁的持有者的任务完成,会检测是否存在等待执行的线程,如有,唤醒执行任务

    • 自旋锁:新thread会用死循环的方式一直等待锁定的代码执行完成,消耗性能

    NSLocking 协议

    public protocol NSLocking {
    	public func lock()
    	public func unlock()
    }
    

    遵循NSLocking协议,包括NSLock, NSCondition, NSConditionLock, NSRecursiveLock

    NSLock

    最常用的锁,lock & unlock, 注意需要在同一线程上调用

    NSConditionLock 条件锁

    确保线程仅在condition符合情况时上锁,并执行相应代码,然后分配新的状态

    NSCondition 基本的条件锁

    手动控制线程wait和signal

    NSRecurisiveLock 递归锁

    可以多次给相同线程上锁并不会造成死锁

    GCD信号量(DispatchSemaphore)

    持有计数的信号,计数为0时等待,不可通过,计数>=1时,计数减1且不等待,可通过

    OC:

    1. dispatch_semaphore_create: 创建一个Semaphore并初始化信号的总量
    2. dispatch_semaphore_signal: 发送一个信号,让信号总量+1
    3. dispatch_semaphore_wait: 当信号总量为0时,就会一直等待(阻塞所在线程),否则就可以正常执行, 并使总信号量-1

    Swift:

    1. DispatchSemaphore(value: 1) 初始化信号量的总量
    2. wait()使信号量减1,如果信号量大于0则返回.success,否则返回timeout
    3. signal()使信号量+1,返回当前信号量

    @synchronized (OC的方法)

    会对访问的变量加互斥锁

    objc_sync_enter/objc_sysn_exit(Swift方法)

    与OC的synchroned关键字类似,对某一个对象加互斥锁

    自旋锁

    • OSSpinLock: iOS10后废弃
    • os_unfair_lock: iOS10新方法

    二.OC

    1.KVO的实现原理

    KVC: Key-Value-Coding 给某个对象属性赋值/取值

    方法:

    1. 点语法
    2. 私有属性:setValue:forKey / setValue:forKeyPath
    3. 字典转模型:setValueForKeysWithDictionary
    KVO: 利用一个Key找到其属性并监听(观察者模式)
    • 使用步骤:

      1. 添加观察者 addObserver:forKeypath:options:context:
      2. 观察者实现监听方法
      3. 移除监听者
    • 底层实现:
      runtime机制动态创建被监听类的派生类,重写setter方法,在调用原setter方法之前和之后通知观察者值的改变,并将原被监听类的isa指针指向这个派生类

    2.消息调用与转发的过程

    详细:iOS 消息发送与转发详解

    objc_msgSend(id self, SEL cmd, …)

    首先要区分方法和消息的概念:

    1. 方法:一段实际代码 + 特定名字 + 方法类型
    • Method = SEL + IMP + method_types
    • SEL: 选择器,相当于char*, 看作方法的名字,所有类,方法名相同,产生的selector相同
    • IMP: 函数指针,指向方法实现的首地址
    1. 消息:发送给对象的名称和一组参数

    消息发送的过程:

    1. 检查selector是否忽略,比如Mac OS X 开发有了垃圾回收旧就不会理会retain/release函数
    2. 检查selector的target是否为nil,OC允许对一个nil对象执行任何方法不会crash
    3. 查找这个类的实现IMP,先从cache查找,如有运行
    4. cache没有就找该类的方法列表是否有对应方法
    5. 类的方法列表没有就找其父类的方法列表中查找,一直找到NSObject为止
    6. 还没有就进入动态方法解析和消息转发

    动态方法解析和消息转发:

    当上述没有找到方法实现,程序在异常抛出前,runtime会有3次拯救的机会

    • Method resolution
    • Fast forwarding
    • Normal forwarding
    1. 动态方法解析:resolveInstanceMethod (实例方法) / resolveClassMethod(类方法), 在该方法内利用class_addMethod绑定,返回YES
    2. Fast forwarding: forwardingTargetForSelector 替换消息的接受者为其他对象,即将A类的某个方法,转发到B类的实现中去,如果return nil/self则进入第三完整转发,
    3. 完整转发: forwardInvocation / methodSignatureForSelector
      第一个方法转发具体实现,第二个方法返回一个方法签名,二者互相依赖,只有返回了正确的方法签名,才会执行另一个方法,与上者类似,都是将A类的某个方法转发B类的实现中,不同的是,它更灵活,前者只能固定转发到一个对象,后者能转发多个对象中去

    3.RunLoop

    iOS 多线程:『RunLoop』详尽总结
    深入理解RunLoop
    解密-神秘的 RunLoop
    我认为的 Runloop 最佳实践

    RunLoop在循环中用来处理程序运行过程中出现的各种事件,从而保持程序的持续运行

    在没有事件处理时,会使线程进入睡眠模式,从而节省CPU资源,提高性能

    RunLoop 和 线程

    1. 一条线程对应一个RunLoop对象
    2. RunLoop不保证线程安全,我们只能在当前线程内部操作当前线程的RunLoop对象
    3. RunLoop对象在第一次获取时创建,销毁则是线程结束的时候
    4. 主线程的RunLoop对象系统自动创建好了,而子线程的RunLoop则需要自行创建和维护
    • RunLoop 与 主线程
      UIApplicationMain自动开启了主线程的RunLoop,内部无限循环

    RunLoop是线程中的一个循环,RunLoop会在循环中不断检测,通过Input sources(输入源)和Timer sources(定时源)两种来源等待接收事件,然后对接收到的事件通知的线程进行处理,并在没有事件的时候让线程休息

    RunLoop 相关类

    Core Foundation框架(括号为Swift写法):

    1. CFRunLoopRef(CFRunLoop): RunLoop对象
    2. CFRunLoopModeRef(CFRunLoopMode): RunLoop的运行模式
    3. CFRunLoopSourceRef(CFRunLoopSource): 输入源/事件源
    4. CFRunLoopTimerRef(CFRunLoopTimer): 定时源,基于时间的触发器
    5. CFRunLoopObserverRef(CFRunLoopObserver): 观察者,能监听RunLoop的状态变化
    • 相互关系:
      • 一个RunLoop对象包含若干个运行模式(RunLoopMode)
      • 一个运行模式包含若干输入源、定时源、观察者
      • 每次runloop启动只能指定其中一个模式,这个模式被称作当前运行模式
      • 切换运行模式,只能退出当前loop,再重新指定一个运行模式进入,保证不同组的输入源,定时源,观察者互不影响
    CFRunLoopModeRef(CFRunLoopMode)
    • kCFRunLoopDefaultMode: 默认运行模式,通常主线程是在这个模式下运行
    • UITrackingRunLoopMode: 跟踪用户交互事件,用于 ScrollView 追踪触摸滑动,保证界面滑动时不受其他 Mode 影响
    • UIInitializationRunLoopMode:在刚启动App时第进入的第一个 Mode,启动完成后就不再使用
    • GSEventReceiveRunLoopMode:接受系统内部事件,通常用不到
    • kCFRunLoopCommonModes: 伪模式,不是一种真正的运行模式,一种标记模式,打上Common modes标记的模式下运行,kCFRunLoopDefaultMode和UITrackingRunLoopMode 都为标记上Common modes
    CFRunLoopTimerRef(CFRunLoopTimer)

    基于时间的触发器
    基本上说得就是NSTimer(CADisplayLink也是加到RunLoop),它受RunLoop的Mode影响
    CCD的定时器不受RunLoop的Mode影响

    CFRunLoopSourceRef(CFRunLoopSource)
    • 按官方文档分类:

      • Port-Base Source (基于端口)
      • Custom Input Sources (自定义)
      • Cocoa Perform Selector Sources
    • 按函数调用栈分类:

      • Source0: 非基于Port
      • Source1: 基于Port,通过内核和其他线程通信,接收、分发系统事件,再分发到Source0中处理
    • Source0: event事件,只含回调,需要先调用CFRunLoopSourceSignal(source),将这个Source标记为待处理,然后手动调用 CFRunLoopWakeUp(runloop) 来唤醒 RunLoop。

    • Source1: 包含了一个mach_port和一个回调,被用于通过内核和其他线程互相发送消息,能主动唤醒RunLoop线程

    CFRunLoopObserverRef(CFRunLoopObserver)

    可监听的状态变化有:

    typedef CF_OPTIONS(CFOptionFlags, CFRunLoopActivity) {
        kCFRunLoopEntry = (1UL << 0),               // 即将进入Loop:1
        kCFRunLoopBeforeTimers = (1UL << 1),        // 即将处理Timer:2    
        kCFRunLoopBeforeSources = (1UL << 2),       // 即将处理Source:4
        kCFRunLoopBeforeWaiting = (1UL << 5),       // 即将进入休眠:32
        kCFRunLoopAfterWaiting = (1UL << 6),        // 即将从休眠中唤醒:64
        kCFRunLoopExit = (1UL << 7),                // 即将从Loop中退出:128
        kCFRunLoopAllActivities = 0x0FFFFFFFU       // 监听全部状态改变  
    };
    

    RunLoop 处理逻辑

    注:进入RunLoop前,会判断模式是否为空,为空直接退出

    1. 通知Observer:即将进入Loop
    2. 通知Observer:将处理Timer
    3. 通知Observer:将要处理Source0
    4. 处理Source0
    5. 如果有Source1,跳9
    6. 通知Observer: 线程即将休眠
    7. 休眠,等待唤醒,直到:
      * Source0
      * Timer
      * 外部手动唤醒
    8. 通知Observer: 线程被唤醒
    9. 处理唤醒时收到的消息,之后跳2
    10. 通知Observer: 即将退出Loop

    应用

    1.NSTimer的应用
    [NSTimer scheduledTimerWithTimeInterval:2.0 target:self selector:@selector(run) userInfo:nil repeats:YES];
    

    上述代码会自动将定时器加入到RunLoop的默认模式下,相当于一下两句代码:

    NSTimer *timer = [NSTimer timerWithTimeInterval:2.0 target:self selector:@selector(run) userInfo:nil repeats:YES];
    [[NSRunLoop currentRunLoop] addTimer:timer forMode:NSDefaultRunLoopMode];
    

    比如,图片轮播器,拖拽时模式从默认到Tracking,此时定时器不响应,停止轮播

    2.ImageView

    场景:用户再拖拽时不显示图片,拖拽完成时显示图片

    1. 监听ScrollView滚动
    2. RunLoop 设置只在默认模式下显示图片
    3.PerformSelector

    当调用 NSObject 的 performSelecter:afterDelay: 后,实际上其内部会创建一个 Timer 并添加到当前线程的 RunLoop 中。所以如果当前线程没有 RunLoop,则这个方法会失效。

    当调用 performSelector:onThread: 时,实际上其会创建一个 Timer 加到对应的线程去,同样的,如果对应线程没有 RunLoop 该方法也会失效。

    4.线程常驻

    开启一个常驻线程,让一个子线程不进入消亡状态,等待其他线程发来消息,处理其他事件

    1.创建常驻线程 thread
    2.对常驻线程运行一下代码

    // 添加下边两句代码,就可以开启RunLoop,之后self.thread就变成了常驻线程,可随时添加任务,并交于RunLoop处理
        [[NSRunLoop currentRunLoop] addPort:[NSPort port] forMode:NSDefaultRunLoopMode];
        [[NSRunLoop currentRunLoop] run];
    

    3.需要常驻线程执行任务时,将任务放到该线程

    5.自动释放池

    在休眠前(kCFRunLoopBeforeWaiting)进行释放,处理事件前创建释放池,中间创建的对象会放入释放池

    特别注意:
    在启动RunLoop之前建议用 @autoreleasepool {…}包裹

    意义:创建一个大释放池,释放{}期间创建的临时对象

    4. autorealeasePool

    自动释放池的前世今生 ---- 深入解析 autoreleasepool

    黑幕背后的Autorelease

    提供了一种可以向一个对象延迟发送 release 消息的机制,由若干个AutoreleasePoolPage以双向链表的形式组合而成

    AutoreleasePoolPage

    class AutoreleasePoolPage {
        magic_t const magic;
        id *next;
        pthread_t const thread;
        AutoreleasePoolPage * const parent;
        AutoreleasePoolPage *child;
        uint32_t const depth;
        uint32_t hiwat;
    };
    
    • AutoreleasePool是按线程一一对应的(结构中的thread指针指向当前线程)
    • 每一个AutoreleasePoolPage的大小都是4096字节,这4096字节中,有56bit用于存储page的成员变量,剩下的用来存储加入到自动释放池中的对象
    • 上面的id *next指针作为游标指向栈顶最新add进来的autorelease对象的下一个位置
    • 一个AutoreleasePoolPage的空间被占满时,会新建一个AutoreleasePoolPage对象,连接链表,后来的autorelease对象在新的page加入
    • 对象调用autorelease方法,就是将这个对象加入到当前AutoreleasePoolPage的栈顶next指针指向的位置
    POOL_SENTINEL(哨兵对象)

    哨兵对象是nil的别名

    #define POOL_SENTINEL nil
    

    每个自动释放池初始化调用时,都会把一个哨兵对象push到自动释放池的栈顶,并返回这个哨兵对象的地址

    objc_autoreleasePoolPush的返回值正是这个哨兵对象的地址,被objc_autoreleasePoolPop(哨兵对象)作为入参,于是:

    1. 根据传入的哨兵对象地址找到哨兵对象所处的page
    2. 在当前page中,将晚于哨兵对象插入的所有autorelease对象都发送一次- release消息,并向回移动next指针到正确位置
    3. 补充2:从最新加入的对象一直向前清理,可以向前跨越若干个page,直到哨兵所在的page
    AutoreleasePool 流程

    自动释放池就是若干个AutoreleasePoolPage以双向链表的形式实现

    1. objc_autoreleasePoolPush,往AutoreleasePoolPage中的next位置插入一个哨兵对象(POOL_SENTINEL),并返回它的内存地址

      • page不存在,创建第一个page,并将对象添加到这个新创建的page中
      • page存在且没有满,直接将对象添加到当前page中,即next指向的位置
      • page存在且已满,创建一个新的page,并将对象添加到新的page中,然后关联child page
    2. 添加autorelease对象

    3. objc_autoreleasePoolPop,将之前返回的哨兵对象传入pop函数,根据这个对象地址找到哨兵对象所在的page,然后对晚于哨兵对象插入的所有autorelease对象都发送依次release消息,并向回移动next指针(可以跨越若干page,直到哨兵所在的page)

    Autorelease对象什么时候释放

    在没有手加Autorelease Pool的情况下,Autorelease对象是在当前的runloop休眠时释放的,而它能够释放的原因是系统在每个runloop迭代中都加入了自动释放池Push和Pop

    5.讲一讲响应链

    UIResponder
    史上最详细的iOS之事件的传递和响应机制-原理篇

    1. 什么是响应者链: 一系列响应者对象构成 -》 响应者
    2. 为什么需要: 用户交互,触摸点击了怎么判断由谁来作出反应
    3. 怎么实现的: 构成响应者链条(视图树状结构构建的同时也构建了一条条事件响应链) -> 确定第一响应者 -> 响应顺序

    响应者对象 UIResponder

    继承了UIResponder的对象,能接受处理、传递事件

    响应链

    由一系列响应者对象构成的链条,能清楚地呈现每个响应者之间的联系,可让一个事件多个对象处理

    确定第一个响应者

    即事件传递机制

    1. 发生触摸/其他事件
    2. 系统将事件加入UIApplication管理的事件队列中,并将队列中最前面的事件分发出去
    3. 事件传递给主窗口keyWindow,再在视图层次结构中找一个合适的视图来处理事件
    4. 传递顺序:UIApplication -> UIWindow -> Superview -> Subview
    5. 不能接受事件情况:userInteractionEnable=NO, isHidden=NO, alpha<=0.01
    6. 通过hitTest:withEvent:方法来查找第一响应者
    如何找到这个合适的视图
    1. 判断是否能接受事件
    2. 判断触摸点是否在自己身上,不在返回nil,再转3
    3. 从后往前遍历子控件(即从上面往下找)的hitTest:withEvent:方法,以此类推
    4. 直到找到点击区域最上面的子视图,并逐步返回给UIApplication

    响应顺序

    找到第一响应者后,应用程序会先调用第一响应者的处理事件,如果不能处理则调用nextResponder将事件传递给下一个响应者,其顺序:Subview -> Superview -> UIViewController -> UIWindow -> UIApplication

    注:
    UIViewController没有hitTest:withEvent:方法,所以控制器不参与查找响应视图的过程。但是控制器在响应者链中,如果控制器的View不处理事件,会交给控制器来处理。控制器不处理的话,再交给View的下一级响应者处理。

    响应链与手势/UIControl

    关于手势
    1. 手势的执行优先级高于响应者链
    2. 手势也通过hitTest方式查找响应视图
    3. 查找到第一响应者后,UIApplication向其派发消息,如果响应者链中存在能处理事件的手势,则手势响应事件,并执行touchesCancelled将响应者链打断
    关于UIControl
    1. UIControl也通过hitTest查找第一响应者
    2. 如果第一响应者是UIControl,则Application直接派发事件,并不再向响应者链派发消息
    3. UIControl不能处理事件,再交给手势处理或响应者链传递

    6.响应链相关问题

    6.1. 如何通过一个view查找它所在的viewController

    通过响应者链,循环查找nextResponder是否为UIViewController
     - (UIViewController *)parentController { UIResponder *responder = [self nextResponder]; while (responder) { if ([responder isKindOfClass:[UIViewController class]]) { return (UIViewController *)responder; } responder = [responder nextResponder]; } return nil; }

    6.2.如何扩大view的响应范围

    1. pointInside:withEvent用来判断一个点是否在视图中,而这个方法是通过bounds来判断的,如果要扩大响应范围,可重写该方法,将判断bounds的范围扩大
    2. bounds扩大的范围如果大于superview,那么扩大的地方也是响应不到的

    不规则点击区域判断也可重写该方法

    6.3.一个事件多个对象处理

    多个对象实现touches并调用super方法

    7.属性

    property = ivar + getter + setter 成员变量+存取方法

    • assign:
      1. 基本数据类型
      2. 直接赋值,不会更改值的引用计数
      3. 当引用计数为0时,对象销毁,编译器不会置为nil,指针仍指向被销毁内存, 产生野指针
    • weak:
      1. OC 对象
      2. 非拥有关系,不会更改值的引用计数
      3. 对象被销毁时,weak修饰属性自动赋值为nil
    • strong:
      1. 拥有关系,对旧值减少引用计数,新值增加引用计数
      2. 当一个对象不在有strong类型指针指向它,它就会被释放,即使还有weak指针
    copy和strong的区别

    前者深拷贝,赋值时,会对新变量的重新生成一份新的内存空间,后者浅拷贝,只是复制对象的指针

    assign可以用于OC对象吗

    可以,但是当OC对象的引用计数为0时,对象销毁,编译器不会置为nil,产生野指针

    8. weak如何实现自动赋nil

    iOS 底层解析weak的实现原理(包含weak对象的初始化,引用,释放的分析)

    Runtime对注册的类会进行内存布局,有个SideTable结构体是负责管理类的引用计数表和weak表,weak修饰的对象地址作为key存放到全局的weak引用表中,value是所有指向这个weak指针的地址集合,调用release会导致引用计数器减一,当引用计数器为0时调用dealloc,在执行dealloc时将会在这个weak的hash表中搜索,找到这个key的记录,将记录中所有附有weak修饰符的变量地址,设置为nil,并从weak表中删除记录。

    SideTable

    主要用于管理对象的引用计数和weak表

    struct SideTable {
         spinlock_t slock;   // 保证原子操作的自旋锁
         RefcountMap refcnts; // 引用计数的 hash 表
         weak_table_t weak_table; // weak 引用全局 hash 表
     }
    

    weak 表,全局弱引用表,使用不定类型对象的地址作为key,用weak_entry_t类型结构体对象作为value,weak_entry_t负责维护和存储指向一个对象的所有弱引用hash表

    // weak 表
    struct weak_table_t {
    	weak entry_t *weak_entries;  // 保存了所有指向指定对象的weak指针
    	size_t num_entries;  // 存储空间
    	uintptr_t mask; // 参与判断引用计数辅助量
    	uintptr_t max_hash_displacement; // 最大偏移量
    }
    

    1、初始化时:runtime会调用objc_initWeak函数,初始化一个新的weak指针指向对象的地址。

    2、添加引用时:objc_initWeak函数会调用 objc_storeWeak() 函数, objc_storeWeak() 的作用是更新指针指向,创建对应的弱引用表。

    3、释放时,调用clearDeallocating函数。clearDeallocating函数首先根据对象地址获取所有weak指针地址的数组,然后遍历这个数组把其中的数据设为nil,最后把这个entry从weak表中删除,最后清理对象的记录。

    9.Block

    Block

    函数指针+捕获上下文变量

    block的类型

    • 全局块(_NSConcreteGlobalBlock),存于全局内存,相当于单例
    • 栈块 (_NSConcreteStackBlock),存于栈内存,超出其作用域马上被销毁
    • 堆块 (_NSConcreteMallocBlock),存于堆内存,是一个带引用计数对象,当引用计数为0时会被销毁
    如何判断Block类型
    1. Block不访问外界变量(包括栈中和堆中的变量)
      Block既不在栈中也不在堆中,在代码段中,ARC和MRC皆是,此时为全局块
    2. Block访问外界变量
      MRC:访问外界变量的Block默认存储栈中
      ARC:访问外界变量的Block默认存储在堆中(实际在栈区,然后ARC情况下自动拷贝到堆区),自动释放

    Block复制

    配置在栈上的Block,如果其所属的栈作用域结束,该Block就会被废弃,对于超出Block作用域仍需使用Block的情况,Block提供了将Block从栈上复制到堆上的方法来解决这种问题

    ARC有效时,以下情况栈上的Block会自动复制到堆上:

    1. 调用block的copy方法
    2. 将block作为函数返回值时(MRC无效,需手动)
    3. 将block赋值给__strong修改的变量(MRC时无效)
    4. 向Cocoa框架含有usingBlock的方法或GCD的API传递Block参数时

    其他情况向方法的参数中传递block时,需手动调用copy

    捕获变量

    1. 默认情况
      对block外的变量引用,默认将其复制到数据结构中,存储在block的结构体内部,此时,block只能访问不能修改变量

    2. __block修饰外部变量
      block复制其引用地址来实现访问,可修改__blcok修饰的外部变量的值

    原理:将栈上用__block修饰的自动变量封装成一个结构体,让其在堆上创建,以方便从栈上或堆上访问或修改同一份数据

    循环引用

    因为对象obj在Block被copy到堆上的时候自动retain了一次。因为Block不知道obj什么时候被释放,为了不在Block使用obj前被释放,Block retain了obj一次,在Block被释放的时候,obj被release一次。

    retain cycle问题的根源在于Block和obj可能会互相强引用,互相retain对方,这样就导致了retain cycle,最后这个Block和obj就变成了孤岛,谁也释放不了谁。

    10.isa、对象、类对象、元类和父类之间的关系?

    (那张表isa/superclass 表)

    • 对象是类的一个实例,类对象是元类的一个实例,元类保存了类的类方法,当一个类方法被调用时,元类会首先查找它本身是否有该类方法的实现,如没有,则该元类会向它父类查找该方法,一直找到继承链根部,找不到就转发
    • 元类的isa指针指向一个根元类,根元类指向自己形成一个闭环
    • 类对象和元类对象有同样的继承关系
    • isa:objc_class 结构指针,在OC中,一个对象所属于哪个类,是由它的isa指针指向的

    11. OC对象释放的流程

    • release: 引用计数器-1,直到0开始释放
    • dealloc:对象销毁的入口
    • dispose: 销毁对象和释放内存
      • objc_destruchInstance: 调用C++的清理方法和移除关联引用
        • clearDeallocating: 把weak置为nil,销毁当前对象的表结构
      • free: 释放内存

    12.数据持久化

    • plist::XML文件,读写都是整个覆盖,需读取整个文件,适用于较少的数据存储,一般用于存储app设置相关信息
    • NSUserDefault:通过UserDefault对plist文件进行读写操作,作用和应用场景同上
    • NSKeyedArchiver: 被序列化的对象必须支持NSCoding协议,可指定任意数据存储位置和文件名,整个文件覆盖重写,读写大数据性能低
    • Sqlite:轻量级数据库
    • CoreData:大规模数据持久化方案,基本逻辑类似于SQL数据库,每个表为entity,可增删查改对象实例,提供模糊搜索、过滤搜索、表关联等各种复杂操作,但学习曲线高,操作复杂

    13.Array和Set的区别

    • array:分配的时一片连续的存储单元,有序的,查找时需要遍历整个数组查找,查找速度不如hash
    • set:不是连续的存储单元,数据无序,通过hash映射存储的位置,直接对数据hash即可判断对应的位置是否存在,查找速度快,不允许重复数据
    • 遍历速度:array的数据结构时一片连续的内存单元,读取速度快,set不是连续的非线性的,读取速度慢

    14.nil、Nil、NULL、NSNull的区别

    nil:空实例对象(给对象赋空值)
    Nil:空类对象(Class class = Nil)
    NULL:指向C类型的空指针
    NSNull:类,用于空对象的占位符(用于替代集合中的空对象,还有判断对象是否为空对象)

    15.loadView的作用

    在UIViewController对象的view属性被访问到且为空的时候调用

    用来自定义view,只要实现了这个方法,其他通过xib或storyboard创建的view都不会被加载,不能调用super方法

    16. layoutIfNeeded和setNeedsLayout的区别

    UIView的setNeedsLayout,layoutIfNeeded等方法介绍

    layout机制相关方法:

    • layoutSubviews: 内部调整子视图时重写,若在外部设置subviews位置则不写(iOS6前方法缺省实现为空,iOS6后缺省实现是使用在此view上的constraints,即auto layout)
    • layoutIfNeeded: 如有需要刷新标记,立即调用layoutSubviews进行布局,此方法会遍历整个view层次请求layout(没有标记,则不掉用layoutSubviews)
    • setNeedsLayout: 标记为需要重新布局,不立即刷新,在系统runloop的下一个周期自动调用layoutSubviews(layoutSubviews一定会被调用)

    1.如果要立即刷新,先调用setNeedsLayout,标记为需要布局,再调用layoutIfNeeded,实现布局
    2.视图第一次显示之前默认标记需要刷新
    3. layoutIfNeeded不一定会调用layoutSubviews,但setNeedsLayout一定会调用layoutSubviews


    • sizeThatFits:传入的参数是receiver当前的size,返回一个适合的size
    • sizeToFit: 自动调用sizeThatFits方法,不应在子类中重写,应重写sizeThatFits,可手动直接调用

    以上两个方法没有递归,对subviews不负责,只负责自己


    • drawRect: 重写此方法,执行重绘任务
    • setNeedsDisplay: 标记为需要重绘,异步调用drawRect,在下一个draw周期自动重绘,iPhone设备的刷新频率是60hz,即1/60秒后重绘
    • setNeedsDisplayInRect: 标记为需要局部重绘

    layoutSubviews触发条件:

    1. init初始化不触发,但initWithFrame且rect不为zero时会触发;
    2. addSubview
    3. 设置frame且frame有变化
    4. 滚动UIScrollView
    5. 旋转Screen触发父UIView上的layoutSubviews
    6. 改变UIView大小也会触发父UIView上的layoutSubviews
    7. 直接调用setLayoutSubviews
    8. 直接调用setNeedsLayout

    drawRect触发条件:

    1. UIView初始化时未设置rect大小,则不自动调用,调用顺序在Controller->loadView,viewDidLoad方法之后
    2. sizeToFit后被触发(可先调用sizeToFit计算size再系统自动调用drawRect方法)
    3. 设置contentMode为UIViewContentModeRedraw,则每次设置或更改frame时自动调用drawRect
    4. 直接调用setNeedsDisplay/setNeedsDisplayInRect(rect不为0)触发

    以上1,2推荐,3,4不提倡

    基于约束的AutoLayer方法:

    • setNeedsUpdateConstraints: 当view的某个属性改变,并可能影响到constrain时,需效用此方法去标记constrains需要再未来的某个点刷新,系统然后调用updateConstraints
    • needsUpdateConstraints:
    • updateConstraintsIfNeeded: 立即触发约束布局,自动更新布局
    • updateConstraints: view重写此方法在其中建立constraints(在实现最后调用[super updateConstraints])

    17.UIView和CALayer的区别

    • 都是UI操作的对象,都是NSObject的子类,发生在UIView上的操作本质上也发生在对应的CALayer上
    • CALayer时绘制内容的,UIView是CALayer用于交互的抽象,UIView是UIResponder的子类,提供了很多CALayer没有的交互接口,主要负责处理用户触发的种种操作
    • CALayer在图像和动画渲染上性能更好,因为UIView有冗余的交互接口,而且相比CALayer还有层级之分,CALayer在无需处理交互时进行渲染可节省时间

    18.applicationWillEnterForeground和applicationDidBecomeActive都会在哪些场景下被调用?

    App 生命周期

    • Not running (未运行) : 程序未启动
    • Inactive(未激活): 激活和后台状态切换时出现的短暂状态
    • Active (激活):在屏幕显示的正常运行状态,该状态下可接收用户输入并更新显示
    • Backgroud (后台): 程序在后台且能执行代码。
    • Suspended (刮起) :程序在后台不能执行代码

    Inactive/Active的切换:
    一般:前后台应用切换,Inactive会在Active和Background之间短暂出现

    其他:Active和Inactive在前台运行时切换,比如来电拒接、拉下通知栏、系统弹出Alert

    AppDelegate协议

    • application:didFinishLaunchingWithOptions: 程序首次已完成启动时执行
    • applicationWillResignActive(将进入后台): 程序将要失去Active状态,比如按下Home键、来电,这个方法用来:
      • 暂停正在执行的任务
      • 禁止计时器
      • 减少OpenGL ES帧率;
      • 若未游戏应暂停游戏
    • applicationDidEnterBackgroud(已进入后台) :进入后台时调用,这个方法用来:
      • 释放共享资源
      • 保存用户数据(写到硬盘)
      • 作废计时器
      • 保存足够的程序状态以便下次恢复
    • applicationWillEnterForeground(将进入前台) :用来撤销applicationWillResignActive
    • applicationDidBecomeActive (已经进入前台) :若程序之前在后台,最后在此方法内刷新用户界面
    • applicationWillTerminate: 程序将退出时调用,记得保存数据

    19.CocoaPods的工作原理

    将所有依赖库放到一个名为Pods的项目中,然后让主项目依赖Pods项目,使源码工作从主项目移到了Pods项目中,Pods项目最终会编译一个libPods.a 的文件,主项目只需要依赖这个.a文件即可

    使用CocoaPods前引用第三方:
    1.复制依赖库的源码
    2.添加依赖框架/动态库
    3.设置参数
    4.管理更新

    20.ARC的工作原理

    深入理解Objective C的ARC机制

    ARC

    auto reference count 自动引用计数,编译器会自动插入对应的代码,再结合Objective C的runtime,实现自动引用计数

    内部实现:

    • retain 增加引用计数
    • release 降低引用计数,引用计数为0的时候,释放对象
    • autorelease 在当前autorealsepool释放后,进行release
    SideTable内部实现
    - (id)retain {
        return ((id)self)->rootRetain();
    }
    inline id objc_object::rootRetain()
    {
        if (isTaggedPointer()) return (id)this;
        return sidetable_retain();
    }
    

    其本质是调用sidetable_retain

    id objc_object::sidetable_retain()
    {
        //获取table
        SideTable& table = SideTables()[this];
        //加锁
        table.lock();
        //获取引用计数
        size_t& refcntStorage = table.refcnts[this];
        if (! (refcntStorage & SIDE_TABLE_RC_PINNED)) {
             //增加引用计数
            refcntStorage += SIDE_TABLE_RC_ONE;
        }
        //解锁
        table.unlock();
        return (id)this;
    }
    

    SideTable结构

    struct SideTable {
        spinlock_t slock; // 自旋锁
        RefcountMap refcnts; // 引用计数表,以地址作为key,引用计数的值作为value
        weak_table_t weak_table; //weak 表
         //省略其他实现...
    };
    

    release的实现

      SideTable& table = SideTables()[this];
        bool do_dealloc = false;
        table.lock();
        //找到对应地址的
        RefcountMap::iterator it = table.refcnts.find(this);
        if (it == table.refcnts.end()) { //找不到的话,执行dellloc
            do_dealloc = true;
            table.refcnts[this] = SIDE_TABLE_DEALLOCATING;
        } else if (it->second < SIDE_TABLE_DEALLOCATING) {//引用计数小于阈值,dealloc
            do_dealloc = true;
            it->second |= SIDE_TABLE_DEALLOCATING;
        } else if (! (it->second & SIDE_TABLE_RC_PINNED)) {
        //引用计数减去1
            it->second -= SIDE_TABLE_RC_ONE;
        }
        table.unlock();
        if (do_dealloc  &&  performDealloc) {
            //执行dealloc
            ((void(*)(objc_object *, SEL))objc_msgSend)(this, SEL_dealloc);
        }
        return do_dealloc;
    
    Autorelease
    //autorelease方法
    - (id)autorelease {
        return ((id)self)->rootAutorelease();
    }
    
    //rootAutorelease 方法
    inline id objc_object::rootAutorelease()
    {
        if (isTaggedPointer()) return (id)this;
    
        //检查是否可以优化
        if (prepareOptimizedReturn(ReturnAtPlus1)) return (id)this;
        //放到auto release pool中。
        return rootAutorelease2();
    }
    
    // rootAutorelease2
    id objc_object::rootAutorelease2()
    {
        assert(!isTaggedPointer());
        return AutoreleasePoolPage::autorelease((id)this);
    }
    
    

    autorelease方法会把对象存储到AutoreleasePoolPage的链表里。等到auto release pool被释放的时候,把链表内存储的对象删除。所以,AutoreleasePoolPage就是自动释放池的内部实现。

    21. 进程间通信

    1. URL Scheme
    2. Keychain
    3. UIPasteboard
    4. UIDocumentInteractonController
    5. local socket
    6. AirDrop
    7. UIActivityViewController
    8. App Groups

    CFMessagePort
    mach port

    三.计算机网络

    1. get和post的区别

    在使用上:
    • 1.get提交的数据有长度限制,而post没有,这是浏览器设置的不同引起的
    • 2.get通过URL传递参数,而post放在body中
    • 3.post比get安全,因为数据放在URL上
    在本身上:

    get请求是幂等性的,即同一个URL的多个请求返回同样的结果,而post不是(因为get是幂等的,在网络不好的隧道中会尝试请求,如果用get来请求增删改数据,会有重复操作的风险)

    2. 为什么说TCP是面向字节流,而UDP是面向报文

    • UDP面向报文,发送方的UDP对应用层交付下来的报文,在添加首部后直接向下发送交付给IP层,不拆分也不合并,保留报文边界,所以需要选择合适的报文大小;而接收方的UDP收到报文后,拆除首部就原封不动地交付上层的应用层,一次交付一个报文
    • TCP将数据看成一连串无结构的字节流,TCP有一个缓冲区,当应用层传送的数据库太大,TCP可把它可它划分短一点再传,若太小,也可累积足够多的字节后再构成报文发送出去

    3. HTTPS是如何保证安全的/SSL建立连接的过程/数字证书怎么验证

    展开全文
  • IOS 知识点总结(一)

    2018-10-17 20:41:14
    IOS线程讲解 对于单核的处理器,可以把多个操作放到不同的线程,当用户操作完UI之后其他后续任务在其他线程操作,当CPU空闲时继续其他操作; 对于多核操作,操作任务可以分散到多个空闲的CPU中继续执行;...

    ###IOS线程讲解

    • 对于单核的处理器,可以把多个操作放到不同的线程,当用户操作完UI之后其他后续任务在其他线程操作,当CPU空闲时继续其他操作;
    • 对于多核操作,操作任务可以分散到多个空闲的CPU中继续执行;
    • 一个核(CPU)可以有多个线程,但其实是CPU在快速的调度不同的任务,造成多线程运行的假象;
    • 每一个线程同时只能处理一件事情,其他的事情会放在队列中等待,当CPU空闲时在接着执行队列中的其他任务;
    • 每个APP的启动就是一个进程,每一个进程会有一个主线程(更新UI呈现),这个线程是其他线程的父线程;
    • 启动一个线程时,并非就一定立即执行,而是处于就绪状态,当系统CPU根据当前状态调度时才真正执行
    • 每开辟一个线程就需要占用系统的开销;

    ####1.线程操作的种类:

    • NSthread:

    轻量级的多线程,需要开发者自己来管理线程的生命周期,由于NSthread需要开发者自动管理生命周期,一般情况下,会放到自动缓冲池中:@autoreleasepool。
    当在多个线程使用时,线程顺序不可控,只能通过调整线程的优先顺序(0-1),默认的优先顺序是0.5;

    • NSOperation:

    该类不能直接使用,只能使用其自类:NSInvocationOperation和NSBlockOperation以及使用NSOperationQueue这个队列中,创建好之后需要放到NSOperationQueue创建好的队列中去;
    比较容易管理线程总数以及线程的顺序;
    使用NSOperation进行多线程开发可以设置最大的并发数;
    可以设置依赖线程,被依赖的线程会优先执行;
    NSOperation是对GCD的OC封装,但是相比于GCD基于C语言开发,效率更高,所以一般建议如果任务之间有依赖关系或者想监听任务的完成是会优先选择NSoperation;

    • GCD(Grand Central Dispatch):

    基于C语言封装的面向过程的线程操作;
    GCD统一管理队列中的任务,队列分为串行队列和并行队列。
    1.串行队列:只有一个线程,所有任务按顺序执行;
    2.并发队列:有多个线程,根据核数决定任务的执行速度,同时保证队列的特性,先进先出;
    3.主队列:主要用于处理UI操作;
    在GCD中一个操作是多线程还是 单线程取决于当前队列的类型和执行方法,只有为并行队列并且使用异步是才能在多个线程中执行;
    GCD中不需要增加@autoreleasepool,GCD会自己管理内存;
    顺序的控制的话可以使用锁或者信号量操作;

    • 解决资源抢占

    使用同步锁:NSlock和@Synchronized,可以再锁未打开之前不允许其他操作;
    GCD引用信号量
    初始化的信号量必须大于0,每发送一个通知,信号量加1,每发送一个等待信号,信号量减1,如果信号量为0则信号会处于等待状态,知道信号量大于0开始执行;

    • NSCondition:控制线程通信

    NSCondition主要用于解决线程之间的调度关系,可以使用wait方法控制某个线程处于等待状态,知道其他线程调用signal(唤醒一个线程,如果有多个则唤醒任意一个)或者broadcast(此方法会唤醒所有的等待线程),方法唤醒该线程才能继续;
    1.GCD的任务:
    同步:在当前线程中依次执行任务;
    异步:新开线程在新线程中执行任务;
    2.GCD队列:
    串行队列:让任务有序的执行,同时只能调度一个任务执行;
    并行队列:可以让多个任务并发、同时执行,可以同时调度多个任务执行;

    • 总结
      1.线程安全问题:资源共享造成的安全问题,多个线程同时操作共享资源,会造成数据的错乱,在这种情况下一般会选择采用锁机制来处理该问题;
      2.线程同步:使多个线程一次有序的执行,就是上面的加锁机制,把共享资源的读写操作锁起来常用的是互斥锁;
      3.线程间的同步:一个线程执行完任务之后,把执行的结果传递到另外一个线程,叫线程间通信。这里写图片描述
    展开全文
  • 知识点汇总一,基础 一,基础 Runloop和线程的关系? 自动释放池什么时候释放? 什么情况下使用weak关键字,和assign的区别? 怎么用copy关键字? @property(copy) NSMutableArray * array;这写法会有什么...

    知识点汇总

    一,基础

    1. Runloop和线程的关系?

    2. 自动释放池什么时候释放?

    3. 什么情况下使用weak关键字,和assign的区别?

    4. 怎么用copy关键字?

    5. @property(copy) NSMutableArray * array;这写法会有什么问题?

    6. 如何让自己的类用copy修饰符?即让自己写的对象具备拷贝功能?

    7. @property的本质是什么? ivar,getter,setter如何生成并添加到这个类中的?

    8. 多线程

    9. @protocol和category中如何使用@property?

    10. @property中有哪些属性关键字?

    11. weak属性需要在dealloc中置nil么?

    12. @synthesize和@dynamic分别有什么作用?

    13. ARC下,不是显示指定任何属性关键字时,默认的关键字都有哪些?

    14. 用@property声明的NSString(或NSArray,NSDictionary)经常使用copy关键字,为什么?如果改用strong关键字,可能造成什么问题?

    15. @synthesize合成实例变量的规则是什么?假如property名为foo,存在一个名为_foo的实例变量,那么还会自动合成新变量么?

    16. 在有了自动合成属性实例变量之后,@synthesize还有那些使用场景?

    17. objc中想一个nil对象发送消息将会发送什么?

    18. objc中想一个对象发送[obj foo]和objc_msgSend()函数直接有什么关系?

    19. 什么时候会包unrecognized selector的异常?

    20. 一个objc对象如何进行内存布局?(考虑有父类的情况)

    21. 一个objc对象的isa指针指向什么?有什么作用?

    22. runtime如何通过selector找到对应的IMP地址?(分别考虑类方法和实例方法)

    23. 下面的代码输出什么?

    24. 使用runtime Associate方法关联的对象,需要在主对象dealloc的时候释放么?

    25. objc中的类方法和实例方法有什么本质的区别和联系?

    26. _objc_msgForward函数是做什么的,直接调用它将会发生什么?

    27. runtime如何实现weak变量的自动置nil?

    28. 能否想编译后得到的类中增加实例变量?能否想运行是创建的类中添加实例变量?为什么?

    29. runloop和线程有什么关系?

    30. runloop的mode作用是什么?

    31. 以+ scheduledTimerWithTimeInterval…的方式触发的timer,在滑动页面上的列表时,timer会暂停回调,为什么?如何解决?

    32. 猜想runloop内部是如何实现的?

    33. objc使用什么机制管理对象内存?

    34. ARC通过什么方式帮助开发者管理内存?

    35. BAD_ACCESS在什么情况下出现?

    36. 使用block是什么情况会发生引用循环,如何解决?

    37. 在block内如何修改block外部变量?

    38. 苹果是符号实现autoreleasepool的?

    39. 不手动知道autoreleasepool的前提下,一个autorelease对象在什么时刻释放?(比如在一个vc的viewDidLoad中创建)

    40. 使用系统的某些blocak api(如UIView的block版本写动画时),是否也考虑引用循环问题?

    41. GCD的队列(dispatch_queue_t)分哪两种类型?

    42. 如何用GCD同步若干个异步调用?(如根据若干个url异步加载多张图片,然后在都下载完成后合成一张整图)

    43. dispatch_barrier_async的作用是什么?

    44. 苹果为什么要废弃dispatch_get_current_queue?

    45. 以下代码运行结果如何?
      在这里插入图片描述
      只输出:1。 发送主线程锁死。

    46. addObserver:forKeyPath:options:context:各个参数的作用分别是什么,observer中需要实现哪个方法才能获得KVO回调?

    47. 如何手动触发一个value的KVO?

    48. 若一个类有实例变量NSString * _foo, 调用setValue:forKey:时,可以以foo还是_foo 作为key?

    49. KVC的keyPath中的集合运算符如何使用?

    50. KVC和KVO的keyPath一定是属性么?

    51. 如何关闭默认的KVO的默认实现,并进入自定义的KVO实现?

    52. apple用什么方式实现对一个对象的KVO?

    53. IBOutlet连出来的视图属性为什么可以被设置成weak?

    54. IB中User Defined Runtime Attributes如何使用?

    55. 如何调试BAD_ACCESS错误?

    56. lldb (gdb) 常用的调试命令?

    57. IOS容易引起”循环引用“的几种场景

    58. 类方法load和initialized的区别?

    59. HTTP和HTTPS

    60. 常见的Exception Type

    61. Category和Extension

    62. 响应者链(responder chain)

    63. UITableView的优化

    64. 离屏渲染(Offscreen-Render)

    65. UIView和CALayer

    66. TCP和UDP

    67. socket和http

    68. 远程推送APNS

    69. IOS应用程序生命周期

    70. View视图生命周期

    71. autorelease pool

    72. view layout

    73. 应用程序的架构MVC, MVVM

    74. FMDB,SQLite

    75. 简述内存分区情况

    76. 各属性作用

    77. 简述Notification,KVC,KVO,delegate?区别?

    78. id和nil代表什么

    79. nil,Nil,NULL, NSNull

    80. 向一个nil对象发送消息会发送什么?

    81. self. 和 self-> 的区别

    82. 如何访问并修改一个类的私有 属性

    83. 如何为class 定义一个对外只读,对内可读写的属性

    84. OC中,meta-class 指的是什么?

    85. NSString用copy和strong的区别

    86. 创建一个对象的步骤

    87. setter,getter

    88. id,instancetype是什么,区别?

    89. 内存管理

    90. KVC的底层实现?

    91. block的内存管理

    92. App的启动过程,从main说起

    93. tableview的cell里面如何嵌套collectionview

    94. awakeFromNib和viewDidLoad的区别

    95. 常见的Crash场景

    96. AFN断点续传

    97. 客户端的缓存机制

    98. 数据存储方式

    99. App需要加载超大量的数据,给服务器发送请求,但服务器卡住了,如何解决?

    100. 网络图片处理问题中怎么解决一个相同的网络地址重复请求的问题?

    101. 如何用GCD同步若干个异步调用?(如根据若干个url异步加载多张图片,然后在都下载完成后合成一张整图)

    102. NSOperation,CGD,NSThread的区别

    103. 是否可以把比较耗时的操作放在NSNotificationCenter中

    展开全文
  • 1.类方法创建的对象的作用域或者说是会在什么时候释放?类方法创建的对象会在合适的地方自动的插入antorelease,所以不需要我们去手动的发送release或者autorelease。例如imageNamed这个方法,使用这种方式生成的...
  • ios 基础知识点总结

    2018-07-24 12:00:07
    1、设置UILabel行间距 NSMutableAttributedString* attrString = [[NSMutableAttributedString alloc] initWithString:label.text]; NSMutableParagraphStyle *style = [[NSMutableParagraphStyle alloc] init];...
  • 以下的知识点都是平时的积累 不足之处请多指教 1. 由于证书的原因,安装在手机上的iOS Demo app 运行几天后就无法打开。 2. typedef returnType (^name)(arguments); 例子:typedef void (^btnClickedHandler)...
  • 1.UI视图相关面试问题思维导图 2.Objective-C语言特性相关面试问题 3.Runtime相关面试问题思维导图 4.内存管理相关面试问题思维导图 5.Block相关面试问题思维导图 6.多线程相关面试问题思维导图 ...
  • 1、堆和栈什么区别?  答:管理方式:对于栈来讲,是由编译器自动管理,无需我们手工控制;对于堆来说,释放工作由程序员控制,容易产生memory leak。 2、数组和链表什么区别?... 答:数组是将元素在内存中连续...
  • 截至2013年11月4日,从事移动开发已经快三年,一直从事项目的开发,从未对知识点进行总结,恰逢最近事件较为宽裕,对iOS开发进行相应的知识点总结,以便自身提高,以下是复习大纲: 一. 语言部分(OC语言部分) 1...
  • iOS 知识图谱

    2019-02-03 10:47:56
    iOS知识图谱IOS开发必备知识IOS知识框架汇总 IOS开发必备知识 IOS知识框架汇总 框架基础知识 进阶路线 模块细分
  • 1、去除数组中重复的对象NSArray *newArr = [oldArr valueForKeyPath:@“@distinctUnionOfObjects.self"];2、强/弱引用#define WeakSelf(type) __weak typeof(type) weak##type = type; // weak ...
  • iOS开发零碎知识点

    2018-04-02 17:28:41
    记录一些常用和不常用的iOS知识点,防止遗忘丢失。(来源为收集自己项目中用到的或者整理看到博客中的知识点),如有错误,欢迎大家批评指正;如有好的知识点,也欢迎大家联系我,添加上去。谢谢! 欢迎大家关注...
  • iOS进阶必须要学习的知识点之Runloop
  • 有两年没有开发iOS了,最近又开始接触iOS开发,发现好多旧知识忘记了,好多新知识还不知道。 最近正在学习中,一些心得做下memo,也可以和大家分享一下。 初始化方法的返回值类型(类名,id,instancetype) 初始...
  • 1.id与instanncetypeid 与 instancetype 区别 1.id 可以当返回值类并且可以声明对象 2.instancetype 只可以当返回值类型 3.instancetype 返回和方法所在类相同类型的对象,id返回未知类型的对象(instancetype ...
  • iOS开发中经常会遇到一些图文混排的情况,特别是在微博类应用的开发过程中经常会遇到各种表情,各种链接的解析。笔者在实践开发中经历了几次这种类型的开发,由最开始的利用WebView进行布局到最后利用富文本进行...
  • iOS开发中数据的存储有很多种,最常见的无非是NSUserDefault 、Plist、 DB、 写文件 、 CoreData几种,以下分别对几种方式的用途及用法进行说明: 1. NSUserDefault NSUserDefaults类用于保存应用程序设置和...
  • UITableVIew是iOS开发中使用最为广泛的一种控件,对于UITableView的基本用法本文不做探讨,本文主要是针对UITableView的展开与收缩进行阐述,在文章的后面也会探讨一下横向table的用法: 1. UITableView的展开与收缩...
  • iOS功能 iOS 如何跳转到系统设置里的指定子功能界面 http://blog.csdn.net/jingfa1993/article/details/70174524 iOS开发中UILocalNotification本地通知实现简单的提醒功能 ...iOS开发网络篇之文
1 2 3 4 5 ... 20
收藏数 62,775
精华内容 25,110
关键字:

ios知识点