《iOS开发进阶》阅读总结

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/Ginhoor/article/details/68924860
1.引用计数的作用
对象A向对象B传递参数对象M ,对象M可能成为对象B的成员变量,也可能只是临时使用,如果不用引用计数控制,则无法在正确的时间释放对象M。

案例一:对象A将对象M传递给对象B后,对象B复制一份对象M的拷贝M1,然后对象A释放对象M,这样由对象B继续维护对象M1的生命周期。
缺点:会带来更多的内存申请、复制、释放的工作,影响性能。

案例二:对象A将对象M传递给对象B后,对象A不再维护对象M的生命周期,由对象B来负责维护对象M的生命周期。
缺点:对象A与对象B的耦合度很高,需要代码维护者明确记住这种编程约定,而且由于对象M在对象A中申请,在对象B中释放,则对象M的内存管理代码分散在不同对象中,管理困难,如果此时对象B要向对象C传递对象M,则复杂度进一步提高,影响代码结构。

而使用引用计数就能规避这个问题,所有对象都遵循这个原则,则对象的生命周期管理可以完全交给引用计数。

引用计数存在一个瑕疵,就是可能出现循环引用,所以需要引入弱引用概念。


2.ARC的使用
Core Foundation 对象不在ARC管理下,需要自己维护对象的引用计数。
与CF对象交互是,需要用到CFRetain(obj),CFRelease(obj),可以直观的认为与OC的retain、release等价
在ARC环境下,将一个CF对象转换成OC对象,需要用到bridge相关关键字:
__bridge:只做类型转换,不修改相关对象的引用计数,原有的CF对象在不用时,需要调用CFRelease方法。
__bridge_retained:类型转换后,将相关对象的引用计数加一,原来的CF对象不用时,需要调用CFRelease方法。
__bridge_transfer:类型转换后,将该对象的引用计数交给ARC管理,CF对象不用时,不需要调用CFRelease方法。

3.GCD(Grand Central Dispatch)的使用
dispatch_queue_t 需要手动管理释放
dispatch_queue_t urls_queue = dispatch_queue_create("com.ginhoor.url",NULL);
dispatch_release(urls_queue);
UIApplication BackgroundTask可以使应用在进入后台后,最多10分钟的时间在后台长久运行。

Block的特点
1.程序块可以在代码中以内联的方式定义。
2.程序块可以访问创建它的范围内的可用变量。

Block的特性
默认情况下,block中外部变量是复制进去的,即写操作不对原变量生效,如果是引用类型,block会将其引用计数+1,加上__block可以使其写操作对原变量生效。

UIWindow的主要作用
从继承关系上,UIWindow继承自UIView,所以UIWindos除了具有UIView的所有功能外,还增加了一些特有的属性和方法。常用的就是UIWindow的makeKeyAndVisible方法使整个程序界面可见。
1.UIWindow是最顶层的界面容器,默认不包含任何内容,包含应用显示所需要的所有UIView。
2.传递触摸消息和键盘事件给UIView。

UIWindow的特性
1.与创建UIview不同,UIWindow一旦被创建,它就自动被添加到整个界面上。
2.如果创建的UIWindow需要处理键盘事件,那就需要合理的将其设置为keyWindow。我们可以通过makeKeyWindow和resignKeyWindow的方法将自己创建的UIWindow实例设置成keyWindow。

UIWebView
可以使用模版文件渲染HTML,例如MGTemplateEngine、GRMustache
OC与JavaScript相互调用:
UIWebView调用原生界面为异步调用,原生界面使用stringByEvaluatingJavaScriptFromString为同步调用。同步调用会阻塞原有javaScript代码的执行,所以用JavaScript端用iFrame的插入延后之行。另外stringByEvaluatingJavaScriptFromString必须在主线程上执行,而主线程的执行时间过长会阻塞UI的更新,所以要尽量缩短执行时间。
无法通过[obj becomeFirstResponder]让光标输入焦点转移到UIWebView上。

CoreText
CoreText是用于处理文字和字体的底层计数,它直接和Core Graphics(又称为Quartz)打交道,具有高效的排版功能。
Quartz是一个2D图形渲染引擎,能够处理OSX和iOS中的图形显示问题。
CoreText处于非常底层,上层的UI控件(包括UILabel、UITextFiled、UITextView)和UIWebView都是基于CoreText来实现的。

