美文网首页
第二章:对象,消息,运行期

第二章:对象,消息,运行期

作者: LucXion | 来源:发表于2016-10-31 22:10 被阅读0次
    • 属性的理解:

    属性是OC的一项特征,用于封装对象中的数据,OC对象通常会把其所需要的的数据保存为各种实例变量。

    对象的布局:
    Java和C++中,对象的布局在编译器就已经固定了, 访问其中某一项变量,编译器就会把其替换为“偏移量”,这个偏移量是“硬编码”,表示该变量距离存放对象的内存的起始地址有多远。这样的缺点在于:代码使用了编译器计算出来的偏移量,那么在修改类定义后必须重新编译,如果代码库中使用的是一份旧的类定义,如果何其相连的代码使用了一份新的类定义,那么就会出现不兼容的现象(incompatibility)。

    OC中的做法是,将实例变量当成存储偏移量的特殊变量

    @property NSString * name;
    @impretment
    @synthesiize name = _nickName; (将生成的name 重命名为_nickName)
    

    @dynamic name(告诉编译器不要生成存取方法,访问属性的代码时编译器也不会报错,例如使用到CoreData框架)

    • 属性内存管理语义:

    assign:“设置方法”只会针对“纯量类型”(CGFloat等)做简单的赋值操作
    strong:此特性表明了一种“拥有关系”,为这种属性设置新值时,保存新值->释放旧值->将新值设置上去
    weak:“非拥有关系”,既不保存新值,也不释放旧值,此特性和assign类似,但在旧值被摧毁的时候,属性值也会被清空
    unsafe_unretained: 语义与assign相似,但是适用于对象,在目标对象被摧毁时,属性值不会被清空
    copy:语义与strong相似,但不保存新值,而是将其拷贝,通常修饰NSString用以保存它的封装性

    strong和copy的区别在于是否希望源值改变的时候你的属性是否也跟着改变(在这一层考虑用copy比较保险),但是oc是弱语言,在修饰可变数组或者可变的变量时,不用copy可能会导致可变指针指向不可变变量,导致崩溃,所以修饰可变时用strong

    方法名:getter BOOL类型重定义getter方法 getter = isXXX

    • 技巧五:直接访问实例变量的坑

    Atomic :确保原子性(严重影响性能),同步锁,但不能保证线程安全,例如在一个线程连续多次读取某属性过程中,有别的线程同时修改属性的值,还是可能会读取错误的值,但是在MacOSX的开发环境下,通常不会有性能瓶颈。

    直接访问实例变量:
    1.速度比较快,编译器所生成的代码会直接访问保存变量实例变量的那块内存
    2.直接访问实例变量会绕过“内存管理语义”
    3.直接访问实例变量会绕过“KVO”

    • 技巧六:何时直接访问实例变量

    1.在对象内部读取数据时,应该直接通过实例变量来读,而写入数据时,则应该通过属性来写
    2.在初始化和dealloc方法中,应该总是直接通过实例变量来读写数据
    3.使用懒加载,应该通过属性来读取数据

    • 技巧七:关于对象的等同性

    对象等同性
    “==”只能判断指针是否相同
    NSObjective 判断等同性的关键方法为

    -(BOOL)isEqual:(id)object
    -(NSUInteger)hash
    类似isEqualToString方法的性能优于isEqual(少类型判断环节)

    检查对象的等同性,需要提供isEqual和hash方法
    相同的对象具有相同的hash码,相同的hash码并不一定就是相同的对象
    不要逐个盲目的检测每条属性,而是应该根据具体的需求制定检测方案(重写isEqual和hash方法)

    isEqual的原理:
    1.先判断指针,再判断类型,接着逐个属性判断(有一条不符既return NO)
    2.如果类型不匹配,那么将不会走
    3.isEqualToString这类方法,而是走基类的isEqual

    注意,当重写-(BOOL)isEqual:(id)object时,重写-(NSUInteger)hash的注意点
    -(NSUInteger)hash{   
        //不需要参与CollectionView    
        // return 1024; 
    
        //需要参与CollectionView
        NSUInteger name = [_name hash];   
        NSUInteger nickName = [_nickName hash];    
        return name ^ nickName;
    }
    

    编写hash方法时,应该使用计算速度快切hash码碰撞几率低的方法

    • 技巧八:类族模式的使用

    1.类族模式可以把实现细节隐藏在一套简单的公共接口后面(子类应该自定义自己的存储方式)

    基类不应该有init接口, 或者在doADaysWork中抛出异常(暴力,不推荐)

    • (BOOL)isKindOfClass:(Class)aClass; 接收者是否是aClass类的实例或者从这个类继承的任何类的实例。如果是返回yes

    • (BOOL)isMemberOfClass:(Class)aClass;接收者是否是aClass的实例,如果是返回yes。

    • 技巧九:对象关联

    关联对象,相当于临时的动态添加属性,非不得已不用

    import <objc/runtime.h>
    setter : objc_setAssociatedObject(luc, (__bridge const void *)(objcKey), block, OBJC_ASSOCIATION_COPY_NONATOMIC);
    getter : void(^block)(NSInteger) = objc_getAssociatedObject(self.luc, (__bridge const void *)(objcKey));
    
    • 技巧十: 充分利用消息转发

    每一条消息发送都可以看做是一个这样的C函数

    objc_msgSend(id self,SEL cmd,…);
    

    当消息遇到“边界情况”时,则需要交给object-c运行环境中的另外函数处理:
    返回值为结构体:objc_msgSend_stret
    返回值为浮点数:objc_msgSend_fpret
    给超类发送消息:objc_msgSendSuper

    实现的步骤: 在消息者所属的类中搜寻其”方法列表“,如果能找到与选择子名称相符的方法,就跳至其实现代码,若是找不到,就沿着继承体系继续向上查找,等找到合适的方法再跳转,如果一直找不到,那就执行消息转发操作。发送成功,那么objec_msgSend会将匹配结果缓存到”快速映射表中fastmap“,每个类都有一块这样的缓存。

    消息转发分为两个大阶段P58

    • 1.先征询接收者所属的类,看是否能动态添加方法,以处理当前这个未知的”选择子“,这个过程也叫”动态方法解析“(dynamic method resolution)
    +(BOOL)resolveInstanceMethod:(SEL)sel(动态添加实例方法)
    +(BOOL)resolveClassMethod:(SEL)sel
    
    • 2.涉及完整的消息转发机制,首先请接收者看看有没有其他对象可以处理这条消息,如果没有备援的接收者,会把消息封装成NSInvocation对象,再给最后一次机会解决当前问题。

    • 3.查找备援接收者:

    -(id)forwardingTargetForSelector:(SEL)aSelector(我们无法经由这一步操作转发的消息)
    
    • 4.最后一步:
    -(void)forwardInvocation:(NSInvocation *)anInvocation
    
    64A0FBDB-3A46-42AE-819E-972322797EC2.png

    方法交换(多用于调试):

    Method peopleRun = class_getInstanceMethod([People class], @selector(run));    Method peopleEat = class_getInstanceMethod([People class], @selector(eat));    method_exchangeImplementations(peopleRun, peopleEat);
    
    • 技巧十一:类型判断

    描述OC对象所用的数据结构定义在运行期程序库的头文件里,id类型本身也定义在这里

    typedef struct objc_object {
        Class isa;
    }*id;
    

    由此可见每个对象结构体的收个成员是Class类型的变量,该变量定义了对象所属的类,通常称为is a指针

    假设有一个类SomeClass的子类从NSObject 中继承而来,其继承体系如下:

    8B1C1CA5-5E0A-44F2-816D-3BED6BF35C1B.png

    总结:
    每个实例都有一个指向Class对象的指针,用以表明其类型,这些Class对象构成了类的继承体系
    如果对象的类型无法在编译器确定,那么就应该使用类型信息查询的方式来探知,而不要直接比较类对象,因为某些对象可能实现了消息转发功能,比如代理:通常情况下,在代理对象上调用class方法,返回的是代理对象本身(此类是NSProxy的子类),而非接收代理的对象所属的类。

    相关文章

      网友评论

          本文标题:第二章:对象,消息,运行期

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