iOS之category

category :有翻譯為分類,有翻譯為類別临燃,個人感覺這種翻譯多多少少有些誤導(dǎo),所以我就不翻譯了,直接喊英文


category 個人覺得是oc語法中的一大亮點

不破壞老得類的結(jié)構(gòu)去進行烙心,擴充方法膜廊,擴充屬性,也可以將龐大的類分門別類的存放

擴充方法比較常用也比較簡答淫茵,這邊主要講一下擴充屬性

還是用demo解釋爪瓜,效果圖如下:

效果圖.gif

** 調(diào)用方式:(這么簡單的調(diào)用是不是很爽?)**

#import "ViewController.h"
#import "UIViewController+Loading.h"

@interface ViewController ()

@end

@implementation ViewController

- (void)viewDidLoad {
    [super viewDidLoad];
    //一句話調(diào)用
    [self showLoading];
    //在不需要loading的時候
    //[self hideLoading];
}

- (void)didReceiveMemoryWarning {
    [super didReceiveMemoryWarning];
}

@end

實現(xiàn)
這里主要用到了運行時的概念匙瘪,首先我們創(chuàng)建UIViewController的category UIViewController+Loading

.h文件

#import <UIKit/UIKit.h>

@interface UIViewController (Loading)

- (void)showLoading;
- (void)hideLoading;

@end

.m文件

#import "UIViewController+Loading.h"
#import "UIImage+animatedGIF.h"
#import <objc/runtime.h>

const char *loadingViewKey = "loadingViewKey";

@interface UIViewController (Private)

@property (nonatomic, strong)UIImageView* loadingView;

@end

@implementation UIViewController (Loading)

- (UIImageView*)loadingView{
    UIImageView* view = objc_getAssociatedObject(self, &loadingViewKey);
    if (!view) {
        view = [[UIImageView alloc]initWithImage:[UIImage animatedImageWithAnimatedGIFData:[NSData dataWithContentsOfFile:[[NSBundle mainBundle] pathForResource:@"loading_img" ofType:@"gif"]]]];
        [view sizeToFit];
        view.center = self.view.center;
        self.loadingView = view;
    }
    return view;
}

- (void)setLoadingView:(UIImageView *)loadingView{
    objc_setAssociatedObject(self, &loadingViewKey, loadingView, OBJC_ASSOCIATION_RETAIN_NONATOMIC);
}

- (void)showLoading{
    [self.view addSubview:self.loadingView];
}

- (void)hideLoading{
    [self.loadingView removeFromSuperview];
}

@end

** 講解: **

  1. import <objc/runtime.h>(這個正在學(xué)習(xí)中铆铆,目前停留在會用)
  2. 在Loading這個category中新增loadingView屬性,重寫其get set方法辆苔,因為category是不允許新增屬性的算灸,但是我們知道.(點)語法無非也就是調(diào)用get set
  3. objc_getAssociatedObject (請看下圖官方文檔)


    objc_getAssociatedObject.png
  4. objc_setAssociatedObject


    objc_setAssociatedObject.png

object : 參數(shù)作為待擴展的對象實例
key: 作為該對象實例的屬性的鍵
value: 就是對象實例的屬性的值
policy: 作為關(guān)聯(lián)的策略

  1. 這個key一定要保持唯一,所以我們用常量指針地址作為key

下面是加載gif UIImageView的 category (gitHub上的)

#import <UIKit/UIKit.h>

/**
        UIImage (animatedGIF)
        
    This category adds class methods to `UIImage` to create an animated `UIImage` from an animated GIF.
*/
@interface UIImage (animatedGIF)

/*
        UIImage *animation = [UIImage animatedImageWithAnimatedGIFData:theData];
    
    I interpret `theData` as a GIF.  I create an animated `UIImage` using the source images in the GIF.
    
    The GIF stores a separate duration for each frame, in units of centiseconds (hundredths of a second).  However, a `UIImage` only has a single, total `duration` property, which is a floating-point number.
    
    To handle this mismatch, I add each source image (from the GIF) to `animation` a varying number of times to match the ratios between the frame durations in the GIF.
    
    For example, suppose the GIF contains three frames.  Frame 0 has duration 3.  Frame 1 has duration 9.  Frame 2 has duration 15.  I divide each duration by the greatest common denominator of all the durations, which is 3, and add each frame the resulting number of times.  Thus `animation` will contain frame 0 3/3 = 1 time, then frame 1 9/3 = 3 times, then frame 2 15/3 = 5 times.  I set `animation.duration` to (3+9+15)/100 = 0.27 seconds.
*/
+ (UIImage *)animatedImageWithAnimatedGIFData:(NSData *)theData;

/*
        UIImage *image = [UIImage animatedImageWithAnimatedGIFURL:theURL];
    
    I interpret the contents of `theURL` as a GIF.  I create an animated `UIImage` using the source images in the GIF.
    
    I operate exactly like `+[UIImage animatedImageWithAnimatedGIFData:]`, except that I read the data from `theURL`.  If `theURL` is not a `file:` URL, you probably want to call me on a background thread or GCD queue to avoid blocking the main thread.
*/
+ (UIImage *)animatedImageWithAnimatedGIFURL:(NSURL *)theURL;