CoreText与UIWebView对富文本的处理
1.CoreText占用的内存更少,渲染速度更快,UIWebView占用内存多,渲染速度慢。
2.CoreText在渲染界面前就可以精确地获得显示内容的高度(只要有了CTFrame即可),而UIWebView 只有在渲染出内容后,才能获得控件高度(而且还需要用JavaScript代码来获取)。
3.CoreText的CTFrame可以在后台线程渲染,UIWebView的内容只能在主线程渲染。
4.基于CoreText可以做更好的原生交互效果,而UIWebView的交互效果都是用JavaScript来实现的,在交互效果上会有一些卡顿存在。

CoreText的劣势
1.CoreText渲染出来的内容不能像UIWebView那样方便的支持内容复制。
2.基于CoreText的排版需要自己处理很多复杂逻辑,例如需要自己处理图片和文字的混排相关逻辑,也需要自己实现链接点击操作的支持。

开发技巧
用CoreGraphics可以实现截屏

为什么ViewDidUnload被废弃
在iOS4、5中,内存不足,收到MemoryWarning时,系统会调用没在当前界面上的ViewController的viewDidUnload方法。
在iOS6中UIView有一个CALayer的成员变量,CALayer是具体用于将自己画到屏幕上。CALayer是一个bitmap图像的容器,当UIView调用自身的drawRect时,CALayer才创建这个bitmap图像类。具体占用内存的其实是一个bitmap图像类,CALayer只占48Bytes,UIView只占96Bytes。而一个iPad的全屏UIView的bitmap类会占到12MB的大小。在iOS6中,当系统发出MemoryWarning时,系统会自动回收bitmap类,但不回收UIView和CALayer类。这样既能回收大部分内存,又能在需要bitmap类的时候,通过调用UIVIew的drawRect:方法重建。

isa 对象指针
对象的方法都保存在类的可变区域内,在OC2.0中并未在头文件中将实现暴露出来,在OC1.0中,方法的定义列表是一个methodLists的指针的指针,通过修改该指针的指向指针的值,就可以动态的胃某一个类增加成员方法,也就是category的实现原理。同时也说明了category只能为对象增加方法,却不能增加成员变量(通过objc_setAssociatedObject 和objc_getAssociatedObject 可以变相添加成员变量,但由于实现机制不同,并不是真正改变了内存结构)。

Method Swizzling API 说明
class_replaceMethod 替换类方法的定义。
有两种不同的行为:当类没有想替换的方法时,该方法会调用class_addMethod 来为该类增加一个新方法。如果存在想替换的方法,则直接替换,所以需要传入types参数。

method_exchangeImplementations 交换两个方法的实现。
内部实现其实就是调用了两次method_setImplementation

method_setImplementation 设置一个方法的实现。

Tagged Pointer
如果存储一个NSNumber对象,正常情况下就是一个NSInteger的普通变量,那么32位CPU下占4个字节,64位CPU下占8个字节,而一个指针在32位CPU下为32/8 = 4个字节 ,在64位CPU下为8个字节。
一个普通的iOS程序,如果没有Tagged Pointer对象,从32位机器迁移到64位机器中,虽然逻辑没有变化,但是NSNumber、NSDate一类的对象占用内存会翻倍。
效率上,为了存储和访问一个NSNumber对象,我们需要在堆上为其分配内存,另外还要维护它的引用计数,管理它的生命周期。这些都给程序增加了额外的逻辑,造成运行效率上的损失。

所以在64位CPU下,NSNumber的指针被拆成两个部分,一部分直接保存数据,另一部分作为特殊标记,表示这是一个指针,不指向任何一个地址。当8个字节可以承载用于表示的数值时,系统就会以Tagged Pointer的方式生成指针,将值直接存储到了指针里,如果8字节承载不了时,又会用以前的方式来生成普通的指针。

特点:
1.Tagged Pointer 专门用于存储小的对象,例如NSNumber和NSDate。
2.Tagged Pointer 指针的值不再是地址了,而是真正的值。所以,实际上它不再是一个对象了,它只是一个披着对象“皮”的普通变量而已。所以它的内存不存储在堆中,也不需要malloc和free。
3.内存读取上有着以前3倍的效率,创建比以前快106倍

