在 Objective-C 中可以通过 Category 给一个现有的类添加属性,但是却不能添加实例变量

反正读第一遍的时候我是有点晕的,可以添加“属性”,然后又说“添加实例变量”,第一感觉就好像 有点自相矛盾了。那么我们谈谈:

  • 什么是实例变量?

    实例变量就是一种变量,可以存放数据的。在oc上,形式就如:        

@interface MyViewController :UIViewController
{
UIButton *myButton;
} @end
  • 什么是属性?

  属性是oc的一个新的机制,并且要求你必须声明与之对应的实例变量,这是最早期的一种概念,随着编译器从GCC转换为LLVM(low level virtual machine),从此不再需要为属性声明实例变量了,如果LLVM发现一个没有匹配实例变量的属性,它将自动创建一个以下划线开头的实例变量。

  • Category可以给一个现有的类添加属性

我建立了个 UIColor的hex分类  ,在UIColor+hex.h上我做了这么一件事:

然后做你会发现编译器并不会报任何错误,build一下也不会有问题,当然这不能说这段代码是可以运行的【crash】。仔细的话,你会发现在.m中是有警告的。

它说,属性 hex的setHex和hex方法需要定义使用 @dynamic或者自己实现。  这里添加的属性,不会自动生成实例变量,这里添加的属性其实是添加的getter与setter方法。

这里,我们是不是可以理解上面的那句话了,其实就是等同于这句话

类别中只能添加方法,不能添加实例变量

首先,我们就用了@dynamic去做:

警告确实立马消失,但是是不是可以运行了呢?梦想是美好的,结果依旧crash。

  • 那么这个@dynamic到底干了什么可以让警告消除,当然你会不会发现这个@dynamic在哪里见过?对了在你用coredata的时候,你就会发现了【NSManagedObject自己探索去吧】。

@dynamic 是相对于 @synthesize的,它们用样用于修饰 @property,用于生成对应的的getter和setter方法。但是@ dynamic表示这个成员变量的getter和setter方法并不是直接由编译器生成,而是手工生成或者运行时生成。

@dynamic just tells the compiler that the getter and setter methods are implemented not by the class itself but somewhere else (like the superclass or will be provided at runtime).

  那为什么会崩溃?哈哈,不要想当然了,因为你没有在runtime中提供getter and setter methods。那当然会崩溃啦。

上面我们就基本上了解了在category添加属性的一些问题。那么该如何解决这些问题,也是下面我想说的Associated Objects,let's  go!

首先我们带着这么2个疑问:

  1. 关联对象被存放到什么地方,是不是存放在被关联对象本身的内存中?
  2. 关联对象的生命周期怎么样。什么时候被释放,什么时候被移除?

上面是两种方法的实现

  • objc_setAssociatedObject
  • objc_getAssociatedObject
  • objc_removeAssociatedObjects

其中第三个函数objc_removeAssociatedObjects 函数我们一般式用不上的,因为这个函数会移除一个对象的所有关联对象,该将对象恢复为“

pristine state”。一般我我没问你如果要移除一个关联对象,我们会使用objc_setAssociatedObject 函数中传入nil就可以移除。

  • 你会发现这两个函数都有一个key,在关联对象的时候我们使用了两种形式,那么这个key的设置有什么要求么?

  key 值必须是一个对象级别的唯一常量

从上面的两种实现关联对象的方法中,我们使用了两种不同的key值

  1. static void *kAssociatedObjectKey = &kAssociatedObjectKey;
  2. 用 selector ,使用 getter 方法的名称作为 key 值。

  3. 其实还有一种 static char kAssociatedObjectKey;      objc_getAssociatedObject(self, &kAssociatedObjectKey);

  对比这三种方式,很多人都喜欢第二种

  • 关于这个 objc_AssociationPolicy(关联策略)又是什么?

  

关联策略

等价属性

说明

OBJC_ASSOCIATION_ASSIGN

@property (assign) or @property (unsafe_unretained)

弱引用关联对象

OBJC_ASSOCIATION_RETAIN_NONATOMIC

@property (strong, nonatomic)

强引用关联对象,且为非原子操作

OBJC_ASSOCIATION_COPY_NONATOMIC

@property (copy, nonatomic)

复制关联对象,且为非原子操作

OBJC_ASSOCIATION_RETAIN

@property (strong, atomic)

强引用关联对象,且为原子操作

OBJC_ASSOCIATION_COPY

@property (copy, atomic)

复制关联对象,且为原子操作

  上面这张表很清晰的告诉你这个关联策略的作用。

  1. 关联对象与被关联对象本身的存储并没有直接的关系,它是存储在单独的哈希表中的;
  2. 关联对象的五种关联策略与属性的限定符非常类似,在绝大多数情况下,我们都会使用 OBJC_ASSOCIATION_RETAIN_NONATOMIC 的关联策略,这可以保证我们持有关联对象;
  3. 关联对象的释放时机与移除时机并不总是一致,比如实验中用关联策略 OBJC_ASSOCIATION_ASSIGN 进行关联的对象,很早就已经被释放了,但是并没有被移除,而再使用这个关联对象时就会造成 Crash 。

