码迷,mamicode.com
首页 > 编程语言 > 详细

OC语言特性

时间:2019-09-17 19:25:18      阅读:113      评论:0      收藏:0      [点我收藏+]

标签:直接   nullable   获取   obj   数据类型   中心   生效   一个   针对   

Category

  • 你用分类都做了哪些事情?
    • 声明私有方法
    • 分解体积庞大的类文件
    • 把Framework的私有方法公开
  • 特点
    • 在运行时决议,也就是在编译时并没有把Category中声明的内容添加到宿主类中,而是在运行的时候通过runtime将添加的方法添加到宿主类上面
    • 可以为系统添加分类
  • 分类中可以添加哪些内容?
    • 实例方法
    • 类方法
    • 协议
    • 属性
  • Category结构
struct category_t {
    const char *name;   //分类名称
    classref_t cls;     //分类所属的宿主类
    struct method_list_t *instanceMethods;  //实例方法列表
    struct method_list_t *classMethods;  //类方法列表
    struct protocol_list_t *protocols;  //协议
    struct property_list_t *instanceProperties; //实例属性列表
    // Fields below this point are not always present on disk.
    struct property_list_t *_classProperties;

    method_list_t *methodsForMeta(bool isMeta) {
        if (isMeta) return classMethods;
        else return instanceMethods;
    }

    property_list_t *propertiesForMeta(bool isMeta, struct header_info *hi);
};
  • Category方法添加源码
// Attach method lists and properties and protocols from categories to a class.
// Assumes the categories in cats are all loaded and sorted by load order, 
// oldest categories first.
static void 
attachCategories(Class cls, category_list *cats, bool flush_caches)
{
    if (!cats) return;
    if (PrintReplacedMethods) printReplacements(cls, cats);

    bool isMeta = cls->isMetaClass();

    // fixme rearrange to remove these intermediate allocations
    /*二维数组
     
     */
    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;
        }
    }

    //获取随著类当中的rw数据,其中包含宿主类的方法列表信息
    auto rw = cls->data();

    //主要针对分类中有关于内存管理想干方法情况下的一些特殊处理
    prepareMethodLists(cls, mlists, mcount, NO, fromBundle);
    
    /*
     rw代表类
     methods代表类的方法列表
     attachLists 方法的含义是 将含有没count个元素的mlists拼接到rw的methods上
     */
    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);
}

关联对象

  • 能否给分类添加“成员变量”?
    • 能通过关联对象的方式给分类添加成员变量
    //设定一个value值,通过key设定和value的映射关系,通过policy策略关联到对象上面
    objc_setAssociatedObject(id _Nonnull object, const void * _Nonnull key,
                         id _Nullable value, objc_AssociationPolicy policy)
    
    //根据指定的key到object中获取和key相对应的关联值
    objc_getAssociatedObject(id _Nonnull object, const void * _Nonnull key)
    
    //根据指定对象移除它所有的关联对象
    objc_removeAssociatedObjects(id _Nonnull object)
    • 通过关联对象为分类添加的"成员变量"由AssociationsManager统一管理全部存储在AssociationsHashMap中,所有类的关联对象都存在同一个全局容器中
    void _object_set_associative_reference(id object, void *key, id value, uintptr_t policy) {
    // retain the new value (if any) outside the lock.
    ObjcAssociation old_association(0, nil);
    //根据policy的值对value进行copy或者retain操作
    id new_value = value ? acquireValue(value, policy) : nil;
    {
        //关联对象管理类,C++实现的一个类
        AssociationsManager manager;
    
        //获取其维护的一个HashMap
        AssociationsHashMap &associations(manager.associations());
        disguised_ptr_t disguised_object = DISGUISE(object);
        if (new_value) {
            // break any existing association.
            //根据对象指针查找对应的一个ObjectAssociationMap结构的map
            AssociationsHashMap::iterator i = associations.find(disguised_object);
            if (i != associations.end()) {
                // secondary table exists
                ObjectAssociationMap *refs = i->second;
                ObjectAssociationMap::iterator j = refs->find(key);
                if (j != refs->end()) {
                    old_association = j->second;
                    j->second = ObjcAssociation(policy, new_value);
                } else {
                    (*refs)[key] = ObjcAssociation(policy, new_value);
                }
            } else {
                //没找到关联值的时候进行添加
                // create the new association (first time).
                ObjectAssociationMap *refs = new ObjectAssociationMap;
                associations[disguised_object] = refs;
                (*refs)[key] = ObjcAssociation(policy, new_value);
                object->setHasAssociatedObjects();
            }
        } else {
            //当value传为空时则对该关联进行擦除操作
            // setting the association to nil breaks the association.
            AssociationsHashMap::iterator i = associations.find(disguised_object);
            if (i !=  associations.end()) {
                ObjectAssociationMap *refs = i->second;
                ObjectAssociationMap::iterator j = refs->find(key);
                if (j != refs->end()) {
                    old_association = j->second;
                    refs->erase(j); //擦除操作
                }
            }
        }
    }
    // release the old value (outside of the lock).
    if (old_association.hasValue()) ReleaseValue()(old_association);
    }

