美文网首页iOS 开发每天分享优质文章iOS基本功iOS Kit
iOS面试题:关联对象有什么应用,系统如何管理关联对象?其被释放

iOS面试题:关联对象有什么应用,系统如何管理关联对象?其被释放

作者: iOS猿_员 | 来源:发表于2019-04-20 15:47 被阅读71次

    更多:iOS面试题大全

    我们在 iOS 开发中经常需要使用分类(Category),为已经存在的类添加属性的需求,但是使用 @property 并不能在分类中正确创建实例变量和存取方法。这时候就会用到关联对象。

    分类中的 @property
    @interface DKObject : NSObject
    
    @property (nonatomic, strong) NSString *property;
    
    @end
    
    

    在使用上述代码时会做三件事:

    • 生成带下划线的实例变量 _property
    • 生成 getter 方法 - property
    • 生成 setter 方法 - setProperty:
    @implementation DKObject {
        NSString *_property;
    }
    
    - (NSString *)property {
        return _property;
    }
    
    - (void)setProperty:(NSString *)property {
        _property = property;
    }
    
    @end
    
    

    这些代码都是编译器为我们生成的,虽然你看不到它,但是它确实在这里,我们既然可以在类中使用 @property 生成一个属性,那么为什么在分类中不可以呢?

    我们来做一个小实验:创建一个 DKObject 的分类 Category,并添加一个属性 categoryProperty

    @interface DKObject (Category)
    
    @property (nonatomic, strong) NSString *categoryProperty;
    
    @end
    
    

    看起来还是很不错的,不过 Build 一下这个 Demo,会发现有这么一个警告:

    image

    在这里的警告告诉我们 categoryProperty 属性的存取方法需要自己手动去实现,或者使用 @dynamic 在运行时实现这些方法。

    换句话说,分类中的 @property 并没有为我们生成实例变量以及存取方法,而需要我们手动实现。

    使用关联对象

    Q:我们为什么要使用关联对象?

    A:因为在分类中 @property 并不会自动生成实例变量以及存取方法,所以一般使用关联对象为已经存在的类添加『属性』。

    以下是与关联对象有关的 API,并在分类中实现一个伪属性:

    #import "DKObject+Category.h"
    #import <objc/runtime.h>
    
    @implementation DKObject (Category)
    
    - (NSString *)categoryProperty {
        return objc_getAssociatedObject(self, _cmd);
    }
    
    - (void)setCategoryProperty:(NSString *)categoryProperty {
        objc_setAssociatedObject(self, @selector(categoryProperty), categoryProperty, OBJC_ASSOCIATION_RETAIN_NONATOMIC);
    }
    
    @end
    
    

    这里的 _cmd 代指当前方法的选择子,也就是 @selector(categoryProperty)

    我们使用了两个方法 objc_getAssociatedObject 以及 objc_setAssociatedObject 来模拟『属性』的存取方法,而使用关联对象模拟实例变量。

    在这里有必要解释两个问题:

    • 为什么向方法中传入 @selector(categoryProperty)?
    • OBJC_ASSOCIATION_RETAIN_NONATOMIC 是干什么的?

    关于第一个问题,我们需要看一下这两个方法的原型:

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

    @selector(categoryProperty) 也就是参数中的key,其实可以使用静态指针 static void *类型的参数来代替,不过在这里,笔者强烈推荐使用 @selector(categoryProperty) 作为 key 传入。因为这种方法省略了声明参数的代码,并且能很好地保证 key 的唯一性

    OBJC_ASSOCIATION_RETAIN_NONATOMIC 又是什么呢?如果我们使用 Command 加左键查看它的定义:

    typedef OBJC_ENUM(uintptr_t, objc_AssociationPolicy) {
        OBJC_ASSOCIATION_ASSIGN = 0,           /**< Specifies a weak reference to the associated object. */
        OBJC_ASSOCIATION_RETAIN_NONATOMIC = 1, /**< Specifies a strong reference to the associated object. 
                                                *   The association is not made atomically. */
        OBJC_ASSOCIATION_COPY_NONATOMIC = 3,   /**< Specifies that the associated object is copied. 
                                                *   The association is not made atomically. */
        OBJC_ASSOCIATION_RETAIN = 01401,       /**< Specifies a strong reference to the associated object.
                                                *   The association is made atomically. */
        OBJC_ASSOCIATION_COPY = 01403          /**< Specifies that the associated object is copied.
                                                *   The association is made atomically. */
    };
    
    

    从这里的注释我们能看到很多东西,也就是说不同的 objc_AssociationPolicy 对应了不通的属性修饰符:

    objc_AssociationPolicy modifier
    OBJC_ASSOCIATION_ASSIGN assign
    OBJC_ASSOCIATION_RETAIN_NONATOMIC nonatomic, strong
    OBJC_ASSOCIATION_COPY_NONATOMIC nonatomic, copy
    OBJC_ASSOCIATION_RETAIN atomic, strong
    OBJC_ASSOCIATION_COPY atomic, copy

    而我们在代码中实现的属性 categoryProperty 就相当于使用了 nonatomic 和 strong 修饰符。

    在obj dealloc时候会调用object_dispose,检查有无关联对象,有的话_object_remove_assocations删除

    更多:iOS面试题大全

    相关文章

      网友评论

        本文标题:iOS面试题:关联对象有什么应用,系统如何管理关联对象?其被释放

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