日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 编程资源 > 编程问答 >内容正文

编程问答

分类(category)的使用

發布時間:2023/12/18 编程问答 27 豆豆
生活随笔 收集整理的這篇文章主要介紹了 分类(category)的使用 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

首先可以查看下官方對于Category的介紹:Apple官方文檔解釋

個人理解:

  • Category初衷是用來給現有類添加或者重寫函數的,不可添加屬性,這個區別于Extension;
  • 將類的一些類似的功能拆分出去,使得每個分類中的函數功能統一,便于管理;
  • 給系統類(NSString,NSArray等)添加一些自定義的方法;
  • 重寫私有類的函數(公有、私有均可),改變內部的實現代碼。
  • 另類使用:

  • 因為Category是不具有屬性列表的,所以不能給Category添加屬性。那怎么通過@property給定義的Catogory添加屬性呢?接下來就詳細講解:
  • 定義一個NSObject的分類Worker,通過@property聲明兩個屬性如下:

    @interface NSObject (Worker) @property (nonatomic, copy) NSString *name; @property (nonatomic, copy) NSString *jodName; @end

    如果這時直接使用name和jodName如下:

    NSObject *worker = [[NSObject alloc] init]; worker.name = @"Jim"; worker.jodName = @"Software Engineer";

    command+R運行則會報如下錯誤:

    *** Terminating app due to uncaught exception 'NSInvalidArgumentException', reason: '-[NSObject setName:]: unrecognized selector sent to instance 0x600001a7c570'

    這是因為系統不會給Category添加屬性的setter/getter方法,所以這時就要用到runtime機制了,寫法如下:

    // name的key static NSString *keyName; // jobName的key static NSString *keyJobName;@implementation NSObject (Worker) - (void)setName:(NSString *)name {objc_setAssociatedObject(self, &keyName, name, OBJC_ASSOCIATION_COPY); } - (NSString *)name {return objc_getAssociatedObject(self, &keyName); } - (void)setJodName:(NSString *)jodName {objc_setAssociatedObject(self, &keyJobName, jodName, OBJC_ASSOCIATION_COPY); } - (NSString *)jodName {return objc_getAssociatedObject(self, &keyJobName); } @end

    setter 里面使用了一個 objc_setAssociatedObject(id _Nonnull object, const void * _Nonnull key, id _Nullable value, objc_AssociationPolicy policy) 方法,這個方法有四個參數,分別是:

  • 源對象(self)
  • 關聯時的用來標記是哪一個屬性的key (keyName,keyJobName)
  • 關聯的對象(name、jobName)
  • 一個關聯策略(OBJC_ASSOCIATION_COPY)
  • getter 里面用到了 objc_getAssociatedObject(id _Nonnull object, const void * _Nonnull key) 這個方法,這個方法有兩個參數,填寫方法參照setter方法。

    這時再運行代碼如下:

    NSObject *worker = [[NSObject alloc] init]; worker.name = @"Jim"; worker.jodName = @"Software Engineer"; NSLog(@"Hello, my name is %@, I am a %@ !",worker.name,worker.jodName);

    打印信息如下:

    2019-04-30 11:24:43.258099+0800 test[44759:2009573] Hello, my name is Jim, I am a Software Engineer !

    這樣就實現了在變相的在分類里添加了假屬性,注意:這種添加方式并不支持_name和_jobName的方式調用方式,因為真正意思上并沒有相關屬性

  • 重寫類的方法,并介紹如何調用原類方法:
  • 在分類中重寫了原類中的方法,在調用該方法時,程序是觸發的分類中重寫的方法,但有時我們還是需要用到原類中的方法,接下來就看下怎么用代碼實現:

    先生成一個Person類,實現一個方法:

    - (NSString *)getWorkerIntroducation {return @"該方法由分類實現的,返回的是這個人的介紹信息"; }

    在生成一個分類Person+Worker.h,重寫方法如下:

    - (NSString *)getWorkerIntroducation {if (self.name.length&&self.jodName.length) {return [NSString stringWithFormat:@"My name is %@, I am a %@!", self.name, self.jodName];} else {return nil;} }

    在調用如下:

    Person *worker = [[Person alloc] init]; worker.name = @"Jim"; worker.jodName = @"Software Engineer"; NSLog(@"%@", [worker getWorkerIntroducation]);

    打印日志:

    2019-04-30 15:59:05.812732+0800 test[46479:2079803] My name is Jim, I am a Software Engineer!

    根據分類的原理,結構體中存在一個對象方法列表,主要就是通過獲取方法的列表實現調用原類的方法,我們可以先打印一下對象方法列表:

    Person *worker = [[Person alloc] init]; worker.name = @"Jim"; worker.jodName = @"Software Engineer"; NSLog(@"%@", [worker getWorkerIntroducation]);// 打印對象方法列表 u_int count; Method *methods = class_copyMethodList([worker class], &count); for (int i=0; i<count; i++) {SEL sel = method_getName(methods[i]);NSString *methodName = [NSString stringWithCString:sel_getName(sel) encoding:NSUTF8StringEncoding];NSLog(@"%d = %@", i, methodName); } // 打印結果 // 調用分類重寫方法 My name is Jim, I am a Software Engineer! 0 = setJodName: // 分類的重寫方法 1 = getWorkerIntroducation // 原類的重寫方法 2 = getWorkerIntroducation 3 = jodName 4 = name 5 = setName:

    從打印信息來看分類的調用優先級是在前面的,所以需要遍歷獲取最后的方法再通過IMP調用,代碼如下:

    Person *worker = [[Person alloc] init]; worker.name = @"Jim"; worker.jodName = @"Software Engineer"; NSLog(@"%@", [worker getWorkerIntroducation]);// 打印對象方法列表 u_int count; Method *methods = class_copyMethodList([worker class], &count); int index = -1; for (int i=0; i<count; i++) {SEL sel = method_getName(methods[i]);NSString *methodName = [NSString stringWithCString:sel_getName(sel) encoding:NSUTF8StringEncoding];if ([methodName isEqualToString:@"getWorkerIntroducation"]) {index = i;} } if (index>=0) {// 通過index獲取原類的方法SEL sel = method_getName(methods[index]);IMP imp = method_getImplementation(methods[index]);NSString *string = ((id (*)(id, SEL)) imp)(self, sel);NSLog(@"%@",string); } else {NSLog(@"不存在該方法"); }

    打印結果:

    // 執行的分類方法 2019-04-30 17:21:50.688067+0800 test[49470:2130743] My name is Jim, I am a Software Engineer! // 執行的原類方法 2019-04-30 17:21:50.688238+0800 test[49470:2130743] 該方法由分類實現的,返回的是這個人的介紹信息

    代碼傳送門:

    • 樣例代碼

    傳送門:

    • IMP原理介紹
    • Category底層實現原理小記

    總結

    以上是生活随笔為你收集整理的分类(category)的使用的全部內容,希望文章能夠幫你解決所遇到的問題。

    如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。