什么是Category?
分类是Objective-C 2.0
之后新加的语言特性,一个特殊类,主要有一下作用:
- 在不改变原有类的前提下为已存在的类扩展新方法
- 拆分类文件做功能分类,便于管理
- 引用即用
- 有多继承的效果
分类不能添加成员变量,可以添加属性,但是系统不会生成属性对应的getter、setter
方法,因此属性是无效的,需要我们手动添加getter、setter
方法,通过关联属性达到扩展属性的效果。
什么是Extension?
Extension
一般称为扩展、延展、匿名分类,应用中不仅能够声明方法,还能声明属性,成员变量。
分类应用
1、NSDate
#pragma mark - 格式化时间
+(NSString*)dateFormat:(NSString*)format seconds:(NSTimeInterval)secs{
NSDateFormatter *formatter = [[NSDateFormatter alloc] init];
[formatter setTimeZone:[NSTimeZone timeZoneForSecondsFromGMT:0]];
[formatter setTimeZone:nil];
[formatter setDateFormat:format];
NSString *timeStr = [formatter stringFromDate:[NSDate dateWithTimeIntervalSince1970:secs]];
return timeStr;
}
#pragma mark - 将字符串转换成时间戳
+(NSTimeInterval)getTimeIntervalFormat:(NSString *)format TimeString:(NSString *)timeString{
NSDateFormatter *formatter = [[NSDateFormatter alloc] init];
[formatter setDateFormat:format];
NSDate *date = [formatter dateFromString:timeString];
NSInteger timeSp = [[NSNumber numberWithDouble:[date timeIntervalSince1970]] integerValue];
return timeSp;
}
#pragma mark - 根据时间戳判断这一天是周几
+(NSString*)getWeekSeconds:(NSTimeInterval)secs{
NSDateFormatter *formatter = [[NSDateFormatter alloc] init];
[formatter setTimeZone:[NSTimeZone timeZoneForSecondsFromGMT:0]];
[formatter setTimeZone:nil];
[formatter setDateFormat:@"EEEE"];
NSString *timeStr = [formatter stringFromDate:[NSDate dateWithTimeIntervalSince1970:secs]];
return timeStr;
}
2、NSString
#pragma mark - 判断字符串是否为空
+ (BOOL)isEmptyString:(NSString*)string{
if ([string isKindOfClass:[NSNull class]]) {
return YES ;
}
if([string isKindOfClass:[NSNumber class]]){
string = [NSString stringWithFormat:@"%@%@",string,@""];
}
if (string == nil) {
return YES ;
}
if (string.length == 0 ||
[string stringByTrimmingCharactersInSet:[NSCharacterSet whitespaceCharacterSet]].length == 0){
return YES ;
}
return NO ;
}
#pragma mark - json格式转换为json字符串
+(NSString *)jsonToJsonString:(id)obj{
NSError *error = nil;
NSData *jsonData = [NSJSONSerialization
dataWithJSONObject:obj options:NSJSONWritingPrettyPrinted error:&error];
NSString *jsonString = [[NSString alloc] initWithData:jsonData encoding:NSUTF8StringEncoding];
jsonString = [jsonString stringByReplacingOccurrencesOfString:@"\n" withString:@""];
jsonString = [jsonString stringByReplacingOccurrencesOfString:@" " withString:@""];
return jsonString;
}
#pragma mark- 获取字符串字节数
+(NSUInteger)textByteLength: (NSString *) text{
NSUInteger asciiLength = 0;
for (NSUInteger i = 0; i < text.length; i++) {
unichar uc = [text characterAtIndex: I];
asciiLength += isascii(uc) ? 1 : 2;
}
NSUInteger unicodeLength = asciiLength;
return unicodeLength;
}
#pragma mark - json字符串转换为json
-(id)jsonStringToJson{
NSData *data = [self dataUsingEncoding:NSUTF8StringEncoding];
id obj = [NSJSONSerialization JSONObjectWithData:data options:NSJSONReadingMutableLeaves error:nil];
return obj;
}
还有加密、手机号验证、身份证验证等等。
3、UIViewController
+ (void)load {
//我们只有在开发的时候才需要查看哪个viewController将出现
//所以在release模式下就没必要进行方法的交换
#ifdef DEBUG
//原本的viewWillAppear方法
Method viewWillAppear = class_getInstanceMethod(self, @selector(viewWillAppear:));
//需要替换成 能够输出日志的viewWillAppear
Method logViewWillAppear = class_getInstanceMethod(self, @selector(logViewWillAppear:));
//两方法进行交换
method_exchangeImplementations(viewWillAppear, logViewWillAppear);
Method viewWillDisappear = class_getInstanceMethod(self,@selector(viewWillDisappear:));
Method logViewWillDisappear = class_getInstanceMethod(self,@selector(logViewWillDisappear:));
method_exchangeImplementations(viewWillDisappear,logViewWillDisappear);
#endif
}
-(void)logViewWillDisappear:(BOOL)animated{
NSString *className = NSStringFromClass([self class]);
//在这里,你可以进行过滤操作,指定哪些viewController需要打印,哪些不需要打印
if ([className hasPrefix:@"UI"] == NO) {
NSLog(@"%@ will disappear",className);
}
//下面方法的调用,其实是调用viewWillAppear
[self logViewWillDisappear:animated];
}
- (void)logViewWillAppear:(BOOL)animated {
NSString *className = NSStringFromClass([self class]);
//在这里,你可以进行过滤操作,指定哪些viewController需要打印,哪些不需要打印
if ([className hasPrefix:@"UI"] == NO) {
NSLog(@"%@ will appear",className);
}
//下面方法的调用,其实是调用viewWillAppear
[self logViewWillAppear:animated];
}
- 实现了
+load
方法,该方法在main
方法之前调用,只调用一次,一般先执行原有类的+load
方法再执行分类的+load
方法 - 交换函数指针,以便于调用时走分类扩展的方法
等等,还有很多常用扩展,根据具体需求来扩展一个不影响原有类的实列方法或类方法。
Category源码分析
创建一个Person
类的分类,并添加一个属性,一个方法。如下:
@interface Person (Test)
@property (nonatomic,strong) NSString *hibo_name;
-(void)hb_eat;
@end
终端进入类文件所在位置,通过命令将分类编译成C++
形式。如下:
clang -rewrite-objc Person+Test.m
打开文件找到_category_t
如下:
struct _category_t {
const char *name;
struct _class_t *cls;
const struct _method_list_t *instance_methods;
const struct _method_list_t *class_methods;
const struct _protocol_list_t *protocols;
const struct _prop_list_t *properties;
};
以结构体的形式存在的,存放方法列表、协议列表、属性列表。
-
name:
类名 -
classref_t:
类对象,指向所属的类,编译时期没有值,在runtime
加载的时候指向对应的类 -
instance_methods:
类扩展的实列方法列表 -
class_methods:
类扩展的类方法列表 -
protocol_list_t:
分类所实现的协议列表 -
_prop_list_t:
属性列表
有相关结构体就一定有对结构体赋值,继续查找如下:
static struct _category_t _OBJC_$_CATEGORY_Person_$_Test __attribute__ ((used, section ("__DATA,__objc_const"))) =
{
"Person",
0, // &OBJC_CLASS_$_Person,
(const struct _method_list_t *)&_OBJC_$_CATEGORY_INSTANCE_METHODS_Person_$_Test,
0,
0,
(const struct _prop_list_t *)&_OBJC_$_PROP_LIST_Person_$_Test,
};
这里对上面声明的结构体进行初始化赋值。注意只有方法被实现后才会赋值到结构体属性。
继续查找发现,下面还定义了_category_t
类型的结构体变量。如下:
static struct _category_t *L_OBJC_LABEL_CATEGORY_$ [1] __attribute__((used, section ("__DATA, __objc_catlist,regular,no_dead_strip")))= {
&_OBJC_$_CATEGORY_Person_$_Test,
};
是一个数组类型的变量,将上面的_category_t
对象地址存储在了__DATA
数据段中的__objc_catlist
中的section
段中。
Category是如何被加载的?
1、dyld
是苹果的动态加载器,用来加载二进制文件;
2、应用启动时,系统内核先加载dyld
,dyld
再将应用中所依赖的各种库加载到内存空间中,而这些工作是在main函数
中执行完成的;
3、_objc_init
是Object-C runtime
的入口函数,读取Mach-O文件
,OC
对应的segment section
,并根据其中的数据代码信息,完成为OC
的内存布局,以及初始化runtime
相关的数据结构。
通过符号断点探索,下符号断点,点击断点 -> 点击加号,添加_objc_init
编译运行:
_dyld_start
在这里插入图片描述
1、_objc_init函数
void _objc_init(void)
{
static bool initialized = false;
if (initialized) return;
initialized = true;
// fixme defer initialization until an objc-using image is found?
environ_init();
tls_init();
static_init();
lock_init();
exception_init();
_dyld_objc_notify_register(&map_images, load_images, unmap_image);
}
-
_dyld_objc_notify_register:
会注册三个回调函数 -
map_images:``dyld
将image
加载到内存时调用 -
load_images:``dyld
初始化image
,load
方法都在此时调用 -
unmap_image:
将image
移除内存时调用
2、map_images
map_images(unsigned count, const char * const paths[],
const struct mach_header * const mhdrs[])
{
mutex_locker_t lock(runtimeLock);
return map_images_nolock(count, paths, mhdrs);
}
3、map_images_nolock在objc-os.mm中
map_images_nolock(unsigned mhCount, const char * const mhPaths[],
const struct mach_header * const mhdrs[])
{
if (hCount > 0) {
_read_images(hList, hCount, totalClasses, unoptimizedTotalClasses);
}
firstTime = NO;
}
-
_read_images
:读取oc
相关的section
4、_read_images在objc-runtime-new.mm中
当前方法内开始遍历头文件,看分类中的
for (EACH_HEADER) {
category_t **catlist =
_getObjc2CategoryList(hi, &count);
bool hasClassProperties = hi->info()->hasCategoryClassProperties();
for (i = 0; i < count; i++) {
category_t *cat = catlist[I];
Class cls = remapClass(cat->cls);
if (!cls) {
// Category's target class is missing (probably weak-linked).
// Disavow any knowledge of this category.
catlist[i] = nil;
if (PrintConnecting) {
_objc_inform("CLASS: IGNORING category \?\?\?(%s) %p with "
"missing weak-linked target class",
cat->name, cat);
}
continue;
}
// Process this category.
// First, register the category with its target class.
// Then, rebuild the class's method lists (etc) if
// the class is realized.
bool classExists = NO;
if (cat->instanceMethods || cat->protocols
|| cat->instanceProperties)
{
addUnattachedCategoryForClass(cat, cls, hi);
if (cls->isRealized()) {
remethodizeClass(cls);
classExists = YES;
}
if (PrintConnecting) {
_objc_inform("CLASS: found category -%s(%s) %s",
cls->nameForLogging(), cat->name,
classExists ? "on existing class" : "");
}
}
if (cat->classMethods || cat->protocols
|| (hasClassProperties && cat->_classProperties))
{
addUnattachedCategoryForClass(cat, cls->ISA(), hi);
if (cls->ISA()->isRealized()) {
remethodizeClass(cls->ISA());
}
if (PrintConnecting) {
_objc_inform("CLASS: found category +%s(%s)",
cls->nameForLogging(), cat->name);
}
}
}
}
- 遍历分类的头文件
_getObjc2CategoryList:GETSECT(_getObjc2CategoryList, category_t *, "__objc_catlist")
-
addUnattachedCategoryForClass:
将当前这个分类加载到这个类当中 - 注册实例方法到目标类
- 注册类方法到目标类
-
remethod
对原有类的分布重新布局
5、addUnattachedCategoryForClass在objc-runtime-new.mm中
static void addUnattachedCategoryForClass(category_t *cat, Class cls,
header_info *catHeader)
{
runtimeLock.assertLocked();
// DO NOT use cat->cls! cls may be cat->cls->isa instead
NXMapTable *cats = unattachedCategories();
category_list *list;
list = (category_list *)NXMapGet(cats, cls);
if (!list) {
list = (category_list *)
calloc(sizeof(*list) + sizeof(list->list[0]), 1);
} else {
list = (category_list *)
realloc(list, sizeof(*list) + sizeof(list->list[0]) * (list->count + 1));
}
list->list[list->count++] = (locstamped_category_t){cat, catHeader};
NXMapInsert(cats, cls, list);
}
-
NXMapInsert(cats, cls, list):
将分类和类建立关联
6、remethodizeClass-重新布局
static void remethodizeClass(Class cls)
{
category_list *cats;
bool isMeta;
runtimeLock.assertLocked();
isMeta = cls->isMetaClass();
// Re-methodizing: check for more categories
if ((cats = unattachedCategoriesForClass(cls, false/*not realizing*/))) {
if (PrintConnecting) {
_objc_inform("CLASS: attaching categories to class '%s' %s",
cls->nameForLogging(), isMeta ? "(meta)" : "");
}
attachCategories(cls, cats, true /*flush caches*/);
free(cats);
}
}
7、attachCategories
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;
}
}
auto rw = cls->data();
prepareMethodLists(cls, mlists, mcount, NO, fromBundle);
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);
}
- 先声明内存大小
-
method_list_t:
二维数组 -
perpareMethodLists:
准备方法列表 -
attachLists:
添加方法列表、属性列表、协议列表
void attachLists(List* const * addedLists, uint32_t addedCount) {
if (addedCount == 0) return;
if (hasArray()) {
// many lists -> many lists
uint32_t oldCount = array()->count;
uint32_t newCount = oldCount + addedCount;
setArray((array_t *)realloc(array(), array_t::byteSize(newCount)));
array()->count = newCount;
memmove(array()->lists + addedCount, array()->lists,
oldCount * sizeof(array()->lists[0]));
memcpy(array()->lists, addedLists,
addedCount * sizeof(array()->lists[0]));
}
else if (!list && addedCount == 1) {
// 0 lists -> 1 list
list = addedLists[0];
}
else {
// 1 list -> many lists
List* oldList = list;
uint32_t oldCount = oldList ? 1 : 0;
uint32_t newCount = oldCount + addedCount;
setArray((array_t *)malloc(array_t::byteSize(newCount)));
array()->count = newCount;
if (oldList) array()->lists[addedCount] = oldList;
memcpy(array()->lists, addedLists,
addedCount * sizeof(array()->lists[0]));
}
}
-
oldCount:
获取原有类的方法(或属性、协议)列表大小 -
newCount:
加上分类中添加的方法列表大小得到新的大小 -
memmove:
拷贝字节,如果目标区域和源区域有重叠的话,能够保证源串在被覆盖之前将重叠区域的字节拷贝到目标区域中,复制后源内容会被更改。没有重叠和memcpy
一样 -
memcpy:
拷贝,在发生内存重叠的时候有可能成功有可能失败 - 将源方法列表或属性协议追加到新的方法列表等上,在获取方法列表新的方法会首先命中
8、perpareMethodLists
prepareMethodLists(Class cls, method_list_t **addedLists, int addedCount,
bool baseMethods, bool methodsFromBundle)
{
runtimeLock.assertLocked();
if (addedCount == 0) return;
// Don't scan redundantly
bool scanForCustomRR = !cls->hasCustomRR();
bool scanForCustomAWZ = !cls->hasCustomAWZ();
// There exist RR/AWZ special cases for some class's base methods.
// But this code should never need to scan base methods for RR/AWZ:
// default RR/AWZ cannot be set before setInitialized().
// Therefore we need not handle any special cases here.
if (baseMethods) {
assert(!scanForCustomRR && !scanForCustomAWZ);
}
// Add method lists to array.
// Reallocate un-fixed method lists.
// The new methods are PREPENDED to the method list array.
for (int i = 0; i < addedCount; i++) {
method_list_t *mlist = addedLists[I];
assert(mlist);
// Fixup selectors if necessary
if (!mlist->isFixedUp()) {
fixupMethodList(mlist, methodsFromBundle, true/*sort*/);
}
// Scan for method implementations tracked by the class's flags
if (scanForCustomRR && methodListImplementsRR(mlist)) {
cls->setHasCustomRR();
scanForCustomRR = false;
}
if (scanForCustomAWZ && methodListImplementsAWZ(mlist)) {
cls->setHasCustomAWZ();
scanForCustomAWZ = false;
}
}
}
-
fixupMethodList:
修复方法列表
关联对象的探索
在分类中添加属性,并使用关联对象来实现值的存取。
//获取关联对象的值
id objc_getAssociatedObject(id object, const void *key) {
return _object_get_associative_reference(object, (void *)key);
}
//设置关联对象的值
void objc_setAssociatedObject(id object, const void *key, id value, objc_AssociationPolicy policy) {
_object_set_associative_reference(object, (void *)key, value, policy);
}
//移除关联属性
void objc_removeAssociatedObjects(id object)
{
if (object && object->hasAssociatedObjects()) {
_object_remove_assocations(object);
}
}
-
object:
绑定到的对象 -
key:
设置绑定key
,以便通过key
获取绑定值 -
value:
绑定值 -
policy:
绑定策略
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_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 |
1、_objc_set_associative_reference
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);
id new_value = value ? acquireValue(value, policy) : nil;
{
AssociationsManager manager;
//初始化hashMap
AssociationsHashMap &associations(manager.associations());
//当前对象的地址按位取反(key)
disguised_ptr_t disguised_object = DISGUISE(object);
if (new_value) {
// break any existing association.
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 {
// 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);
}
-
acquireValue:
根据内存管理语义进行内存管理 -
ObjcAssociation:
该对象用来存储关联属性的属性值,两个属性一个存储策略,一个属性值 -
AssociationsManager:
管理关ObjcAssociationHashMap
对象 -
ObjectAssociationMap:
关联对象存储属性值的数据结构,key
为用户自定义的字符串,value
为属性值
acquireValue:
static id acquireValue(id value, uintptr_t policy) {
switch (policy & 0xFF) {
case OBJC_ASSOCIATION_SETTER_RETAIN:
return objc_retain(value);
case OBJC_ASSOCIATION_SETTER_COPY:
return ((id(*)(id, SEL))objc_msgSend)(value, SEL_copy);
}
return value;
}
- 根据配置项,对value操作
AssociationsManager:
class AssociationsManager {
// associative references: object pointer -> PtrPtrHashMap.
static AssociationsHashMap *_map;
public:
AssociationsManager() { AssociationsManagerLock.lock(); }
~AssociationsManager() { AssociationsManagerLock.unlock(); }
AssociationsHashMap &associations() {
if (_map == NULL)
_map = new AssociationsHashMap();
return *_map;
}
};
AssociationsManager
管理着AssociationsHashMap
这个类,在该类中提供了对关联对象的属性添加、删除、查找的函数,并且访问是安全的。
总结如下:
-
AssociationsHashMap:key
为对象地址按位取反value
为ObjectAssociationMap
-
ObjectAssociationMap:key
为用户自定义字符串value
为ObjcAssociation
对象 -
ObjcAssociation:
包含两个属性,policy
存储策略,value
为关联属性的值 AssociationsManager -> AssociationsHashMap -> ObjcAssociation -> value
2、_object_get_associative_reference
通过对象地址和设置的key
获取关联属性值:
id _object_get_associative_reference(id object, void *key) {
id value = nil;
uintptr_t policy = OBJC_ASSOCIATION_ASSIGN;
{
AssociationsManager manager;
AssociationsHashMap &associations(manager.associations());
disguised_ptr_t disguised_object = DISGUISE(object);
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()) {
ObjcAssociation &entry = j->second;
value = entry.value();
policy = entry.policy();
if (policy & OBJC_ASSOCIATION_GETTER_RETAIN) {
objc_retain(value);
}
}
}
}
if (value && (policy & OBJC_ASSOCIATION_GETTER_AUTORELEASE)) {
objc_autorelease(value);
}
return value;
}
同存属性值一样,通过mananger
来初始化AssociationsHashMap
,通过对象地址指针取反作为key来获取ObjectAssociationMap
对象,在通过用户自定义key
(字符串)在ObjectAssociationMap
对象中获取ObjcAssociation
对象,该对象存在两个属性,一个为属性值存储策略,另一个为存储的属性值。
3、_object_remove_assocations
移除对象的关联对象
void _object_remove_assocations(id object) {
vector< ObjcAssociation,ObjcAllocator<ObjcAssociation> > elements;
{
AssociationsManager manager;
AssociationsHashMap &associations(manager.associations());
if (associations.size() == 0) return;
disguised_ptr_t disguised_object = DISGUISE(object);
AssociationsHashMap::iterator i = associations.find(disguised_object);
if (i != associations.end()) {
// copy all of the associations that need to be removed.
ObjectAssociationMap *refs = i->second;
for (ObjectAssociationMap::iterator j = refs->begin(), end = refs->end(); j != end; ++j) {
elements.push_back(j->second);
}
// remove the secondary table.
delete refs;
associations.erase(i);
}
}
// the calls to releaseValue() happen outside of the lock.
for_each(elements.begin(), elements.end(), ReleaseValue());
}
该方法不需要我们手动调用,系统在清理对象的时候会调用,及时清理关联属性。可以在objc-750
源码中搜索dealloc
来验证一下是否调用过该方法。如下:
- (void)dealloc {
_objc_rootDealloc(self);
}
* 内部调用了_objc_rootDealloc来清理内存
_objc_rootDealloc(id obj)
{
assert(obj);
obj->rootDealloc();
}
继续追踪:
objc_object::rootDealloc()
{
if (isTaggedPointer()) return; // fixme necessary?
if (fastpath(isa.nonpointer &&
!isa.weakly_referenced &&
!isa.has_assoc &&
!isa.has_cxx_dtor &&
!isa.has_sidetable_rc))
{
assert(!sidetable_present());
free(this);
}
else {
object_dispose((id)this);
}
}
- 对isa指针做了判断,判断是否存在一些关联内容,如果都不存在直接释放当前对象,判断是否有弱引用对象、关联对象
- 如果存在继续调用
object_dispose
object_dispose(id obj)
{
if (!obj) return nil;
objc_destructInstance(obj);
free(obj);
return nil;
}
* objc_destructInstance:销毁实例对象
void *objc_destructInstance(id obj)
{
if (obj) {
// Read all of the flags at once for performance.
bool cxx = obj->hasCxxDtor();
bool assoc = obj->hasAssociatedObjects();
// This order is important.
if (cxx) object_cxxDestruct(obj);
if (assoc) _object_remove_assocations(obj);
obj->clearDeallocating();
}
return obj;
}
hasAssociatedObjects
判断是否存在关联属性,如果有就移除。在这里就调用了_object_remove_assocations
方法来移除关联属性。
网友评论