iOS 疯狂讲义(上)整理

作者: 赫子丰 | 来源:发表于2019-08-20 19:11 被阅读0次

    1、LLVM(低级虚拟机)的 Clang 编译器来编译 OC 程序

    Clang(前端)-- LLVM(后端)
    Clang: a C language family frontend for LLVM;LLVM的C语言家族(C、C++、OC)前端。
    LLVM 包含LLVM中介码(LLVM IR)、LLVM除错工具、LLVM C++标准库等一套工具,和传统底层虚拟机并没什么关系。(LLDB 也是 LLVM 的一部分)
    参考链接

    2、OC 数据类型:

    基本类型:整型、字符型、浮点型(float、double)、枚举型
    构造类型:数组类型、结构体类型、共同体类型
    指针类型

    3、NSLog 输出函数及格式字符

    d 以带符号的十进制形式输出的整数(正数不输出符号)

    o 以八进制无符号形式输出的整数(不输出0前缀)

    x 以十六进制无符号形式输出的整数(不输出0x前缀)

    u 以无符号十进制形式输出的整数

    c 以字符形式输出,只输出一个字符

    s 输出C风格的字符串

    f 以小数形式输出浮点数,默认输出6位小数

    e 以指数形式输出浮点数,数字部分默认输出6位小数

    g 自动选择%f或%e其中之一,保证输出宽度较短的格式,并且不会输出无意义的0

    p 以十六进制形式输出变量所代表的地址值

    @ 输出objective-C的对象

    NSLog() 函数支持的附加字符
    l(字母)可在格式字符d、o、x、u之前,用于输出长整型整数;也可以在f、e、g之前,用于输出长浮点型数

    m(整数) 指定输出数据所占的最小宽度

    .n 对于浮点数,表示输出n位小数,对于字符串,表示截取的字符个数

    - 表示输出的数值向左对齐(和m配合使用,相当于负号)

    4、ANSI C

    OC 不支持复杂的运算,可以引入 ANSI C 的 <math.h>头文件,其中定义了很多复杂的数学计算函数
    同时ANSI C 还提供了很多函数用于处理 C 字符串

    5、原码、反码、补码

    参考链接
    正整数的原码、补码、反码都一样;
    负数的反码就是他的原码除符号位外,按位取反;
    负数的补码等于他的原码自低位向高位,尾数的第一个‘1’及其右边的‘0’保持不变,左边的各位按位取反,符号位不变。(只不过是补码正好就等于反码加1罢了。)

    6、goto 语句:用于多层循环跳出

    当我们要从循环体内跳出循环或忽略循环体剩下的语句时——如果只是跳出不带嵌套的循环,或忽略不带嵌套循环的剩下语句,可使用break或continue;但如果要从多层嵌套的内层循环直接跳出外层循环,这就可以考虑使用goto语句,或需要从多层循环的内层循环中忽略本次循环的剩下语句,也可以考虑使用goto语句。
    在if语句中若是使用break或continue语句都只是作用于内层循环,不会影响外层循环;而goto语句则连外层循环也跳过。

    7、值传递

    函数的参数传递机制:值传递,就是将实际参数值的副本传入函数内,而参数本身不会受到任何影响。

    8、存储

    • 动态存储:程序运行期间根据需要动态分配内存的存储方式。(函数形参变量、非static的局部变量、函数执行的现场数据以及返回地址等)
    • 静态存储:程序运行开始就分配固定内存的存储方式(全局变量、static修饰的局部变量)

    9、C 语言特性

    • “&”:取地址运算符
    • “*” :取变量运算符

    10、assign , copy, strong, retain, weak, assign区别

    • 1.是否开辟新的内存
    • 2.是否对地址有引用计数增加

    可变变量中,copy是重新开辟一个内存,strong,weak,assgin后三者不开辟内存,只是指针指向原来保存值的内存的位置,storng指向后会对该内存引用计数+1,而weak,assgin不会。weak,assgin会在引用保存值的内存引用计数为0的时候值为空,并且weak会将内存值设为nil,assign不会,assign在内存没有被重写前依旧可以输出,但一旦被重写将出现奔溃

    不可变变量中,因为值本身不可被改变,copy没必要开辟出一块内存存放和原来内存一模一样的值,所以内存管理系统默认都是浅拷贝。其他和可变变量一样,如weak修饰的变量同样会在内存引用计数为0时变为nil。

    容器本身遵守上面准则,但容器内部的每个值都是浅拷贝。
    (当NSString长度小于10时不再遵循引用计数规则,Tagged Pointer技术对其进行了优化。基本意思就是默认会将一些长度小于10的字符串直接保存在指针上面,下次创建相同值的时候直接用同一份拷贝,这样既减少了一次指针到值的访问,又减少了一份内存的占用。)

    11、KVC 执行机制

    • ①优先调用set<key>方法 如果有该方法 在setter 方法中完成设置
    • ②当没有set方法时,kvc机制会检查+(bool)accessInstanceVariablesDirectly;是否返回YES。当你重写了该方法并且返回的时No时 kvc机制会直接执行setValue:forUndefinedKey:啊这样做可以让你的类不被别人使用kvc;
    • ③一般情况开发者不会重写+(bool)accessInstanceVariablesDirectly方法 所以kvc 会搜索有没有名称为<key>的成员变量。无论是在.h还是在.m部分定义也无论是使用了什么访问修饰符只要存在成员变量都可以对其成员变量进行赋值。
    • ④如果该类中没有set,也没有_成员变量,kvc机制会搜索_is<key>成员变量。
    • ⑤如果该类还是没有_ 也没有_is kvc 会继续搜索<key>,is<key>再给它们赋值。
    • ⑥如果都不存在 系统将会执行setValue:forUndefinedKey 抛出异常。

    12、私有方法

    OC 不存在绝对隐藏的方法,其他类只需要 performSelector 即可调用

    13、KVO 使用场景

    数据模型组件的状态数据发生改变时,试图组件可以动态的更新自己。

    • (1)KVO 是基于 runtime 机制实现的
    • (2)当一个对象(假设是person对象,对应的类为 JLperson)的属性值age发生改变时,系统会自动生成一个继承自JLperson的类

    NSKVONotifying_JLPerson,在这个类的 setAge 方法里面调用

        [super setAge:age];
        [self willChangeValueForKey:@"age"];
        [self didChangeValueForKey:@"age"];
    

    三个方法,而后面两个方法内部会主动调用
    -(void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary<NSString *,id> *)change context:(void *)context方法,在该方法中可以拿到属性改变前后的值.

    14、多继承

    OC 严格说不支持多继承,但并不代表他只有一个父类。应该是只有一个直接父类,但可以有无限多个间接父类。

    15、强制类型转换

    只是改变了该指针变量的编译时类型,该变量所指向对象的实际类型不会发生改变。

    16、包装类

    • NSValue、 NSNumber :可以用于包装 short、int、long、float、char、指针、对象id等数据项,这样就可以塞进集合中。
    • NSInteger 、NSUInteger 、CGFloat并不属于包装类
    • 自动装箱生产的 NSNumber 不支持 ARC,但是不能把浮点型数赋值给 NSNumber 类型的变量。建议显式生成 NSNumber 对象。

    17、OC 反射机制

    就是与运行环境交互:直接通过 OC 源码、通过NSObject 定义的方法进行动态编程、直接调用运行时函数进行动态编程。
    运行状态中,对于任意一个类,都能够知道这个类的所有属性和方法;对于任意一个对象,都能够调用它的任意一个方法;这种动态获取的以及动态调用对象的方法的功能就是反射机制.

    18、动态调用方法

    • performSelector
    • objc_msgSend

    19、自动释放

    集合类如果不是用 alloc、new、copy、mutableCopy创建的话,相当于默认创建了一个自动释放的对象。

    20、OC 集合类和 C 数组

    集合类和数组不太一样,C 的数组元素既可以是基本数据类型,也可以是对象,但是 OC 中的集合类(NSArray、NSSet、NSDictionary)只能保存对象类型。

    21、NSSet 用 Hash 算法来存储集合中的元素,元素没有索引且不能重复。

    哈希算法历史悠久,业界著名的哈希算法也有很多,比如 MD5、SHA。哈希算法是指将任意长度的二进制值串映射为固定长度的二进制值串,这个映射的规则就是哈希算法,而通过原始数据映射之后得到的二进制值串就是哈希值。有以下几个特点:
    从哈希值不能反向推导出原始数据(所以哈希算法也叫单向哈希算法或单向散列函数)。
    对输入数据非常敏感,哪怕原始数据只修改了一个 Bit,最后得到的哈希值也大不相同。
    散列冲突的概率要很小,对于不同的原始数据,哈希值相同的概率非常小。
    哈希算法的执行效率要尽量高效,针对较长的文本,也能快速地计算出哈希值。

    * NSCountedSet:可变,无序的不同对象集合,可能在集合中出现多次。(即使对象已多次添加到集合中,集合中中也只有一个对象实例);继承自NSMutableSet;
    * NSOrderedSet:静态的,有序的唯一对象集合,继承自NSObject。(创建后无法修改集合内容);
    * NSMutableOrderedSet:动态的,有序的独特对象集合,继承自NSOrderedSet。(创建后可修改集合内容);
    * NSIndexSet:是个无符号整数集合。集合中的元素不可变的、不可重复。常被用来当作索引使用。就从它字面上理解,就叫做:索引集合。
    

    22、NSPointerArray、NSHashTable、NSMapTable实现对象的弱引用。

    参考链接

    • NSPointerArray:NSPointerArray效仿了NSArray(NSMutableArray)

      • 和传统 Array 一样,用于有序的插入或移除;
      • 与传统 Array 不同的是,可以存储 NULL,并且 NULL 还参与 count 的计算;
      • 与传统 Array 不同的是,count 可以 set,如果直接 set count,那么会使用 NULL 占位;
      • 可以使用 weak 来修饰成员;
      • 成员可以是所有指针类型;
      • 遵循 NSFastEnumeration,可以通过 for...in 来进行遍历。
    • NSHashTable:NSHashTable效仿了NSSet(NSMutableSet),但提供了比NSSet更多的操作选项,尤其是在对弱引用关系的支持上,NSHashTable在对象/内存处理时更加的灵活。相较于NSSet,NSHashTable具有以下特性

      • 1.NSHashTable是可变的,它没有不可变版本。
      • 2.它可以持有元素的弱引用,而且在对象被销毁后能正确地将其移除。而这一点在NSSet是做不到的。
      • 3.它的成员可以在添加时被拷贝。
      • 4.它的成员可以使用指针来标识是否相等及做hash检测。
      • 5.它可以包含任意指针,其成员没有限制为对象。我们可以配置一个NSHashTable实例来操作任意的指针,而不仅仅是对象。
    • NSMapTable:NSMapTable是更广泛意义上的NSDictionary。和NSDictionary/NSMutableDictionary相比具有如下特性:

      • 1.NSDictionary/NSMutableDictionary会复制keys并且通过强引用values来实现存储;
      • 2.NSMapTable是可变的;
      • 3.NSMapTable可以通过弱引用来持有keys和values,所以当key或者value被deallocated的时候,所存储的实体也会被移除;
      • 4.NSMapTable可以在添加value的时候对value进行复制;

    23、NSDictionary & NSSet

    NSDictionary 的所有 key 放在一起其实就是一个 NSSet ,但是调用 allKeys 返回的是 NSArray,可见是被处理过了。

    24、自定义 NSDictionary 的 key

    • 重写 isEqual 和 hash 方法
    • 同时实现copyWithZone 方法

    25、NSPredicate 与 NSRegularExpression

    • Predicates表示一个可以用来过滤对象集合的条件。通常,我们可以从 NSComparisonPredicate, NSCompoundPredicate, 和 NSExpression的实例直接创建predicates,但是,我们也可以从格式化的string来创建predicates,这个格式化的string可以被NSPredicate的类方法解析,一般是正则表达式的string。通过 evaluateWithObject 系列的方法可以判断条件是否符合。

    • NSRegularExpression的基本匹配方法是一个Block迭代方法,可允许客户端提供一个Block对象,来当正则表达式匹配上目标字符的一部分时,该Block块被调用。还有其他一些方便的方法,可以返回把所有匹配结果的数组,匹配的总数,第一个匹配结果,第一个匹配的range。

    26、使用 NSProcessInfo 可以用于获取进程相关的信息。

    @interface NSProcessInfo : NSObject {
    @private
        NSDictionary    *environment;
        NSArray     *arguments;
        NSString        *hostName;
        NSString        *name;
        NSInteger       automaticTerminationOptOutCounter;
    }
    
    @property (class, readonly, strong) NSProcessInfo *processInfo;
    
    @property (readonly, copy) NSDictionary<NSString *, NSString *> *environment;
    @property (readonly, copy) NSArray<NSString *> *arguments;
    @property (readonly, copy) NSString *hostName;
    @property (copy) NSString *processName;
    @property (readonly) int processIdentifier;
    
    @property (readonly, copy) NSString *globallyUniqueString;
    

    27、对象归档:可以看做就是序列化。

    方法一:普通方式实现:
    - (void)encodeWithCoder:(NSCoder *)coder
    {
        //告诉系统归档的属性是哪些
        [coder encodeObject:self.name forKey:@"name"];
        [coder encodeInteger:self.age forKey:@"age"];
    }
    
    - (instancetype)initWithCoder:(NSCoder *)coder
    {
        self = [super init];
        if (self) {
            //解档
            self.name = [coder decodeObjectForKey:@"name"];
            self.age = [coder decodeIntegerForKey:@"age"];
        }
        return self;
    }
    
    方法二:使用runtime方法实现:
    - (void)encodeWithCoder:(NSCoder *)coder
    {
        //告诉系统归档的属性是哪些
        unsigned int count = 0;//表示对象的属性个数
        Ivar *ivars = class_copyIvarList([Person class], &count);
        for (int i = 0; i<count; i++) {
            //拿到Ivar
            Ivar ivar = ivars[i];
            const char *name = ivar_getName(ivar);//获取到属性的C字符串名称
            NSString *key = [NSString stringWithUTF8String:name];//转成对应的OC名称
            //归档 -- 利用KVC
            [coder encodeObject:[self valueForKey:key] forKey:key];
        }
        free(ivars);//在OC中使用了Copy、Creat、New类型的函数,需要释放指针!!(注:ARC管不了C函数)
    }
    
    - (instancetype)initWithCoder:(NSCoder *)coder
    {
        self = [super init];
        if (self) {
            //解档
            unsigned int count = 0;
            Ivar *ivars = class_copyIvarList([Person class], &count);
            for (int i = 0; i<count; i++) {
                //拿到Ivar
                Ivar ivar = ivars[i];
                const char *name = ivar_getName(ivar);
                NSString *key = [NSString stringWithUTF8String:name];
                //解档
                id value = [coder decodeObjectForKey:key];
                // 利用KVC赋值
                [self setValue:value forKey:key];
            }
            free(ivars);
        }
        return self;
    }
    注:使用runtime的好处不言而喻,无论对象有多少属性都可以通过这个for循环搞定,非常棒。
    
    

    28、最初的 MVC 模式是针对相同的数据需要不同显示的应用而设计的, MVC 思想非常类似于一个观察者模式,但手写 UI 代码其实是有点违背 MVC 思想的。

    29、自定义 UI 控件,可以通过重写 UIView 的一系列方法实现。

    • 新建一个继承UIView的类
    • 在刚刚新建类的类扩展中添加子控件属性(用weak声明,防止内存泄露)
    • 在initWithFrame:方法中添加子控件
    • 在layoutSubviews方法中设置子控件的frame(在该方法中一定要调用[super layoutSubviews]方法)
    • 提供一个模型属性,重写模型属性的set方法
    • 在该setter方法中取出模型属性,给对应的子控件赋值

    30、UI 控件分为:

    • 活动控件:继承与 UIControl 可以与用户进行交互、
    • 静态控件:只继承于 UIView 只显示不交互、被动控件:文本框。 (UIControl 继承于 UIView )

    31、拖动条 UISlider

    这个值是介于滑块的最大值和最小值之间的,如果没有设置边界值,默认为0-1;
    @property(nonatomic) float value; 
    
    设置滑块最小边界值(默认为0)
    @property(nonatomic) float minimumValue;  
    
    设置滑块最大边界值(默认为1)
    @property(nonatomic) float maximumValue;
    
    设置滑块最左端显示的图片:
    @property(nonatomic,retain) UIImage *minimumValueImage;
    
    设置滑块最右端显示的图片:
    @property(nonatomic,retain) UIImage *maximumValueImage;
    
    设置滑块值是否连续变化(默认为YES),这个属性设置为YES则在滑动时,其value就会随时变化,设置为NO,则当滑动结束时,value才会改变。
    @property(nonatomic,getter=isContinuous) BOOL continuous; 
    
    设置滑块左边(小于部分)线条的颜色
    @property(nonatomic,retain) UIColor *minimumTrackTintColor;
    
    设置滑块右边(大于部分)线条的颜色
    @property(nonatomic,retain) UIColor *maximumTrackTintColor;
    
    设置滑块颜色(影响已划过一端的颜色)
    注意这个属性:如果你没有设置滑块的图片,那个这个属性将只会改变已划过一段线条的颜色,不会改变滑块的颜色,如果你设置了滑块的图片,又设置了这个属性,那么滑块的图片将不显示,滑块的颜色会改变(IOS7)
    @property(nonatomic,retain) UIColor *thumbTintColor;
    
    手动设置滑块的值:
    - (void)setValue:(float)value animated:(BOOL)animated;
    
    设置滑块的图片:
    - (void)setThumbImage:(UIImage *)image forState:(UIControlState)state;
    
    设置滑块划过部分的线条图案
    - (void)setMinimumTrackImage:(UIImage *)image forState:(UIControlState)state;
    
    设置滑块未划过部分的线条图案
    - (void)setMaximumTrackImage:(UIImage *)image forState:(UIControlState)state;
    
    对应的几个get方法
    - (UIImage *)thumbImageForState:(UIControlState)state;
    - (UIImage *)minimumTrackImageForState:(UIControlState)state;
    - (UIImage *)maximumTrackImageForState:(UIControlState)state;
    
    对应的设置当前状态的响应属性的方法
    @property(nonatomic,readonly) UIImage* currentThumbImage;
    @property(nonatomic,readonly) UIImage* currentMinimumTrackImage;
    @property(nonatomic,readonly) UIImage* currentMaximumTrackImage;
    
    添加触发事件
    [slider addTarget:self action:@selector(log:) forControlEvents:UIControlEventValueChanged];
    

    32、微调器 UIStepper

    如果我们的需求是点住 "+" 或者 "-"按钮,数量就一直递增递减的话, btn-label-btn的方式来做可能就不太好用了,使用 UIStepper 可以很方便的实现这个功能,只是 UI 可能需要自定义一下。
    

    33、应用启动过程,application 各个方法调用顺序

    参考链接

    34、程序被中断时(电话进来)应该做什么处理

    停止timer 和其他周期性的任务
    停止任何正在运行的请求
    暂停视频的播放
    如果是游戏那就暂停它
    减少OpenGL ES的帧率
    挂起任何分发的队列和不重要的操作队列(你可以继续处理网络请求或其他时间敏感的后台任务)。
    当程序回到active状态 ,   applicationDidBecomeActive:   方法应该上面提到的任务重新开始,比如重新开始timer, 继续分发队列,提高OpenGL ES的帧率。不过游戏要回到暂停状态,不能自动开始。
    

    35、Quartz 2D 绘图基础:CGContextRef, 同时也可以用于绘制文本

    参考链接

    Quartz2D 苹果封装的一套绘图的函数库,同时支持iOS和Mac. UIKit框架,里面有各种各样的UI控件,其实大部分控件的内容都是通过Quartz2D画出来的
    Quartz2D能做什么?

    绘制图形 : 线条、三角形、矩形、圆、弧等
    绘制文字
    绘制、生成图片(图像)
    读取、生成PDF
    截图、裁剪图片
    自定义UI控件
    涂鸦、画板
    手势解锁
    .........
    iOS中常用的是截屏、裁剪、自定义UI控件
    

    与直接在UIView控件上绘图不同,在内存中绘图时,需要开发者自己准备绘图环境,Quartz 2D提供了一个非常便捷的函数:UIGraphicsBeginImageContext(CGSize size),该函数用于准备绘图环境。当图形绘制完成后,可调用UIGraphicsEndImageContext()函数结束绘图和关闭绘图环境。不需要在drawRect 方法中了,可以提高性能。

    总结来说,在内存中绘图的步骤如下:

    • 调用UIGraphicsBeginImageContext(CGSize size)函数准备绘图环境。

    • 调用UIGraphicsGetCurrentContext()函数获取绘图CGContextRef。

    • 用前面介绍的绘制集合图形、使用路径等方式进行绘图。

    • 调用UIGraphicsGetImageFromCurrentImageContext()函数获取当前绘制的图形,该方法返回一个UIImage对象。

    • 调用UIGraphicsEndImageContext()函数结束绘图,并关闭绘图环境。

    36、Core Image 滤镜,可以创建基于 CPU 、GPU 、OpenGL 的 CIContext 对象,用于处理图片。

    参考链接

    1.框架介绍
    (1)CoreImage
    (2)是一个图片框架
    它基于OpenGL顶层创建
    底层则用着色器来处理图像
    (3)他利用了GPU基于硬件加速来处理图像
    (4)CoreImage中有很多滤镜
    (5)它们能够一次给予一张图像或者视频帧多种视觉效果 -> 滤镜链
    (6)而且滤镜可以连接起来组成一个滤镜链 把滤镜效果叠加起来处理图像
    2.类的介绍
    1.CIImage 保存图像数据的类
    CGImageRef->图像中的数据
    2.CIFilter  滤镜类
    图片属性进行细节处理的类
    它对所有的像素进行操作  用键-值(KVC)来设置
    3.CIContext   上下文是实现对图像处理的具体对象  用来把滤镜和图片合成成为一张图片     滤镜对象输出的图像并不是合成之后的图像,需要使用图片处理的上下文     合并输出图像
    

    37、Quartz Corec 可以用于控制动画。

    参考链接

    iOS设备给用户视觉反馈其实都是通过QuartzCore框架来进行的,说白了,所有用户最终看到的显示界面都是图层合成的结果,而图层即是QuartzCore中的CALayer。

    通常我们所说的视图即UIView,并不是直接显示在屏幕上,而是在创建视图对象的时候视图对象会自动创建一个层,而视图对象把要显示的东西绘制在层上,待到需要显示时硬件将所有的层拷贝,然后按Z轴的高低合成最终的显示结果。

    CALayer本质上是一块包含一幅位图的缓冲区,由视图创建的层为隐式层,而手动创建的层称为显示层。

    如果要在iOS上能够有良好的用户体验,动画的过渡效果是必不可少的,而所有的动画效果都是通过CAAnimation类的子类(CAAnimation是抽象类)来完成的。CAAnimation类的子类包括了CAAnimationGroup,CAPropertyAnimation,CATransition,而CAPropertyAniamtion(同为抽象类)也衍生了CABasicAnimation和CAKeyframeAnimation。用UIView的animation实现的动画本质上也是通过CALayer来实现的,iOS系统中CALayer的很多属性都是隐含有动画效果的,如果不想要隐式动画或者想要显示动画效果,都可以通过CATransaction来设置是否显示动画效果。同时,在CATransaction内可同时修改多个属性,然后再一并同时渲染,另外CATransaction还是可嵌套的。

    参考链接

    相关文章

      网友评论

        本文标题:iOS 疯狂讲义(上)整理

        本文链接:https://www.haomeiwen.com/subject/qbrmsctx.html