美文网首页
Objective-C语言特性相关

Objective-C语言特性相关

作者: 细雨菲菲v | 来源:发表于2018-07-13 10:40 被阅读8次

    1.分类相关

    (1)特点:
    • 运行时决议
      在编好分类文件之后,它并没有将分类当中对应添加的内容附加到相应的宿主类中,实际上宿主类中还没有分类中方法,而是在运行时,通runtime将分类中添加的内容添加到宿主类中。
    • 可以为系统类添加分类
    (2)实战

    分类中可以添加哪些内容?

    • 实例方法
    • 类方法
    • 协议
    • 属性(我们在分类中定义一个属性,实际上只是生成了对应的get方法和set方法,并没有为我们在分类中添加实例变量)

    你用分类都做了哪些事情?

    • 声明私有方法
    • 分解体积庞大的类文件
    • 把Framework的私有方法公开
    (3)分类的结构体
    截图.png
    (4)分类加载调用栈
    截图1.png
    static void remethodizeClass(Class cls){
    category_list *cats;
    bool isMeta;
    runtimeLock.assertWriting();
    /*
     我们只分析分类当中实例方法添加的逻辑
     因此在这里我们假设 isMeta = NO
    */
    isMeta = cls->isMetaClass();
    // Re-methodizing: check for more categories
    // 获取cls中未完成整合的所有分类
    if ((cats = unattachedCategoriesForClass(cls, false/*not realizing*/))) {
        if (PrintConnecting) {
            _objc_inform("CLASS: attaching categories to class '%s' %s", 
                         cls->nameForLogging(), isMeta ? "(meta)" : "");
        }
        attachCategories(cls, cats, true /*flush caches*/);        
        free(cats);
    }}      
    
     static void attachCategories(Class cls, category_list *cats, bool flush_caches){
         if (!cats) return;
         if (PrintReplacedMethods) printReplacements(cls, cats);
         /*
           我们只分析分类当中实例方法添加的逻辑,
          因此在这里我们假设isMeta = NO
        */
        bool isMeta = cls->isMetaClass();
       /*
      二维数组 [[method_t, method_t,...],[method_t, method_t,...],                               [method_t, method_t,...],...]
      */
       method_list_t **mlists = (method_list_t **)
        malloc(cats->count * sizeof(*mlists));
       property_list_t **proplists = (property_list_t **)
        malloc(cats->count * sizeof(*proplists));
       protocol_list_t **protolists = (protocol_list_t **)
        malloc(cats->count * sizeof(*protolists));
       // Count backwards through cats to get newest categories first
       int mcount = 0;
       int propcount = 0;
       int protocount = 0;
       int i = cats->count;
       bool fromBundle = NO;
       while (i--) {//这里是倒叙遍历,最先访问最后编译的分类
         //  获取一个分类
         auto& entry = cats->list[i];
        // 获取该分类的方法列表
         method_list_t *mlist = entry.cat->methodsForMeta(isMeta);
         if (mlist) {
             //最后编译的分类最先添加到分类数组中
             mlists[mcount++] = mlist;
             fromBundle |= entry.hi->isBundle();
         }
         //属性列表添加规则 同方法列表添加规则
          property_list_t *proplist = 
          entry.cat->propertiesForMeta(isMeta, entry.hi);
          if (proplist) {
            proplists[propcount++] = proplist;
          }
          protocol_list_t *protolist = entry.cat->protocols;
          if (protolist) {
             protolists[protocount++] = protolist;
         }
      }
      auto rw = cls->data();
      prepareMethodLists(cls, mlists, mcount, NO, fromBundle);
    // 添加方法
      rw->methods.attachLists(mlists, mcount);
     free(mlists);
      if (flush_caches  &&  mcount > 0) flushCaches(cls);
    // 添加属性
      rw->properties.attachLists(proplists, propcount);
      free(proplists);
    // 添加协议
      rw->protocols.attachLists(protolists, protocount);
      free(protolists);
     }
    

    如果两个分类中都有一个同名的方法,最后编译的分类当中的同名方法才会最终生效。

    2.关联对象

    能否给分类添加"成员变量" ?

      id objc_getAssociatedObject(id object,const void * key)
      void objc_setAssociatedObject(id object,const void *key,id value,objc_AssociationPolicy policy)
      void objc_removeAssociatedObjects(id object)
    

    关联对象的本质
    关联对象由AssociationsManager管理并在AssociationsHashMap存储,所有对象的关联内容都在同一个全局容器中。

    截图2.png
    截图3.png

    3.扩展属性

    (1) 特点
    • 编译时决议
    • 只以声明的形式存在,多数情况下寄生于宿主类.m中。
    • 不能为系统类添加扩展。
    (2) 一般用扩展做什么?
    • 声明私有属性
    • 声明私有方法
    • 声明私有成员变量

    4.代理

    准确的说是一种软件设计模式,iOS当中以@protocol形式体现,传递方式一对一。

    工作流程

    截图1.png

    注意点
    一般声明为weak以规避循环引用。

    截图2.png

    5.通知

    概念:通知是使用观察者模式来实现的用于跨层传递消息的机制,传递方式为一对多

    截图3.png
    如何实现通知机制?
    截图4.png

    6.KVO

    • KVO是Objective-C对观察者设计模式的一种实现
    • apple使用了isa混写(isa-swizzling)来实现KVO


      截图5.png
    • 使用setter方法改变值kvo才会生效。
    • 使用setValue:forkey:改变KVO才会生效
    • 成员变量直接修改需求手动添加KVO才会生效。
    #import <Foundation/Foundation.h>
    @interface MObject : NSObject
    @property (nonatomic,assign)int value;
    - (void)increase;
    @end
    
    #import "MObject.h"
    @implementation MObject
    - (instancetype)init{
        self = [super init];
        if(self){
            _value = 0;
        }
        return self;
    }
    - (void)increase{
        _value = _value + 1;
    }
    @end
    
    #import <Foundation/Foundation.h>
    @interface MObserver : NSObject
    @end
    
    #import "MObserver.h"
    #import "MObject.h"
    @implementation MObserver
    - (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary<NSKeyValueChangeKey,id> *)change context:(void *)context{
        if([object isKindOfClass:[MObject class]] && [keyPath isEqualToString:@"value"]){
            NSNumber *valueNum = [change valueForKey:NSKeyValueChangeNewKey];
            NSLog(@"value is %@",valueNum);
        }
    }
    @end
    
    截图6.png
    打印结果
    截图7.png
    NSKVONotifying_A的setter实现
    - (void)setValue:(id)value forKey:(NSString *)key{
        [self willChangeValueForKey:@"keyPath"];
        //调用父类实现,也即原类的实现
        [super setValue:value forKey:key];
        [self didChangeValueForKey:@"keyPath"];
    }
    

    问题

    1. 通过kvc设置value能否生效?
      2.通过成员变量直接赋值value能否生效?

    7.KVC

    苹果系统为我们提供的一种键值编码技术。

    (1) -(id)valueForKey:(NSString *)key
    截图8.png
    a.Accessor Method
    • <getKey>
    • <key>
    • <isKey>
    b.Instance var
    • _key
    • _isKey
    • key
    • isKey
    (2)-(void)setValue:(id)value forKey:(NSString *)key
    截图9.png

    8.属性关键字

    • 读写权限
    • 原子性
    • 引用技术
    (1)读写权限
    • readonly
    • readwrite
    (2)原子性
    • atomic (赋值和获取是线程安全的)
      例如一个数组使用atomic关键字,对数组进行赋值和获取是线程安全的,但是对数组进行添加数据,删除数据,不是线程安全的
    • nonatomic
    (3)引用计数
    • retain/strong

    • assign/unsafe_unretained

    • weak

    • copy
      assign

      • 修饰基本数据类型,如int , BOOL等
      • 修饰对象类型时,不改变其引用计数
      • 会产生悬垂指针(assign所修饰的对象被释放后,assign指针仍然指向原对象内存地址,这时通过assign继续访问对象,由于悬垂指针的存在,会导致程序内存泄漏或者程序异常)

      weak
      - 不改变修饰对象的引用计数。
      - 所指对象在被释放之后,weak指针会自动置为nil

      copy
      (a) 浅拷贝
      特点:1.增加被拷贝对象的引用计数 2.并没有发生新的内存分配

      截图10.png
      浅拷贝就是对内存地址的复制,让目标对象指针和原对象指针指向同一片内存空间。
      (b)深拷贝
      特点: 1.深拷贝不会增加被拷贝对象的引用计数 2.深拷贝产生了内存分配
      截图11.png
      深拷贝让目标对象指针和源对象指针指向两片内容相同的内存空间。
      (c)深拷贝vs浅拷贝
      • 是否开辟了新的内存空间
      • 是否影响了引用计数
        截图12.png
        copy关键字总结:可变对象的copy和mutableCopy都是深拷贝,不可变对象的copy是浅拷贝,mutableCopy是深拷贝,copy方法返回的都是不可变对象,
        (d)注意点

        @property(copy)NSMutableArray*array ?
        不论赋值过来的是NSMutableArray 还是NSArray,copy之后都是NSArray,这样对array进行添加元素或者是删除元素会导致程序异常。

    9.经典题目

    MRC下如何重写retain修饰变量的setter方法?

      @property(nonatomic,retain) id obj;
         - (void)setObjc:(id)obj{
          if(_obj != obj){
              [_obj release];
              [_obj retain];
          }
    }
    

    请简述分类的实现原理?

    分类的实现是由运行时决议的, 不同分类中含有同名分类方法,谁最终生效取决于谁最后参与编译,最后参与编译的分类当中的同名分类方法会最终生效,如果分类中添加的方法与宿主类方法名相同,分类方法会覆盖同名的宿主方法,这里说的覆盖是指,由于消息传递过程中,优先查找数组靠前的方法,如果找到了一个同名方法就进行调用,实际上宿主类的同名方法是仍然存在?

    KVO实现原理?

    能否为分类添加成员变量?

    相关文章

      网友评论

          本文标题:Objective-C语言特性相关

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