什麼是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
方法來移除關聯屬性。