阅读 40

iOS 开发中的一些简单总结

ObjC

属性

  • @property 的本质是生成setter方法和getter方法以及一个带下划线的成员变量,通过autosynthesis添加到类中的。

  • 通常的关键字为:

    • nonatomic & atomic

      这是代表原子性 但是atomic不能完全保证线程安全 它只保证了setter和getter方法的线程安全,具体的情况:当A线程进行写操作的时候,B线程会等待。当A线程写完,B线程进行写操作,然后A线程读取的将是B线程中的值,如果C线程在A线程读写前将对象release了还会造成程序崩溃

    • strong & retain

      strong相当于ARC下的retain 会强引用对象 使得对象的引用计数加一

    • weak & assign

      assign多被用来修饰基本数据类型,是指针赋值,不会对引用计数操作,使用之后不会自动置nil,可能会导致野指针。A对象的指针赋值给B,此时B只是保存了A的指针,不会持有A,所以当A被释放的时候再访问B可能会出现野指针错误。

      weak表示弱引用,它既不会设置新值,也不会保存旧值。不会对引用计数操作,当指向的对象被释放之后,自身也会被置nil

    • copy

      copy是对对象的引用计数加一,它对应的setter方法不会保存新值,而是将其拷贝一份,通常NSString以及具有可变类型的集合类型可以使用copy修饰,拷贝一份出来,以保证不会再使用的过程中被其他地方的操作所改变

      block也使用copy来修饰,这是MRC遗留下的传统,提示开发者block是从栈上复制到堆上的。

    • readwrite&readonly

      读写,只读

    • setter= & getter=

      指定setter方法和getter方法

    • 默认

      基本数据类型默认是assign, atomic,readwirte

      对象类型strong,atomic, readwrite

@synthesize 自动生成setter getter方法

@dynamic不需要编译器生成,手动生成

默认是@synthesize var = _var

对象copy

  • 拷贝的本质是生成一份独立的副本

  • 对不可变对象来说 copy是浅拷贝,因为该对象本身就是不可变的了。而mutablecopy是深拷贝因为需要拷贝出来一份可变的对象,所以需要进行深拷贝,拷贝内存中的内容。

  • 对可变对象来说,copy和mutable拷贝都是深拷贝,copy生成的对象是不可变的,mutablecopy会生成可变对象

copy & mutableCopy

  • 自定义对象想要进行copy操作需要实现NSCopying协议

    • 实现copyWithZone或者mutableCopyWithZone方法

      - (id)copyWithZone:(NSZone *)zone {
          Person *p = [[Person allocWithZone:zone] init];
          //属性也要拷贝赋值
          p.name = [self.name mutableCopy];
          p.age = self.age;
          return p;
      }
      复制代码
    • 如果Person中有其他自定义对象,那么该自定义对象也需要实现遵循并实现 NSCopying协议。

类和对象

  • NSObject 实例本质是一个结构体
  • NSObect对象的大小是一个C语言结构体的大小
  • 实例对象的isa指针指向类对象,类对象的isa指向元类对象。
  • 每个类中只有一个类对象,类对象中存放了对象的方法列表,协议列表等本质是一个object_class结构体
  • 每一个类中只有一个元类对象,元类对象的结构跟类对象是一样只不过用途不一样。可以通过 runtime 的 class_isMetaClass 来验证某个类是不是元类,其本质是一个 objc_class 的结构体。
  • 有继承关系的对象的结构体中存放着其父类的结构体

+ initialize 与 +load

  • 调用时机
    • + initialize第一次初始化这个类之前调用,我们常用来初始化静态变量,是runtime负责的。
    • +load 方法会在加载类的时候就被调用,也就是 ios 应用启动的时候,就会加载所有的类,就会调用每个类的 + load 方法。load通过指针直接找到函数地址直接调用没有通过消息机制
  • 大量使用load会导致App启动速度变慢

Category

可以使用Category为类添加新方法,使用Category为类添加新方法在一定程度上比使用继承添加方法的释放耦合度低。

