OC分类(类目/类别) 和 类扩展 - 全解析
#if !__OBJC2__
Class super_class OBJC2_UNAVAILABLE;
const char *name OBJC2_UNAVAILABLE;
long version OBJC2_UNAVAILABLE;
long info OBJC2_UNAVAILABLE;
long instance_size OBJC2_UNAVAILABLE;
struct objc_ivar_list *ivars OBJC2_UNAVAILABLE;
struct objc_method_list **methodLists OBJC2_UNAVAILABLE;
struct objc_cache *cache OBJC2_UNAVAILABLE;
struct objc_protocol_list *protocols OBJC2_UNAVAILABLE;
#endif
// Class ISA;
Class superclass;
cache_t cache; // formerly cache pointer and vtable
class_data_bits_t bits; // class_rw_t * plus custom rr/alloc flags
......
}
由此可见,结构体objc_class也是继承objc_object,说明Class在设计中本身也是一个对象。
int ivar_count OBJC2_UNAVAILABLE;
#ifdef __LP64__
int space OBJC2_UNAVAILABLE;
#endif
/* variable length structure */
struct objc_ivar ivar_list[1] OBJC2_UNAVAILABLE;
}
objc_ivar_list其实就是一个链表,存储多个objc_ivar,而objc_ivar结构体存储类的单个成员变量信息。
struct objc_method_list *obsolete OBJC2_UNAVAILABLE;
int method_count OBJC2_UNAVAILABLE;
#ifdef __LP64__
int space OBJC2_UNAVAILABLE;
#endif
/* variable length structure */
struct objc_method method_list[1] OBJC2_UNAVAILABLE;
}
同理,objc_method_list也是一个链表,存储多个objc_method,而objc_method结构体存储类的某个方法的信息。
iOS分类(category),类扩展(extension)—史上最全攻略
背景:
在大型项目,企业级开发中多人同时维护同一个类,此时程序员A因为某项需求只想给当前类currentClass添加一个方法newMethod,那该怎么办呢?
最简单粗暴的方式是把newMethod添加到currentClass中,然后直接实现该方法就OK了。
但考虑到OC是单继承的,子类可以拥有父类的方法和属性。
如果把newMethod写到currentClass中,那么currentClass的子类也会拥有newMethod。但真正的需求是只需要currentClass拥有newMethod,而currentClass的子类不会拥有。
苹果为了解决这个问题,就引入了分类(Category)的概念。
分类(Category):
概念
分类(Category)是OC中的特有语法,它是表示一个指向分类的结构体的指针。原则上它只能增加方法,不能增加成员(实例)变量。具体原因看源码组成:
Category源码:
Category
Category 是表示一个指向分类的结构体的指针,其定义如下:
typedef struct objc_category *Category;
struct objc_category {
char *category_name OBJC2_UNAVAILABLE; // 分类名
char *class_name OBJC2_UNAVAILABLE; // 分类所属的类名
struct objc_method_list *instance_methods OBJC2_UNAVAILABLE; // 实例方法列表
struct objc_method_list *class_methods OBJC2_UNAVAILABLE; // 类方法列表
struct objc_protocol_list *protocols OBJC2_UNAVAILABLE; // 分类所实现的协议列表
}
通过上面我们可以发现,这个结构体主要包含了分类定义的实例方法与类方法,其中instance_methods 列表是 objc_class 中方法列表的一个子集,而class_methods列表是元类方法列表的一个子集。
但这个结构体里面
根本没有属性列表,
根本没有属性列表,
根本没有属性列表。
那么问题来了:
为什么在分类中声明属性时,运行不会出错呢?
既然分类不让添加属性,那为什么我写了@property仍然还以编译通过呢?
接下来我们探究下分类不能添加属性的实质原因:
我们知道在一个类中用@property声明属性,编译器会自动帮我们生成_成员变量和setter/getter,但分类的指针结构体中,根本没有属性列表。所以在分类中用@property声明属性,既无法生成_成员变量也无法生成setter/getter。
因此结论是:我们可以用@property声明属性,编译和运行都会通过,只要不使用程序也不会崩溃。但如果调用了_成员变量和setter/getter方法,报错就在所难免了。
报错原因如下:
// programmer.nameWithoutSetterGetter = @"无setter/getter"; //调用setter,编译成功,运行报错为:(-[Programmer setNameWithSetterGetter:]: unrecognized selector sent to instance 0x7f9de358fd70')
// NSLog(@"%@",programmer.nameWithoutSetterGetter); //调用getter,编译成功,运行报错为-[Programmer setNameWithSetterGetter:]: unrecognized selector sent to instance 0x7fe22be11ea0'
// NSLog(@"%@",_nameWithoutSetterGetter); //这是调用_成员变量,错误提示为:(Use of undeclared identifier '_nameWithoutSetterGetter')
那接下来我们继续思考:
既然报错的根本原因是使用了系统没有生成的setter/getter方法,可不可以在手动添加setter/getter来避免崩溃,完成调用呢?
其实是可以的。由于OC是动态语言,方法真正的实现是通过runtime完成的,虽然系统不给我们生成setter/getter,但我们可以通过runtime手动添加setter/getter方法。那具体怎么实现呢?
代码实现如下:
按照这个思路,我们通过运行时手动添加这个方法。
#import <objc/runtime.h>
static NSString *nameWithSetterGetterKey = @"nameWithSetterGetterKey"; //定义一个key值
@implementation Programmer (Category)
//运行时实现setter方法
- (void)setNameWithSetterGetter:(NSString *)nameWithSetterGetter {
objc_setAssociatedObject(self, &nameWithSetterGetterKey, nameWithSetterGetter, OBJC_ASSOCIATION_COPY);
}
//运行时实现getter方法
- (NSString *)nameWithSetterGetter {
return objc_getAssociatedObject(self, &nameWithSetterGetterKey);
}
@end
实际使用效果
//通过runtime实现了setter/getter
programmer.nameWithSetterGetter = @"Baby"; //调用setter,成功
NSLog(@"%@",programmer.nameWithSetterGetter); //调用getter,成功
//NSLog(@"%@",_nameWithSetterGetter); //这是调用_成员变量,错误提示为:(Use of undeclared identifier '_nameWithSetterGetter')
问题解决。但是注意,以上代码仅仅是手动实现了setter/getter方法,但调用_成员变量依然报错。
类扩展(Class Extension)
Extension是Category的一个特例。类扩展与分类相比只少了分类的名称,所以称之为“匿名分类”。
其实开发当中,我们几乎天天在使用。对于有些人来说像是最熟悉的陌生人。
类扩展格式:
@interface XXX ()
//私有属性
//私有方法(如果不实现,编译时会报警,Method definition for 'XXX' not found)
@end
作用:
为一个类添加额外的原来没有变量,方法和属性
一般的类扩展写到.m文件中
一般的私有属性写到.m文件中的类扩展中
类别与类扩展的区别:
runtime解决无setter/getter的问题而已);②类扩展不仅可以增加方法,还可以增加实例变量(或者属性),只是该实例变量默认是@private类型的(
⑤定义在 .m 文件中的类扩展方法为私有的,定义在 .h 文件(头文件)中的类扩展方法为公有的。类扩展是在 .m 文件中声明私有方法的非常好的方式。
1.分类是用于给原有类添加方法的,因为分类的结构体指针中,没有属性列表,只有方法列表。所以< 原则上讲它只能添加方法, 不能添加属性(成员变量),实际上可以通过其它方式添加属性> ;
2.分类中的可以写@property, 但不会生成
setter/getter方法, 也不会生成实现以及私有的成员变量(编译时会报警告);3.可以在分类中访问原有类中.h中的属性;
4.如果分类中有和原有类同名的方法, 会优先调用分类中的方法, 就是说会忽略原有类的方法。所以同名方法调用的优先级为
分类 > 本类 > 父类。因此在开发中尽量不要覆盖原有类;
OC分类(类目/类别) 和 类扩展 - 全解析的更多相关文章
- OC学习篇之—写类别(类的扩展)
首先我们来看一下场景,如果我们现在想对一个类进行功能的扩充,我们该怎么做? 对于面向对象编程的话,首先会想到继承,但是继承有两个问题: 第一个问题:继承的前提是这个类可以被继承,因为在Java中有些类 ...
- Objective-C中的类目(Category),延展(Extension)
类目和延展的作用都是为了扩展一个类. Objective-C中的类目(Category) 一.类目的定义和作用 类目也叫分类,英文Category,在没有原类.m文件的基础上,给该类添加方法. 比如, ...
- ECshop网点程序优化-自动生成类目页Keywords、Desciption Meta
ECshop支持针对每个新建的类目自定义Keywords.Description Meta信息,好处就不用说了,帮助SEO或者让浏览者了解这是什么页面,但如果有几百个类目的时候,人工去写这些类目又有点 ...
- 微信小程序个人/企业开放服务类目一览表
微信小程序个人/企业开放服务类目一览表 微信小程序个人开放服务类目表 服务类目 类目分类一 类目分类二 引导描述 出行与交通 代驾 / / 生活服务 家政.丽人.摄影/扩印.婚庆服务.环保回收/废 ...
- Java生鲜电商平台-生鲜电商中商品类目、属性、品牌、单位架构设计与实战
Java生鲜电商平台-生鲜电商中商品类目.属性.品牌.单位架构设计与实战 说明:Java生鲜电商平台-生鲜电商中商品类目.属性.品牌.单位架构设计与实战经验分享 凡是涉及到购物,必然是建立在商品的基础 ...
- Object-C类目(Category)
类目是Object-C中最有用的一个特性.实质上,类目允许你为一个已存在的类添加一些方法而不用子类化该类,也不需要你了解该类的实现细节. 这是特别有用的,因为你可以给一个内建的对象添加方法.当你想在你 ...
- sell 项目 类目表 设计 及 创建
1.数据库设计 2.类目表 创建 /** * 类目表 */ create table `product_category` ( `category_id` int not null auto_incr ...
- OC语言中类目,延展,协议
一.类目 指向已知的类中添加新方法,不破坏封装性.已知类可以是自定义的类和系统的类. 1.类目的实现和声明 建一个学生类,并增加类目 (1).声明(是在Student+Working.h中) 必须引入 ...
- iOS中的分类(category)和类扩展(extension)
今天在研究swift的时候看到了分类和扩展.这是两个十分重要有用的功能,但是之前用的不多,没有深入了解过,在今天就从头理一遍. 一.分类(Category): 概念: 分类(Category)是OC中 ...
随机推荐
- python学习之老男孩python全栈第九期_day009知识点总结
'''# len# 计算字符串的长度# s = '金老板小护士'# len(s)# 不能用 len 怎么办#low一点的方法# count = 0# for i in s:# count += 1# ...
- jquery中 苹果手机对on触发的点击事件无效果
在被点击的元素上加上样式 cursor:pointer; 苹果手机就可以触发事件了
- CSS编辑元素的浮动
1.元素浮动: 1)使用 float:left; 这样的格式设置元素的浮动方式,属性值可以是left,right: 2)元素设置为左浮动时,元素将从原区域浮动到浏览器的左侧页面:右浮动时,就会附在右侧 ...
- wampserver的配置教程
对于初做PHP网站的朋友来说,第一步肯定是希望在自己电脑是搭建PHP环境,省去空间和上传的麻烦!但搭建环境也不是件容易的事情,特别是对于新手同学来说!因此在这里跟大家介绍我作为一名新手在使用的方便好用 ...
- Android 如何执行java命令
android的程序基于java开发,当我们接上调试器,执行adb shell,就可以执行linux命令,但是却并不能执行java命令. 那么在android的shell中是否就不能执行java程序了 ...
- 使用redis 处理高并发场景
1.原理: 当同一个用户获取锁之后,会让该用户一直持有锁.同样 的用户再次获取,会根据原子性 ,lock返回true. /** * 获取锁(非公平锁), 默认获取超时为2分钟 */ public bo ...
- LeetCode题解之 Binary Tree Preorder Traversal
1.题目描述 2.问题分析 利用递归. 3.代码 vector<int> preorderTraversal(TreeNode* root) { vector<int> v; ...
- C# System.IO.Path
Path的常用方法 函数列表 对一个路径做相应操作,包括文件路径,目录路径,通常会用到Path这个类, 本文列举一些常用的操作. 获取指定路径字符串的目录信息 public static string ...
- 虚拟机克隆linux centos 6.5 系统网卡配置
作为一个刚刚接触linux系统的小白来说,VMware虚拟机安装好CentOS6.5系统后,纯净的系统多克隆几份出来方便后期做试验.克隆步骤很简单,克隆后出现的问题是克隆后的网卡MAC地址和原系统MA ...
- [Redis_1] Redis 介绍 && 安装
0. 说明 Redis 介绍 && 安装 1. Redis 介绍 2. Redis 安装(Windows 10) [2.1 解压 redis-2.2.2-win32-win64.rar ...