@end
#import "UIImage+animatedGIF.h"
#import <ImageIO/ImageIO.h>

#if __has_feature(objc_arc)
#define toCF (__bridge CFTypeRef)
#define fromCF (__bridge id)
#else
#define toCF (CFTypeRef)
#define fromCF (id)
#endif

@implementation UIImage (animatedGIF)

static int delayCentisecondsForImageAtIndex(CGImageSourceRef const source, size_t const i) {
    int delayCentiseconds = 1;
    CFDictionaryRef const properties = CGImageSourceCopyPropertiesAtIndex(source, i, NULL);
    if (properties) {
        CFDictionaryRef const gifProperties = CFDictionaryGetValue(properties, kCGImagePropertyGIFDictionary);
        if (gifProperties) {
            NSNumber *number = fromCF CFDictionaryGetValue(gifProperties, kCGImagePropertyGIFUnclampedDelayTime);
            if (number == NULL || [number doubleValue] == 0) {
                number = fromCF CFDictionaryGetValue(gifProperties, kCGImagePropertyGIFDelayTime);
            }
            if ([number doubleValue] > 0) {
                // Even though the GIF stores the delay as an integer number of centiseconds, ImageIO “helpfully” converts that to seconds for us.
                delayCentiseconds = (int)lrint([number doubleValue] * 100);
            }
        }
        CFRelease(properties);
    }
    return delayCentiseconds;
}

static void createImagesAndDelays(CGImageSourceRef source, size_t count, CGImageRef imagesOut[count], int delayCentisecondsOut[count]) {
    for (size_t i = 0; i < count; ++i) {
        imagesOut[i] = CGImageSourceCreateImageAtIndex(source, i, NULL);
        delayCentisecondsOut[i] = delayCentisecondsForImageAtIndex(source, i);
    }
}

static int sum(size_t const count, int const *const values) {
    int theSum = 0;
    for (size_t i = 0; i < count; ++i) {
        theSum += values[i];
    }
    return theSum;
}

static int pairGCD(int a, int b) {
    if (a < b)
        return pairGCD(b, a);
    while (true) {
        int const r = a % b;
        if (r == 0)
            return b;
        a = b;
        b = r;
    }
}

static int vectorGCD(size_t const count, int const *const values) {
    int gcd = values[0];
    for (size_t i = 1; i < count; ++i) {
        // Note that after I process the first few elements of the vector, `gcd` will probably be smaller than any remaining element.  By passing the smaller value as the second argument to `pairGCD`, I avoid making it swap the arguments.
        gcd = pairGCD(values[i], gcd);
    }
    return gcd;
}

static NSArray *frameArray(size_t const count, CGImageRef const images[count], int const delayCentiseconds[count], int const totalDurationCentiseconds) {
    int const gcd = vectorGCD(count, delayCentiseconds);
    size_t const frameCount = totalDurationCentiseconds / gcd;
    UIImage *frames[frameCount];
    for (size_t i = 0, f = 0; i < count; ++i) {
        UIImage *const frame = [UIImage imageWithCGImage:images[i]];
        for (size_t j = delayCentiseconds[i] / gcd; j > 0; --j) {
            frames[f++] = frame;
        }
    }
    return [NSArray arrayWithObjects:frames count:frameCount];
}

static void releaseImages(size_t const count, CGImageRef const images[count]) {
    for (size_t i = 0; i < count; ++i) {
        CGImageRelease(images[i]);
    }
}

static UIImage *animatedImageWithAnimatedGIFImageSource(CGImageSourceRef const source) {
    size_t const count = CGImageSourceGetCount(source);
    CGImageRef images[count];
    int delayCentiseconds[count]; // in centiseconds
    createImagesAndDelays(source, count, images, delayCentiseconds);
    int const totalDurationCentiseconds = sum(count, delayCentiseconds);
    NSArray *const frames = frameArray(count, images, delayCentiseconds, totalDurationCentiseconds);
    UIImage *const animation = [UIImage animatedImageWithImages:frames duration:(NSTimeInterval)totalDurationCentiseconds / 100.0];
    releaseImages(count, images);
    return animation;
}

static UIImage *animatedImageWithAnimatedGIFReleasingImageSource(CGImageSourceRef CF_RELEASES_ARGUMENT source) {
    if (source) {
        UIImage *const image = animatedImageWithAnimatedGIFImageSource(source);
        CFRelease(source);
        return image;
    } else {
        return nil;
    }
}

+ (UIImage *)animatedImageWithAnimatedGIFData:(NSData *)data {
    return animatedImageWithAnimatedGIFReleasingImageSource(CGImageSourceCreateWithData(toCF data, NULL));
}

