亚洲免费av电影一区二区三区,日韩爱爱视频,51精品视频一区二区三区,91视频爱爱,日韩欧美在线播放视频,中文字幕少妇AV,亚洲电影中文字幕,久久久久亚洲av成人网址,久久综合视频网站,国产在线不卡免费播放

        ?

        基于Runtime的iOS編程研究與實(shí)現(xiàn)

        2021-03-09 00:46:02特日根
        關(guān)鍵詞:定義方法

        胡 坤, 特日根

        (長光衛(wèi)星技術(shù)有限公司 a. 數(shù)據(jù)中心; b. 吉林省衛(wèi)星遙感應(yīng)用技術(shù)重點(diǎn)實(shí)驗(yàn)室, 長春 130000)

        0 引 言

        iOS的編程語言objective-C是對(duì)C語言的擴(kuò)展, 加入了面向?qū)ο蠛拖鬟f機(jī)制, 其函數(shù)的調(diào)用過程是動(dòng)態(tài)的[1], 在編譯時(shí)并不能決定真正調(diào)用的函數(shù), 只有在真正運(yùn)行時(shí)才會(huì)根據(jù)函數(shù)的名稱找到對(duì)應(yīng)的函數(shù)實(shí)體進(jìn)行調(diào)用。而這個(gè)消息傳遞機(jī)制的核心是由C語言和匯編語言編寫的Runtime庫完成的[2], 它是objective-C語言面向?qū)ο蠛蛣?dòng)態(tài)機(jī)制的核心[3]。

        Runtime的核心是消息傳遞, 高級(jí)編程語言想要成為可執(zhí)行文件需要先編譯為匯編語言再匯編為機(jī)器語言。機(jī)器語言也是計(jì)算機(jī)能識(shí)別的唯一語言, 但objective-C并不能直接編譯為匯編語言, 而是要先轉(zhuǎn)寫為純C語言再進(jìn)行編譯和匯編操作, 從objective-C到C語言的過渡就是由Runtime實(shí)現(xiàn)的[4]。然而C語言是面向過程的編程語言, 而使用objective-C進(jìn)行面向?qū)ο箝_發(fā)時(shí), 就需要將面向?qū)ο蟮念愞D(zhuǎn)變?yōu)槊嫦蜻^程的結(jié)構(gòu)體實(shí)現(xiàn)[5]。

        筆者針對(duì)實(shí)際編程開發(fā)中無法修改系統(tǒng)方法的問題, 首先研究Runtime的主要API(Application Programming Interface)接口以及消息傳遞的本質(zhì), 然后使用恰當(dāng)?shù)慕涌诤瘮?shù)對(duì)系統(tǒng)方法動(dòng)態(tài)的修改和替換, 從而提高了開發(fā)效率, 降低程序運(yùn)行故障率。

        1 消息機(jī)制

        在objective-C語言中, 實(shí)例對(duì)象在調(diào)用方法時(shí), 編譯器在編譯階段不知道調(diào)用方法的具體位置, 只有在運(yùn)行時(shí), 才會(huì)向?qū)嵗龑?duì)象發(fā)送消息, 并通過遍歷實(shí)例對(duì)象進(jìn)行方法選擇。這種機(jī)制稱為消息機(jī)制。Runtime的特性主要是消息傳遞, 若消息在對(duì)象中找不到, 則進(jìn)行消息轉(zhuǎn)發(fā)。

        1.1 Runtime消息傳遞

        對(duì)一個(gè)對(duì)象的普通方法[object func], 編譯器會(huì)轉(zhuǎn)成消息并發(fā)送objc_msgSend(object,func), 其中objc_msgSend方法定義如下。

        定義1 OBJC_EXPORT id objc_msgSend(id self,SEL op,...)

        Runtime消息的傳遞過程為:

        1) 系統(tǒng)首先找到消息的接收對(duì)象, 然后通過對(duì)象的isa指針找到它的類;

        2) 在它的類中遍歷method_list, 尋找func方法;

        3) 若沒有func方法則查找父類的method_list;

        4) 找到對(duì)應(yīng)的method, 執(zhí)行其IMP;

        5) 轉(zhuǎn)發(fā)IMP的return值。

        以下6個(gè)小節(jié)為消息傳遞中的概念。

        1.1.1類對(duì)象(objc_class)

        Objective-C類是由Class類型表示的, 它實(shí)際上是一個(gè)指向objc_class結(jié)構(gòu)體的指針。其中objc_class方法定義如下。

        定義2 typedef struct objc_class*Class;

        struct objc_class {

        Class _Nonnull isa;

        #if!__OBJC2__

        Class_Nullable super_class;

        const char*_Nonnull name;

        long version;

        long info;

        long instance_size;

        struct objc_ivar_list*_Nullable ivars;

        struct objc_method_list*_Nullable*_Nullable methodLists;

        struct objc_cache*_Nonnull cache;

        struct objc_protocol_list*_Nullable protocols;

        #endif

        }

        該結(jié)構(gòu)體的第1個(gè)成員變量也是isa指針, 這說明了Class本身其實(shí)也是一個(gè)對(duì)象, 因此稱之為類對(duì)象。類對(duì)象在編譯期產(chǎn)生用于創(chuàng)建實(shí)例的對(duì)象, 在全局中只有一個(gè), 這個(gè)唯一的實(shí)例, 稱之為單例。

        1.1.2 實(shí)例(objc_object)

        類對(duì)象中存儲(chǔ)了諸多信息, 這些信息描述如何創(chuàng)建一個(gè)實(shí)例。類對(duì)象和類方法應(yīng)該從isa指針指向的結(jié)構(gòu)體創(chuàng)建, 類對(duì)象的isa指針的指向?qū)ο蠓Q之為元類(metaclass), 元類中保存了創(chuàng)建類對(duì)象以及類方法的所有信息。其中實(shí)例objc_object的方法定義如下。

        定義3 struct objc_object {

        Class isa OBJC_ISA_AVAILABILITY;

        };

        ypedef struct objc_object *id;

        1.1.3方法(objc_method)

        方法(Method)和人們平時(shí)理解的函數(shù)是一致的, 即表示能獨(dú)立完成一個(gè)功能的一段代碼。方法(Method)定義如下。

        定義4 typedef struct objc_method *Method;

        struct objc_method {

        SEL method_name

        char *method_types

        IMP method_imp

        }

        在這個(gè)結(jié)構(gòu)體中, SEL和IMP都是Method的屬性。

        1.1.4 SEL(objc_selector)

        方法選擇器SEL定義如下。

        定義5 typedef struct objc_selector *SEL;

        定義1中提及的消息發(fā)送函數(shù)objc_msgSend的第2個(gè)參數(shù)類型為SEL, 它是selector在Objective-C中的表示類型。selector是方法選擇器, 可以理解為區(qū)分方法的ID, 而這個(gè)ID的數(shù)據(jù)結(jié)構(gòu)是SEL, SEL定義為: @property SEL selector??梢钥吹絪elector是SEL的一個(gè)實(shí)例。

        1.1.5 IMP函數(shù)實(shí)現(xiàn)

        函數(shù)實(shí)現(xiàn)IMP定義如下。

        定義6 typedef id (*IMP)(id,SEL,...);

        #endif

        IMP就是指向程序內(nèi)存地址的指針。在iOS的Runtime中, Method通過selector和IMP兩個(gè)屬性, 實(shí)現(xiàn)了快速查詢方法及實(shí)現(xiàn), 相對(duì)提高了性能, 又保持了靈活性。

        1.1.6 Category(objc_category)

        Category是表示一個(gè)指向分類結(jié)構(gòu)體的指針, 其定義如下。

        定義7 struct category_t {

        const char*name;

        classref_t cls;

        struct method_list_t*instanceMethods;

        struct method_list_t*classMethods;

        struct protocol_list_t*protocols;

        struct property_list_t*instanceProperties;

        };

        從上面的category_t結(jié)構(gòu)體中可以看出, 分類中可以添加實(shí)例方法、 類方法, 甚至可以實(shí)現(xiàn)協(xié)議, 添加屬性, 但不可以添加成員變量。

        1.2 Runtime消息轉(zhuǎn)發(fā)

        當(dāng)有消息發(fā)送時(shí), 系統(tǒng)會(huì)在相關(guān)類對(duì)象的方法列表中搜索所需方法, 若找不到則會(huì)沿著繼承樹向上搜索, 若搜索到繼承樹根部(通常為NSObject)時(shí)仍未找到, 則此消息轉(zhuǎn)發(fā)失敗, 并通過執(zhí)行“doesNotRecognizeSelector:” 方法向系統(tǒng)報(bào)錯(cuò), 其錯(cuò)誤提示為“unrecognized selector”。

        1) 動(dòng)態(tài)方法解析(resolveInstanceMethod)。首先, objective-C運(yùn)行時(shí)會(huì)調(diào)用“+resolveInstanceMethod:”, 使之有機(jī)會(huì)提供一個(gè)函數(shù)實(shí)現(xiàn)。若添加了函數(shù)并返回YES, 系統(tǒng)就會(huì)重新啟動(dòng)一次消息發(fā)送過程。

        2) 備用接收者(備用receiver)。若方法“+resolveInstanceMethod:”返回為NO, 而且目標(biāo)對(duì)象實(shí)現(xiàn)了“-forwardingTargetForSelector:”方法, 此時(shí)Runtime就會(huì)調(diào)用這個(gè)方法, 并把這個(gè)消息轉(zhuǎn)發(fā)給備用接收者receiver。

        3) 完整消息轉(zhuǎn)發(fā)。若在上一步還不能處理未知消息, 則唯一能做的就是啟用完整的消息轉(zhuǎn)發(fā)機(jī)制。首先它會(huì)發(fā)送“-methodSignatureForSelector:”消息獲得函數(shù)的參數(shù)和返回值類型。若“-methodSignatureForSelector:”返回“nil” , Runtime則會(huì)發(fā)出“-doesNotRecognizeSelector:”消息, 此時(shí)程序執(zhí)行完畢。若返回了一個(gè)函數(shù)簽名(函數(shù)簽名就是函數(shù)的聲明信息, 包括參數(shù)、 返回值、 調(diào)用約定), Runtime就會(huì)創(chuàng)建一個(gè)NSInvocation對(duì)象并發(fā)送“-forwardInvocation:”消息給目標(biāo)對(duì)象。

        圖1 消息轉(zhuǎn)發(fā)流程Fig.1 Message forwarding process

        完整轉(zhuǎn)發(fā)的代碼實(shí)現(xiàn)以及運(yùn)行打印結(jié)果如圖2所示。

        圖2 消息轉(zhuǎn)發(fā)代碼及打印Fig.2 Message forwarding code and printing

        2 Runtime應(yīng)用

        2.1 KVO實(shí)現(xiàn)

        KVO(Key-Value Observing)是蘋果提供的一套事件通知機(jī)制。允許對(duì)象監(jiān)聽另一個(gè)對(duì)象特定屬性的改變, 并在改變時(shí)接收到事件。在經(jīng)典的MVC設(shè)計(jì)模式中, 經(jīng)常用于在Model和Controller之間進(jìn)行通訊。

        KVO實(shí)現(xiàn)依賴于Objective-C 的Runtime機(jī)制, 當(dāng)觀察某對(duì)象A時(shí), Runtime為對(duì)象A動(dòng)態(tài)創(chuàng)建一個(gè)子類, 并為這個(gè)新的子類重寫了被觀察屬性keyPath的setter方法。setter 方法隨后負(fù)責(zé)通知觀察對(duì)象屬性的改變狀況。

        Apple使用了isa-swizzling技術(shù)實(shí)現(xiàn)KVO 。當(dāng)觀察對(duì)象A時(shí), Runtime創(chuàng)建一個(gè)名為“NSKVONotifying_A”的新類, 該類繼承自對(duì)象A, 且Runtime為NSKVONotifying_A重寫觀察屬性的setter方法, NSKVONotifying_A的setter方法會(huì)負(fù)責(zé)在調(diào)用對(duì)象A的setter方法前后, 觀察對(duì)象A屬性值的更改情況[6-7]。

        在筆者參與的某項(xiàng)目中大量使用KVO監(jiān)聽變量屬性的改變, 其中監(jiān)聽鍵盤改變的代碼如下。

        //注冊(cè)鍵盤出現(xiàn)通知

        [[NSNotificationCenter defaultCenter] addObserver:selfselector:@selector(keyboardWillShow:) name:UIKeyboardWillShowNotification object:nil];

        //注冊(cè)鍵盤消失通知

        [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(keyboardWillHide:)

        name:UIKeyboardWillHideNotification object:nil];

        2.2 關(guān)聯(lián)對(duì)象(Objective-C Associated Objects)給分類增加屬性

        由于分類無法添加成員屬性, 但可通過Runtime的關(guān)聯(lián)對(duì)象進(jìn)行實(shí)現(xiàn)。Runtime的關(guān)聯(lián)對(duì)象提供了下面幾個(gè)接口。

        1) 關(guān)聯(lián)對(duì)象接口:

        void objc_setAssociatedObject(id object, const void *key, id value, objc_AssociationPolicy policy)

        2)獲取關(guān)聯(lián)對(duì)象接口:

        id objc_getAssociatedObject(id object, const void *key)

        3) 移除關(guān)聯(lián)對(duì)象接口:

        void objc_removeAssociatedObjects(id object)

        下例使用Runtime為UIView的分類動(dòng)態(tài)添加“defaultColor”的屬性。

        圖3 分類添加屬性代碼及打印Fig.3 Add attribute code and print for classification

        2.3 Method Swizzling方法的添加和替換

        該替換方案是通過“class_getInstanceMethod”函數(shù)獲取實(shí)例方法(實(shí)例方法是類的實(shí)例才能調(diào)用的方法)的實(shí)現(xiàn), 通過“class_getClassMethod”獲取類方法(類方法只能類本身進(jìn)行調(diào)用)實(shí)現(xiàn), 并最終通過“method_exchangeImplementations”方法對(duì)兩個(gè)函數(shù)的實(shí)現(xiàn)進(jìn)行調(diào)換[8]。

        2.3.1 適配舊項(xiàng)目的高系統(tǒng)版本圖片問題

        在項(xiàng)目開發(fā)中, 針對(duì)不同的iOS版本, 顯示的圖片常常不同。若項(xiàng)目中圖片的獲取方法為[UIImage imageNamed:@“test.png”], 而區(qū)分不用版本所需圖片的方法是在圖片的名稱后面加上后綴“_系統(tǒng)版本號(hào)”, 則項(xiàng)目中有n處圖片需要做版本適配, 同時(shí)再有m個(gè)版本需要做適配, 則需要新增n×m個(gè)圖片選擇的代碼[9-10]。通過Runtime的Method Swizzling方式將自己的方法和系統(tǒng)的“[UIImage imageNamed:]”方法進(jìn)行替換, 進(jìn)而減少代碼量, 降低代碼維護(hù)成本[11]。其代碼如下。

        #import〈objc/runtime.h〉

        +(void)load {

        Class class=[self class];

        //獲取想要替換方法實(shí)現(xiàn)

        Method originalMethod=

        class_getInstanceMethod(class,imageNamed:);

        //獲取自定義方法hkImageNamed實(shí)現(xiàn)

        Method swizzledMethod=

        class_getInstanceMethod(class,hkImageNamed:);

        //通過method_exchangeImplementations函數(shù)進(jìn)行IMP替換

        method_exchangeImplementations(originalMethod,

        swizzledMethod);

        }

        2.3.2 NSMutableArray中插入空串報(bào)錯(cuò)問題

        在NSMutableArray數(shù)組中, 經(jīng)常有插入空串報(bào)錯(cuò)情況, 編譯器報(bào)錯(cuò)為“[__NSSetM addObject:] object cannot be nil”。這個(gè)錯(cuò)誤是因?yàn)檎{(diào)用了NSMutableArray的“[arrayM addObject:nil]”方法造成的[12]。若依次在報(bào)錯(cuò)位置加上if-else語句進(jìn)行判斷, 將會(huì)增加代碼維護(hù)成本。針對(duì)該問題, 同樣可以用Runtime的Method Swizzling進(jìn)行方法替換, 代碼如下。

        #import 〈objc/runtime.h〉

        +(void)load {

        Class class=[self class];

        //獲取想要替換方法實(shí)現(xiàn)

        Method originalMethod=

        class_getInstanceMethod(class,addObject:);

        //獲取自定義方法hkAddObject:實(shí)現(xiàn)

        Method swizzledMethod=

        class_getInstanceMethod(class,hkAddObject:);

        //通過method_exchangeImplementations函數(shù)進(jìn)行IMP替換

        method_exchangeImplementations(originalMethod, swizzledMethod);

        }

        2.3.3 數(shù)組越界問題

        同樣, 數(shù)組越界問題也是項(xiàng)目中常見問題[13]。對(duì)數(shù)組[self.arrays objectAtIndex:int]和self.arrays[int], 若int的值大于arrays元素?cái)?shù)量self.arrays.count時(shí), 則會(huì)報(bào)iOS “reason: ***-[__NSArrayM objectAtIndex:]: index int beyond bounds [0..self.arrays.count-1]”錯(cuò)誤[14-15]。針對(duì)這樣問題, 同樣可以用Runtime的Method Swizzling進(jìn)行方法替換, 代碼如下。

        #import 〈objc/runtime.h〉

        +(void)load {

        Class class=[self class];

        //獲取想要替換方法實(shí)現(xiàn)

        Method originalMethod=

        class_getInstanceMethod(class,objectAtIndex:);

        //獲取自定義方法hkObjectAtIndex:實(shí)現(xiàn)

        Method swizzledMethod=

        class_getInstanceMethod(class,hkObjectAtIndex:);

        //通過method_exchangeImplementations函數(shù)進(jìn)行IMP替換

        method_exchangeImplementations(originalMethod, swizzledMethod);

        }

        3 結(jié) 語

        筆者首先對(duì)objective-C語言的運(yùn)行時(shí)機(jī)制Runtime進(jìn)行了系統(tǒng)剖析, 闡述了Runtime是一套基于C語言的底層API庫的集合, objective-C是消息機(jī)制, 其方法最終都轉(zhuǎn)化成消息的轉(zhuǎn)發(fā)過程, 并通過消息轉(zhuǎn)發(fā)流程圖, 完整地闡述了消息轉(zhuǎn)發(fā)全過程。

        然后, 通過對(duì)KVO機(jī)制分析, 給分類(category)增加屬性方法, 驗(yàn)證了Runtime在代碼運(yùn)行過程中起到的作用。并針對(duì)項(xiàng)目中常見問題, 提出了基于Runtime方式的解決方案, 使代碼更加精簡, 降低了代碼編碼和維護(hù)成本, 并證明了該方法可解決系統(tǒng)自帶功能不足的問題。

        猜你喜歡
        定義方法
        永遠(yuǎn)不要用“起點(diǎn)”定義自己
        海峽姐妹(2020年9期)2021-01-04 01:35:44
        定義“風(fēng)格”
        學(xué)習(xí)方法
        可能是方法不對(duì)
        用對(duì)方法才能瘦
        Coco薇(2016年2期)2016-03-22 02:42:52
        成功的定義
        山東青年(2016年1期)2016-02-28 14:25:25
        四大方法 教你不再“坐以待病”!
        Coco薇(2015年1期)2015-08-13 02:47:34
        賺錢方法
        捕魚
        修辭學(xué)的重大定義
        а√天堂资源8在线官网在线| 亚洲女同同性一区二区| 男女肉粗暴进来动态图| 欧美aaaaaa级午夜福利视频| 国产无套露脸| 亚洲中国美女精品久久久| 青青草视频在线观看色| а天堂中文最新一区二区三区| 亚洲色偷偷色噜噜狠狠99| 欧美综合自拍亚洲综合百度| 日本少妇熟女一区二区| 成人做受黄大片| 精品无码久久久久久久动漫| av黄片免费在线观看| 国产成人高清在线观看视频| 国产精品亚洲αv天堂无码| 久久亚洲精品成人| 亚洲午夜精品国产一区二区三区| 国产高清在线视频一区二区三区| 曰本无码人妻丰满熟妇啪啪| 欧美韩日亚洲影视在线视频| 91中文字幕精品一区二区| 亚洲码欧美码一区二区三区| 国产色秀视频在线播放| 99久久精品一区二区三区蜜臀| 久久精品国产自产对白一区| 朝鲜女人大白屁股ass孕交| 欧美在线三级艳情网站| 亚洲一区二区精品在线看| 美腿丝袜诱惑一区二区| 海角国精产品一区一区三区糖心 | 日韩一区二区av极品| 少妇aaa级久久久无码精品片| av一区无码不卡毛片| 亚洲精品中文字幕乱码3| 日本国产成人国产在线播放| 99亚洲精品久久久99| 丝袜美腿爆炒国产在线观看 | 国精产品一区一区三区| 内射中出无码护士在线| 91在线无码精品秘 入口九色十|