我的iOS开发之旅

你若安好,便是晴天☀️

OC内存管理

1、RunTime(运行时)概念理解

(1)函数调用方式

      OC的函数调用即通过在对象上调用方法,也称之为“传递消息”,相对于C语言函数调用,使用静态绑定,即在编译期就决定运行时所应调用的函数,OC则是通过动态绑定的方式,对所要调用的函数在运行期才能确定,即对象收到消息之后,究竟该调用哪个方法完全由运行期决定,并且可以在程序运行时改变,是一门真正的动态语言。

(2)函数调用(传递消息)过程 1

       函数调用过程即给对象发送消息的处理过程,以id returnValue = [someObject measageName:pameter] 消息为例:
       编译期,编译器看到这行代码,会将其转换为一条标准的C语言函数调用,id returnValue = objc_msgSend(someObject, @selector(messageName:),  parameter);  如果在编译期向类发送了其无法解读的消息也不会报错,因为运行时可以继续向类中添加方法。

       运行期,即动态消息派发系统执行过程,objc_msgSend函数会首先根据接收者(someObject)和选择子(measageName)的类型在接收者所属的类中搜寻它的方法列表,这个方法列表其实是类里面的一张表格,表格是一个存放健值对的列表,健用选择子的名字表示,值即选择子对应方法的函数指针,因为OC对象的每个方法都可以视为如下简单的C函数,<return_type> Class_selector(id self, SEL _cmd, ...),这也是为什么后面找到相应匹配的方法后,能够跳转到相应实现代码的原因。
        如果在方法列表中找到与选择子名称相符的方法,就跳转到相应方法的实现代码,如果找不到,就沿着继承体系继续向上查找,若找到合适的方法就会再跳转到该方法实现代码,如果最终还是没找到相符合的代码,执行消息转发。即后面将要介绍的消息转发过程。《在这个过程中,objc_msgSend会将匹配结果缓存到快速映射表,这样后面执行相同的查找速度更快,尽管没有静态绑定的函数调用那么迅速,但是也不会慢很多了》
       下面的几个函数,与objc_msgSend函数类似,它们主要用于处理一些边界情况,可以了解一下
        objc_msgSend_stret 待发送的消息要返回结构体   objc_msgSend_fpret 待发送的消息要返回浮点数  objc_msgSendSuper待发送的消息要给超类发消息。

(3)函数调用(消息转发)过程 2

      经过上面传递消息过程还是没有找到匹配的方法之后,就会启动下面的消息转发过程。消息转发又分为动态方法解析、备援接受者、完整的消息转发三个步骤。
     动态方法解析:类的接受者,看是否能动态添加方法,以处理当前无法解读的选择子,首先要在所属类中重写下面的类方法,+(BOOL) resolveInstanceMethod ( SEL ),前面是为了处理未知的实例方法,如果是处理未知的类方法,重写 +(BOOL) resolveClassMethod ( SEL ),selector 参数,即未知的选择子,在这里有机会新增处理此选择子的方法,若方法的实现代码已经写好,运行的时候就会动态插入到类里面。书本上coredata的dynamic属性即通过上面的方式实现。

     备援接受者:请接受者看看有没有其他对象能处理这条消息,这一步进行处理的相应方法为 - (id) forwardingTargetForSelector: (SEL) selector; 方法参数即代码未知选择子,注意:我们无法操作经由这一步转发的消息,要通过接受者自己去找备援对象,若找到则返回,找不到返回nil。 

    完整消息转发:修改消息的内容,即创建NSInvocation对象,把未能处理的消息的选择子,目标及参数封装到这个对象中,然后触发NSInvocation对象,把消息指派给目标对象,通过重写下列方法来转发消息。- (void) forwardInvocation:(NSInvocation *) invocation。此方法默认实现,即改变调用目标,使消息在新目标上可以调用,这样与备援接受者实现的方法等效,只是在发送给备援接受者之前可以修改消息内容。

2、method swizzling的理解和用法

    (1) Method Swizzling基本概念        
        通过前面对OC运行时的理解,允许我们修改selector(method name)到implementation(the method code itself)的映射。网上说,利用这个特性,我们可以"修补"那些没有源码的方法,如(AppKit,FoundationKit,或第三方的库等)。给这些方法基础上,增加新的操作,等和category不同的是,category如果定义了一个原来相同的方法,那么会直接覆盖原来的方法。Method Swizzling让你可以在替代原来的方法的同时可以调用原来的方法,并且为这个方法增加一些新的功能,记录日志等等,有点像subclassing。
    (2) Method Swizzling原理
    在前面已经讲到,在Objective-C中调用一个方法,其实是向一个对象发送消息,查找消息的唯一依据是selector的名字。利用Objective-C的动态特性,可以实现在运行时偷换selector对应的方法实现,达到给方法挂钩的目的。

  每个类都有一个方法列表,存放着selector的名字和方法实现的映射关系。IMP有点类似函数指针,指向具体的Method实现。
  selectorA --> IMPa    selectorB --> IMPb    selectorC -->IMPc ....

 要实现互换两个函数的操作,首先要利用下面的两个函数:
 void method_exchangeImplementation(Method m1, Method m2)   //交换参数中传入的两个方法实现,方法实现可通过下面的函数获得
 Method class_geInstanceMethod (Class aClass, SEL aSelector)   //根据给定的选择从类中取出与之相关的方法的实现
以交换selectorA和selectorB两个方法的实现为例,交换后的指针指向为,selectorA --> IMPb  selectorB -->  IMPa

 (3) Method Swizzling实例代码
 以在NSString的一个分类中添加一个新的方法myLowercaseString ,与NSString已经存在的lowercaseString方法的交换为例,首先在分类中定义如下方法:

  @interface NSString (myAdditions)
 - (NSString *)myLowercaseString;
 @end

 @implementation NSString (myAdditions)
 - (NSString *)myLowercaseString {
NSString *lowercase = [self myLowercaseString];
NSLog(@"输出结果为:%@ => %@", self, lowercase);
return lowercase;
}
 @end

 // 通过下面代码来交换上面两个方法
Method originMethod = class_getInstanceMethod([NSString class], @selector(lowercaseString));
 Method swappedMethod = class_getInstanceMethod([NSString class], @selector(myLowercaseString));
 method_exchangeImplementations(originMethod, swappedMethod);
 //最后在NSString上调用lowercaseString方法,会输出NSLog的打印语句,输出结果为:
 NSString *string = @"THIS IS THE STRING";
 NSString *lowercaseString = [string lowercaseString];

 //实际上,交换方法后,lowercaseString调用的是myLowercaseString的具体实现,而myLowercaseString里面又调用它自己指向的lowercaseString方法的实现,从而得到上面的输出结果

第三部分、懒加载

懒加载——也称为延迟加载,即在需要的时候才加载(效率低,占用内存小)。 注意:如果是懒加载的话则一定要注意先判断是否已经有了,如果没有那么再去进行实例化

使用懒加载的好处: (1)不必将创建对象的代码全部写在viewDidLoad方法中,代码的可读性更强 (2)每个控件的getter方法中分别负责各自的实例化处理,代码彼此之间的独立性强,松耦合 具体实现:重写相关属性的get方法,在这个方法中alloc init该属性,如果是ui控件,在这里创建后添加到父控件上。

有一个问题:在ARC下使用懒加载,不在当前可见范围内的控件会不会做release处理,就像UITabBarController不在当前显示窗口上的viewcontroller就应该释放。