+ (UIImage *)animatedImageWithAnimatedGIFURL:(NSURL *)url {
    return animatedImageWithAnimatedGIFReleasingImageSource(CGImageSourceCreateWithURL(toCF url, NULL));
}

@end
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末驻啤,一起剝皮案震驚了整個濱河市菲驴,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌骑冗,老刑警劉巖赊瞬,帶你破解...
    沈念sama閱讀 222,000評論 6 515
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異贼涩,居然都是意外死亡巧涧,警方通過查閱死者的電腦和手機,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 94,745評論 3 399
  • 文/潘曉璐 我一進店門遥倦,熙熙樓的掌柜王于貴愁眉苦臉地迎上來谤绳,“玉大人,你說我怎么就攤上這事袒哥∷跎福” “怎么了?”我有些...
    開封第一講書人閱讀 168,561評論 0 360
  • 文/不壞的土叔 我叫張陵堡称,是天一觀的道長瞎抛。 經(jīng)常有香客問我,道長却紧,這世上最難降的妖魔是什么桐臊? 我笑而不...
    開封第一講書人閱讀 59,782評論 1 298
  • 正文 為了忘掉前任胎撤,我火速辦了婚禮,結(jié)果婚禮上断凶,老公的妹妹穿的比我還像新娘伤提。我一直安慰自己,他們只是感情好懒浮,可當我...
    茶點故事閱讀 68,798評論 6 397
  • 文/花漫 我一把揭開白布飘弧。 她就那樣靜靜地躺著,像睡著了一般砚著。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上痴昧,一...
    開封第一講書人閱讀 52,394評論 1 310
  • 那天稽穆,我揣著相機與錄音,去河邊找鬼赶撰。 笑死舌镶,一個胖子當著我的面吹牛,可吹牛的內(nèi)容都是我干的豪娜。 我是一名探鬼主播餐胀,決...
    沈念sama閱讀 40,952評論 3 421
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼瘤载!你這毒婦竟也來了否灾?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 39,852評論 0 276
  • 序言:老撾萬榮一對情侶失蹤鸣奔,失蹤者是張志新(化名)和其女友劉穎墨技,沒想到半個月后,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體挎狸,經(jīng)...
    沈念sama閱讀 46,409評論 1 318
  • 正文 獨居荒郊野嶺守林人離奇死亡扣汪,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 38,483評論 3 341
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了锨匆。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片崭别。...
    茶點故事閱讀 40,615評論 1 352
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖恐锣,靈堂內(nèi)的尸體忽然破棺而出茅主,到底是詐尸還是另有隱情,我是刑警寧澤侥蒙,帶...
    沈念sama閱讀 36,303評論 5 350
  • 正文 年R本政府宣布暗膜,位于F島的核電站,受9級特大地震影響鞭衩,放射性物質(zhì)發(fā)生泄漏学搜。R本人自食惡果不足惜娃善,卻給世界環(huán)境...
    茶點故事閱讀 41,979評論 3 334
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望瑞佩。 院中可真熱鬧聚磺,春花似錦、人聲如沸炬丸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,470評論 0 24
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽稠炬。三九已至焕阿,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間首启,已是汗流浹背暮屡。 一陣腳步聲響...
    開封第一講書人閱讀 33,571評論 1 272
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留毅桃,地道東北人褒纲。 一個月前我還...
    沈念sama閱讀 49,041評論 3 377
  • 正文 我出身青樓,卻偏偏與公主長得像钥飞,于是被迫代替她去往敵國和親莺掠。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 45,630評論 2 359

推薦閱讀更多精彩內(nèi)容

  • 轉(zhuǎn)至元數(shù)據(jù)結(jié)尾創(chuàng)建: 董瀟偉读宙,最新修改于: 十二月 23, 2016 轉(zhuǎn)至元數(shù)據(jù)起始第一章:isa和Class一....
    40c0490e5268閱讀 1,726評論 0 9
  • 引用: 無論一個類設(shè)計的多么完美彻秆,在未來的需求演進中,都有可能會碰到一些無法預(yù)測的情況论悴。那怎么擴展已有的類呢掖棉?一般...
    helloDolin閱讀 2,661評論 0 24
  • Category擴展,它是對一個類進行功能的擴展膀估。在項目的開發(fā)過程中幔亥,在不斷的迭代開發(fā)過程中,我們的類也不可避免的...
    未之閱讀 2,679評論 0 5
  • 七年前的夏天察纯,天氣格外的炎熱帕棉,高考失利的我,獨自一人踏上了北上的列車饼记,之所以選擇去東北上學(xué)香伴,就是想離家遠點,以...
    城南舊巷閱讀 134評論 0 0
  • 高鐵運行到立交的那一頭具则,你對我說即纲,勿念。 陽光很毒博肋,照在臉上火辣辣的疼低斋。我機械地點點頭蜂厅,直到你用力抱住了我。我說膊畴,...
    鹿蜀max閱讀 446評論 0 0