OC语言特性

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];
    }
}

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

时间: 2024-10-09 19:29:14

OC语言特性的相关文章

OC语言的特性(二)-Block

本篇文章的主要内容 了解何谓block. 了解block的使用方法. Block 是iOS在4.0版本之后新增的程序语法. 在iOS SDK 4.0之后,Block几乎出现在所有新版的API之中,换句话说,如果不了解Block这个概念就无法使用SDK 4.0版本以后的新功能,因此虽然Block本身的语法有点难度,但为了使用iOS的新功能我们还是得硬着头皮去了解这个新的程序概念. 一.看一看什么是Block 我们使用'^'运算符来声明一个Block变量,而且在声明完一个Block变量后要像声明普通

【转】浅思OC的语言特性

算了算,学习IOS已经有一段时间了.今天花了点时间思考一下OC的语言特性,让自己的心不要那么浮躁,注重基础,回归本源. OC做为一门面向对象语言,自然具有面向对象的语言特性,如封装.继承.多态.他具有静态语言的特性(如C++),又有动态语言的效率(动态绑定.动态加载等).整体来说,确实是一门不错的编程语言. 现在,让我来想想OC的动态语言特性.OC的动态特性表现为了三个方面:动态类型.动态绑定.动态加载.之所以叫做动态,是因为必须到运行时(run time)才会做一些事情. (1)动态类型 动态

oc语言--面向对象的三大特性

一.封装 1.什么是封装 在程序上,隐藏对象的属性和实现细节,仅对外公开接口,控制在程序中属性的读和修改的访问级别:将对象得到的数据和行为(或功能)相结合,形成一个有机的整体,也就是将数据与操作数据的源代码进行有机的结合,形成“类”,其中数据和函数都是类的成员. 1> set方法 ① 作用:提供一个方法给外界设置成员变量值,实现对参数的相应过滤 ② 命名规范 *方法名必须以set开头 *set后面跟上成员变量的名称,成员变量名首字母必须大写 *返回值一定是void *一定要接收一个参数,而且参数

OC学习2——C语言特性之函数

1.OC是在C语言的基础上进行扩展的,在OC中直接用C语言进行coding也是可以通过编译的.因此,函数定义的语法格式如下: 函数返回值类型 函数名(形参列表) { //由零条或多条可执行性语句组成的函数提 } 2.函数的传递机制:值传递.地址传递. 值传递:将实际的参数值的副本(复制品)传入函数内,参数本身不受函数内对其副本的改变的影响. 地址传递:将实际参数的地址的副本传入函数,函数中对地址对应位置的值进行改变会影响到实际参数的值. 3.内部函数和外部函数: 内部函数:定义函数时用stati

OC语言的特性(一)-消息传递与调用函数的表现形式

我们在初学Objective-C时,都会觉得ObjC中的消息传递和其他语言的调用函数差不多,只是在OC中,方法调用用消息传递这一概念来代替. 那么到底怎样区别OC中的消息传递与其他语言的调用函数呢. 可以使用C语言与OC语言进行一下对比. C语言示例 //声明一个函数,用来获取两个整型值的中较大的值int max(int a,int b);  int main(int argc, const char * argv[]) {    //调用函数,获取4,6两个整型值的最大值    int res

李洪强iOS开发之OC语言description方法和sel

OC语言description方法和sel 一.description方法 Description方法包括类方法和对象方法.(NSObject类所包含) (一)基本知识 -description(对象方法) 使用NSLog和@%输出某个对象时,会调用对象的description方法,并拿到返回值进行输出. +description(类方法) 使用NSLog和@%输出某个对象时,会调用类对象的description方法,并拿到返回值进行输出,把整个对象一次性打印出来,打印对象使用%@. 使用@%打

Go的语言特性总结

写在前面: 近来关于对Golang的讨论有很多,七牛的几个大牛们也断定Go语言在未来将会快速发展,并且很可能会取代Java成为互联网时代最受欢迎的编程语言.Go语言是google推出的编程语言,在已经成功的给世人创造了改变人们生活的操作系统之后,google似乎感觉有必要再为世人带来一款强大的编程语言,而Go语言依靠自己众多友好的特性也不负众望正在被开发者接触,我有幸在学习高性能并发编程的时候认识了Go语言,在了解了Go的一些特性之后决定系统的学习一番.我发现关于Go的学习资料并不多,以至于我需

OC语言基础知识

OC语言基础知识 一.面向对象 OC语言是面向对象的,c语言是面向过程的,面向对象和面向过程只是解决问题的两种思考方式,面向过程关注的是解决问题涉及的步骤,面向对象关注的是设计能够实现解决问题所需功能的类. 术语:OO面向对象.OOP面向对象编程 二.类 (一)关于类 类的设计只关注三个东西:类名.属性和方法 注意:一般名词都是类,拥有相同属性和行为的对象都可以抽象为一个类,类名是标识符的一种,需要符合规范,通常类名的第一个字母大写,且不能有下划线,如果有多个单词则使用驼峰标识.在对方法进行类的

Java基础学习(一)——语言特性及开发环境搭建

学习了一段时间的Java,想通过几篇文章做一下这一段时间的学习总结.由于有c/c++的一些基础,总的来说Java语言还是挺具有亲和感的.虽然经常到博客园找资料,但是也一直没有自己动手写过博客,现在练练手,也为日后有个回忆吧. 一,Java简介 1.语言起源 语言起源:Java是SUN ( Stanford University Network 斯坦福大学网络公司 ) 1995年推出的一门高级编程语言.Java名称的来源Java最初被命名为Oak,目标在于家用电器等小型系统的编程语言,随着市场的变