iOS之 Category 属性 的理解的更多相关文章

  1. iOS数据存储之属性列表理解

    iOS数据存储之属性列表理解 数据存储简介 数据存储,即数据持久化,是指以何种方式保存应用程序的数据. 我的理解是,开发了一款应用之后,应用在内存中运行时会产生很多数据,这些数据在程序运行时和程序一起 ...

  2. IOS学习5——属性与成员变量

    [转]iOS中属性与成员变量的区别 ios中属性修饰符的作用 1. 属性用property声明 2. 简而言之,对于目前的ios开发,属性和成员变量的区别,完全可以不管. 3. 这个是历史原因造成的. ...

  3. iOS开发-automaticallyAdjustsScrollViewInsets属性

    iOS开发-automaticallyAdjustsScrollViewInsets属性 Available in iOS 7.0 and later. 简单点说就是automaticallyAdju ...

  4. iOS类别(Category)

    iOS类别(Category)与扩展(Extension) 苹果的官方文档 Category在iOS开发中使用非常频繁.尤其是在为系统类进行拓展的时候,我们可以不用继承系统类,直接给系统类添加方法,最 ...

  5. IOS UITableView NSIndexPath属性讲解

    IOS UITableView NSIndexPath属性讲解   查看UITableView的帮助文档我们会注意到UITableView有两个Delegate分别为:dataSource和deleg ...

  6. prototype属性的理解

    1.对象:对象是JS的基本数据类型(原始类型(数字.字符串和布尔值),对象类型).对象是一种复合值:它将很多值(原始值或者其他对象)聚合在一起,可通过名字访问这些值. 2.三类JS对象和两类属性: 内 ...

  7. Intent的属性及Intent-filter配置——Action、Category属性与intent-filter属性

    Intent的Action.Category属性都是一个普通的字符串,其中Action代表该Intent所要完成的一个抽象“动作”,而Category则用于为Action增加额外的附加列别的信息.通常 ...

  8. iOS开发 关于addChildViewController的理解

    iOS开发 关于addChildViewController的理解 前言 我之前是做Android开发的接触ios开发不到一个月的时间,所以在有些东理解上会不自觉的向Android方向靠拢. 理解 通 ...

  9. Intent的Component,Action和Category属性详解-android学习之旅(五十)

    Component属性 代码示例 public class MainActivity extends Activity{ @Override protected void onCreate(Bundl ...

随机推荐

  1. Spring @RequestParam乱码问题

    在网上找了很多资料才找到解决的方法,通过URL传递命名参数,后台接收的却是乱码解决方法如下: 方法一:将接收的参数重新编码 @RequestMapping(value="/handle&qu ...

  2. 嵌入系统squashfs挂载常见问题总结

    由于squahsfs的一些优点,嵌入系统常常直接使用squashfs作为initrd挂载到/dev/ram,作为rootfs.这里对常见的一些问题进行一些分析. 1. kernel启动出现错误 RAM ...

  3. 短视频图像处理 OpenGL ES 实践

    2017年,短视频正以其丰富的内容表现力和时间碎片化的特点,快速崛起,而短视频最具可玩性之处就在支持人脸识别的动态贴图和各种不同效果的美颜.滤镜等.那短视频动态贴纸.滤镜.美颜等功能究竟是如何实现的呢 ...

  4. python 实现登录程序

    本文介绍一个用python 实现的登录程序.python新手们可以参考一下. 用户信息存放于一个文件中,需要引入文件,校验输入的用户名.密码是否跟用户列表中的用户名密码相匹配,如果匹配,这登录成功,否 ...

  5. iOS开发之UITableView中计时器的几种实现方式(NSTimer、DispatchSource、CADisplayLink)

    最近工作比较忙,但是还是出来更新博客了.今天博客中所涉及的内容并不复杂,都是一些平时常见的一些问题,通过这篇博客算是对UITableView中使用定时器的几种方式进行总结.本篇博客会给出在TableV ...

  6. 在 macOS High Sierra 10.13 搭建 PHP 开发环境

    2017 年 9 月 26 日,苹果公司正式发布了新一代 macOS,版本为 High Sierra (11.13). macOS High Sierra 预装了 Ruby(2.3.3).PHP(7. ...

  7. BZOJ-1050-[HAOI2006]旅行comf(并查集)

    Description 给你一个无向图,N(N<=500)个顶点, M(M<=5000)条边,每条边有一个权值Vi(Vi<30000).给你两个顶点S和T,求 一条路径,使得路径上最 ...

  8. 关于高德地图Android开发时地图只显示一次、第二次打开不定位的解决办法

    我按照高德官方Demo改的 第一次是可以定位的,如左图 第二次就不能定位了,如右图 在onDestory中把aMap置为空即可 aMap = null; 修改完如下图: 原理是第二次打开时aMap不为 ...

  9. bzoj4236 JOIOJI hash 模拟

    JOIOJI桑是JOI君的叔叔."JOIOJI"这个名字是由"J.O.I"三个字母各两个构成的. 最近,JOIOJI桑有了一个孩子.JOIOJI桑想让自己孩子的 ...

  10. lambda表达式杂谈

    var personInfo = [ { name: "张三", age: 20, gender: "male" }, { name: "李四&quo ...