注意事项:
Tagged Pointer并不是真正的对象,它并没有isa指针,不能直接访问Tagged Pointer的isa成员(obj->isa),需用isKindOfClass和object_getClass代替。

64位下的isa指针优化
在32位环境下,对象的引用计数都保存在一个外部的hash表中,每一个对象的Retain操作,实际上包括了如下5个步骤:
1.获得全局的记录引用计数的hash表。
2.为了线程安全,给该hash表加锁。
3.查找到目标对象的引用计数值。
4.将该引用计数值+1,写回hash表。
5.给该hash表解锁。

在64位环境下,isa指针也是64位,实际作为指针部分只用到了其中33位,剩余31位苹果使用了类似Tagged Pointer 的概念,其中19位
将保存对象的引用计数,这样引用计数的操作只需要修改这个指针就可以了。只有当引用计数超过19位,才会将引用计数存到外部表,而这个情况很少,所以这样引用计数的效率会更高。
与前面5个步骤对应,在64位环境下,新的Retain操作包括如下5个步骤:
1.检查isa指针上面的标记为,看引用计数是否保存在isa变量中,如果不是,则是用以前的步骤,否则,执行第2步。
2.检查当前对象是否正在释放,如果是,则不做任何事情。
3.增加该对象的引用计数,但时并不是马上写会isa变量中。
4.检查增加后的引用计数的值是否能够被19位表示,如果不是,则切换成以前的办法,否则执行第5步。
5.进行一个原子的写操作,将isa的值写会。

虽然步骤都是5步,但是由于没有了全局加锁的操作,所以引用计数的更改更快了。


isa的bit位含义
从低位到高位
bit位:1bit,变量名:indexed,意义:0表示普通isa,1表示Tagged Pointer。
bit位:1bit,变量名:has_assoc,意义:表示该对象是否有过associated对象,如果没有,在析构释放内存可以更快。
bit位:1bit,变量名:has_cxx_dtor,意义:表示该对象是否有C++或ARC的析构函数,如果没有,在析构释放内存时可以更快。
bit位:30bits,变量名:shiftcls,意义:类指针。
bit位:9bits,变量名:magic,意义:其值固定位0xd2,用于调试时分辨对象是否未完成初始化。
bit位:1bit,变量名:wealky_referenced,意义:表示该对象是否指向过weak对象,如果没有,在析构释放内存时后可以更快。
bit位:1bit,变量名:deallocating,意义:表示该对象是否正在析构。
bit位:1bit,变量名:has_sidetable_rc,意义:表示该对象的引用计数是否达到无法直接在isa中保存。
bit位:19bits,变量名:extra_rc,意义:表示该对象超过1的引用计数值,例如该对象的引用计数是6,则extra_rc的值为5。

Block的内部数据结构定义
1.isa指针,所有对象都有该指针,用于实现对象相关功能。
2.flags,用于按bit位表示一些block的附加信息。
3.reserved,保留变量。
4.invoke,函数指针,指向具体的block实现的函数调用地址。
5.descriptor,表示该block的附加描述信息,主要是size大小,以及copy和dispose函数的指针。
6.variables,capture过来的变量,block能够访问它外部的局部变量,就是因为将这些变量(或变量地址)复制到了结构体重。

在Objective-C语言中,一共有3种类型的block:
1._NSConcreteGlobalBlock,全局的静态block,不会访问任何外部变量。
当block没有传入外部参数的时候就会标记为 NSGlobalBlock
2._NSConcreteStackBlock,保存在栈中的block,当函数返回时会被销毁。
在MRC环境下,默认位StackBlock,在ARC环境中被 NSMallocBlock替代。
3._NSConcreteMallocBlock,保存在堆中的block,当引用计数为0是会被销毁。
所以结论是,在ARC环境下,Block都好使,因为使用的是NSMallocBlock,在MRC环境先爱,最好在任何时候都使用 block = [[block copy] autorelease]。
So, what's the point of all this? The point is  always use ARC . With ARC, blocks pretty much always work correctly. If you're not using ARC, you better defensively  block = [[block copy] autorelease]  any time a block outlives the stack frame where it is declared. That will force it to be copied to the heap as an  NSMallocBlock


猜你喜欢

转载自blog.csdn.net/Ginhoor/article/details/68924860