扩展(Extension)

  • 作用
    • 声明私有属性,可以不被子类继承
    • 声明私有方法
    • 声明私有成员变量
  • 特点
    • 编译时决议
    • 只以声明的形式存在,多数情况下寄生于宿主类的.m中来进行声明方法的实现
    • 不能为系统类添加扩展

代理(Delegate)

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

  • 怎么声明必须实现方法和选择实现方法
    • 被标记为@required为必须实现的
    • 被标记为@optional为可选实现的
  • 容易出现循环引用,所以委托方需要通过weak关键字来弱化引用

通知(NSNotification)

  • 通知是使用观察者模式来实现、用于跨层传递消息的机制
  • 通知和代理的区别
    • 代理是通过代理模式实现的,通知是通过观察者模式实现的
    • 代理是一对一,通知是一对多
  • 通知的实现流程
    • 发送者将发送的消息发送到通知中心
    • 通知中心广播给观察者
  • 通知的实现机制(代码未开源,猜测)
    • 底层应该维护一个Map表来存储注册的通知,同事每个通知下面设有观察者列表,当收到消息后则给观察者列表中的观察者发送消息

KVO

  • Key-value observing的缩写
  • 是OC对观察者设计模式的又一实现
  • Apple使用了isa混写(isa-swizzling)来实现KVO(派生出一个名为NSKVONotifying_(ObjName)的子类并重写其setter方法)
  • KVO触发的问题
    • 通过外部直接修改属性可能(比如属性名设置isAge,通过key=age进行kvc设置)可以触发KVO监听方法(调动setter方法)
    • 通过KVC修改也可以触发KVO的回调
    • 通过直接修改成员变量无法触发KVO监听,需手动添加监听
  //直接为成员变量赋值
    [self willChangeValueForKey:@"value"];
    _value += 1;
    [self didChangeValueForKey:@"value"];

KVC(键值编码技术)

  • Key-value coding的缩写
- (void)setValue:(nullable id)value forKey:(NSString *)key;

- (nullable id)valueForKey:(NSString *)key;
  • 通过键值编码技术可以修改对象同名成员变量的值,其中key是没有任何限制的,在外界可以通过key进行私有变量的修改,破坏了面向对象的思想方法(把对象行为属性结合为一个整体,隐藏内部实现,吧不想告诉别人的东西隐藏起来)
  • KVC调用流程
    • 若存set(key)的方法则优先调用该方法
    • 如果不存在set(key)方法,KVC机制会检查+ (BOOL)accessInstanceVariablesDirectly方法有没有返回YES,如果返回yes则按照会按照_key,_iskey,key,iskey的顺序搜索成员变量,如果返回NO则调用- (void)setValue:(id)value forUndefinedKey:(NSString *)key方法抛出异常
// 如果开发者想让这个类禁用KVC里,那么重写+ (BOOL)accessInstanceVariablesDirectly方法让其返回NO即可,这样的话如果KVC没有找到set<Key>:属性名时,会直接用setValue:forUndefinedKey:方法。如果返回YES则按照会按照_key,_iskey,key,iskey的顺序搜索成员变量

+ (BOOL)accessInstanceVariablesDirectly
{
    return YES;
}

//setValue:forKey:通过_key,_iskey,key,iskey的顺序搜索成员变量查找仍未找到对应的key时调用该方法
- (void)setValue:(id)value forUndefinedKey:(NSString *)key
{
    NSLog(@"%@", key);
}

//valueForKey:通过_key,_iskey,key,iskey的顺序搜索成员变量查找仍未找到对应的key时调用该方法
- (id)valueForUndefinedKey:(NSString *)key
{
    NSLog(@"%@", key);
    return [super valueForUndefinedKey:key];
}

属性关键字

  • 原子性
    • atomic修饰数组时对数组进行赋值和获取时是安全的,但是对数组进行增删是不在atomic责任之内的
    • nonmatic
  • 引用计数
    • retain/strong
    • assign/unsafe_unetained
    • weak
    • copy
    • assign
  • assign和weak区别
    • assign
      • assign可以修饰基本数据类型,如int,BOOL等;
      • 修饰对象类型时,不改变其引用计数
      • 会产生垂悬指针(assign修饰对象释放后指针会继续指向原地址,如果继续访问可能获取原对象造成异常)
    • weak
      • 不改变被修饰对象的引用计数
      • 所指对象在被释放后会自动置nil
    • assign既可以修饰对象又可以修饰基本数据类型,weak只能修饰对象
    • assign修饰对象释放后指针会继续指向原地址,而weak是置为nil的

OC语言笔试题

  • MRC下如何重写retain修饰变量的setter方法?
@property (nonatomic, retain) id obj;

- (void)setName:(id)obj
{
    if (_obj != obj) {
        [_obj release];
        _obj = [obj retain];
    }
}

OC语言特性

标签:直接   nullable   获取   obj   数据类型   中心   生效   一个   针对   

原文地址:https://www.cnblogs.com/GoodmorningMr/p/11536076.html

(0)
(0)
   
举报
评论 一句话评论(0
登录后才能评论!
© 2014 mamicode.com 版权所有  联系我们:gaon5@hotmail.com
迷上了代码!