category是不支持直接使用属性的,可以通过runtime的属性绑定来实现。声明一个属性,实现setter和getter方法。在setter方法中使用objc_setAssociatedObject第二个参数标记可以用``@selector([getter method name]),getter方法中使用objc_getAssociatedObject获取属性值,第二个参数使用_cmd`

category 中实现的方法是在运行时添加到类中的。category是按照编译顺序排列的,所以后编译的cat中取出的方法、属性和协议列表,分别放在mlist、proplists和protolists的最前面。

Block

block的本质是封装了函数调用和函数调用环境的OC对象

  1. Block对变量的捕获:

    • auto变量 值捕获
    • static 引用捕获
    • 全局变量不会捕获
  2. block类型

    MRC下 当block访问了auto类型变量的时候是stackblock 其他情况下是globalblock

    ARC下会将stackblock从栈上copy到堆上,当然这是编译器帮我们做的

  3. ARC下的block会在一下情况自动调用copy方法

    • block作为返回值的时候
    • 当block被强指针引用的时候
    • cocoa api中 作为usingBlock 或者GCD的参数的时候
  4. 对象类型的捕获

    • 会根据对象的修饰词对变量进行retain或者弱引用,当block从堆中移除的时候对应的dispose方法释放对象
  5. __weak

    ARC下通常使用__weak来解决block的循环引用问题,使用它修饰的对象在被block捕获的时候,在block内部也会使用__weak来修饰,而使用__weak修饰的对象会被弱引用。

  6. __block

    在block内部修改auto变量的时候通常会报错,这个时候需要使用__block修饰。__block修饰的变量,在block内部会生成一个结构体,结构体中存放有isa指针__forwarding指针,变量等信息,当改变变量的值得时候会通过__forwarding指针找到这个结构体,改变其中变量的值。

KVO KVC

  1. KVO是键值监听,当对一个对象进行KVO的时候,runtime会创建一个新的对象作为之前对象的子类,这个子类中重写了变量的setter方法,class方法和dealloc方法,调用Foundation中的_NSSetIntValueAndNotify函数,它会在赋值之前调用willChangeValueForKey,之后调用didChangeValueForKey来监听值得改变。
  2. KVC是键值编码
    • 设置值的时候会按照setKey,_setKey的顺序查找setter方法,如果找到则直接调用设置值,找不到则查看+ (BOOL)accessInstanceVariablesDirectly方法的返回值,如果返回值是NO则调用setValue:forUndefinedKey方法,如果没有实现会抛出异常。如果是YES,则会按照_key,_isKey,key,isKey的顺序查找成员变量赋值
    • 获取值的时候会按照getKeykeyisKey_key的顺序查找对应的方法,如果找到则展开调用,如果找不到那么查看+ (BOOL)accessInstanceVariablesDirectly返回值,YES的话会按照_key,_isKey,key,isKey的顺序查找成员变量,返回NO的话就会调用- (id)valueForUndefinedKey:(NSString *)key,如果没有实现会抛出异常
    • + (BOOL)accessInstanceVariablesDirectly默认为YES

内存

iOS 使用引用计数的方式管理内存,内存管理的原则是谁创建,谁释放 。 谁引用,谁管理

使用new、alloc、copy或者mutable copy,引用计数为初始值1,再使用retain的话,引用计数为+1,使用release或者autorelease会使引用计数-1。

自动释放池,OC对象调用autorelase方法之后,会将对象放到离自己最近的自动释放池内,Run Loop在每个事件循环结束后会去自动释放池,会将释放池内所有的对象都做一次release操作。

MRC下的setter方法

先释放旧值再引用新值

- (void)setAge:(NSString *)age {
		if (_age != age) {
      	[_age release];
      	[age retain];
        _age = age;
		}
}
复制代码

循环引用

  • Block
  • NSTimer
  • self 加到array中
  • delegate

多线程

iOS的多线程方案:

  1. GCD :GCD中有四种队列:主队列、串行队列、并行队列、全局队列。全局队列是一种特殊的并行队列,它没有标识。同步只能在当前线程中执行,不会开辟新线程,且都是串行执行任务。异步线程在主队列中不会创建线程,在并行队列中会开启新线程并行执行任务,串行队列会创建线程串行执行任务。
  2. 线程间通信可以使用performSelector:onThread...
  3. 队列组
  4. NSOperation 是GCD的OC封装,它是一个抽象类,不能直接使用,需要使用其子类。NSOperation可以提供 OC Api操作线程,还可以指定依赖关系,并且可以使用KVO监听任务的执行情况
  5. 信号量
    • OSSpinLock 自旋锁,会处于忙等的状态
      • OSSpinLock & OSSpinUnLock
    • os_unfair_lock 用来代替OSSpinLock 属于互斥锁会进入休眠状态
      • os_unfair_lock_lock & os_unfair_lock_unlock
    • pthread_mutex 属于pthread的api
      • 递归锁 & 条件锁
    • NSLock 对 pthread_mutex的封装
    • @synchronized 对mutex递归锁的封装
    • dispatch_barrier_async 要求线程必须是dispatch_queue_cretate创建的,读写操作的时候可以使用栅栏函数隔离写操作。

runtime

  • 更换系统字体
  • 为category添加属性
  • 字典转模型
  • NSCoding协议

objcMsgSend

首先会在方法缓存中查找方法的实现,如果没有找到则查找父类的实现,父类没有实现会进入动态方法解析,为该类添加一个方法的实现。如果动态方法解析还没有实现,那么会进入消息转发,将消息转发给一个可以相应该方法的target。如果这一步还没有实现则会进行方法签名,返回一个方法签名,并调用forwardInvacation返回一个target处理该消息,如果还没有实现则会报方法找不到错误。

runloop

  1. 首先会通知监听者Observers:即将处理Timers

  2. 通知监听者Observers:即将处理Sources

  3. 处理blocks

  4. 处理source0,如果处理完了会再次处理blocks

  5. 如果存在source1,则跳到handle_msg处理,如果没有则通知监听器即将进入休眠

  6. 休眠时期等待消息来唤醒当前线程

  7. 如果有消息唤醒则进入handle_msg处理计时器,gcd,source1这些信息

  8. 再次处理blocks

  9. 获取返回值retVal

  10. 进入do-while(),如果retVal == 0 则循环持续进行。否则返回给CFRunLoopRunSpecific函数,退出RunLoop

UI

UITableView

  1. 优化
    1. 尽量不要使用透明的颜色
    2. 不要动态的添加控件
    3. 注意重用id
    4. 在model中缓存高度
    5. 尽量一次将控件添加到cell上使用hidden来控制显隐
    6. 圆角处理避免离屏渲染
  2. 重用机制
    1. 首先会按照屏幕显示创建cell,存放在visiableCells数组中
    2. 滑动出屏幕的cell会放到reuseableCells的数组中,从visiableCells中移除
    3. 重用的时候回检查reuseableCells中是否有相同id的cell
  3. reload
    1. reloadData:猜测是将visiableCells中所有cell移入reusableTableCellsvisiableCells清空。cellForRowAtIndexPath调用后,再把reuse的cell从reusableTableCells取出来,放入到visiableCells
    2. reloadRowsAtIndex: 如果调用时reusableTableCells为空,那么cellForRowAtIndexPath调用后,是新创建cell,新的cell加入到visiableCells。老的cell移出visiableCells,加入到reusableTableCells

响应者链

iOS响应者链彻底掌握

其他

HTTP&HTTPS

  1. client向server发送请求https://baidu.com,然后连接到server的443端口。

  2. 服务端必须要有一套数字证书,可以自己制作,也可以向组织申请。区别就是自己颁发的证书需要客户端验证通过,才可以继续访问,而使用受信任的公司申请的证书则不会弹出提示页面,这套证书其实就是一对公钥和私钥。

  3. 传送证书 这个证书其实就是公钥,只是包含了很多信息,如证书的颁发机构,过期时间、服务端的公钥,第三方证书认证机构(CA)的签名,服务端的域名信息等内容。

  4. 客户端解析证书 这部分工作是由客户端的TLS来完成的,首先会验证公钥是否有效,比如颁发机构,过期时间等等,如果发现异常,则会弹出一个警告框,提示证书存在问题。如果证书没有问题,那么就生成一个随即值(秘钥)。然后用证书对该随机值进行加密。

  5. 传送加密信息 这部分传送的是用证书加密后的秘钥,目的就是让服务端得到这个秘钥,以后客户端和服务端的通信就可以通过这个随机值来进行加密解密了。

  6. 服务段加密信息 服务端用私钥解密秘密秘钥,得到了客户端传过来的私钥,然后把内容通过该值进行对称加密。

  7. 传输加密后的信息 这部分信息是服务端用私钥加密后的信息,可以在客户端被还原。

  8. 客户端解密信息

    客户端用之前生成的私钥解密服务端传过来的信息,于是获取了解密后的内容。

上述文字出自HTTP和HTTPS协议,看一篇就够了

源码

关注下面的标签,发现更多相似文章
评论