1. 程式人生 > >iOS程式設計基礎-OC(七)-執行時系統(續)

iOS程式設計基礎-OC(七)-執行時系統(續)

第7章 執行時系統


 7.4 動態繫結

     動態繫結(dynamic binding):

        是指在執行程式時(而不是在編譯時)將訊息與方法對應起來的處理過程;

     在執行程式和傳送訊息前,訊息和接收訊息的物件不會對應;

        因為許多接收器物件可能會實現相同的方法,呼叫方法的方式會動態變化;

        也因此,是動態繫結實現了OOP的多型性;

     使用動態繫結可以在不影響既有程式碼的情況下,將新物件和程式碼 連線或新增到系統中,從而降低物件之前的耦合度;

     動態繫結應用於使用了動態型別的情況;

     例如我們有如下的程式碼:(我們使用第三章的示例類)

  id atom = [[C3Hydrogen alloc]initWithNeutrons:2];
    [atom logInfo];

     分析:

        在執行這段程式碼時,執行時系統會確定變數atom的實際型別(通過動態型別:執行時系統會在執行時確定動態型別的實際型別);

        然後通過動態繫結,使用訊息選擇器(loginfo)將該訊息與接收器(atom物件)的例項方法對應起來;

      在本例中:

        變數atom的型別被設定為C3Hydrogen *,因此執行時系統會搜尋C3Hydrogen類的例項方法loginfo;

        如果找不到,則會在C3Hydrogen類的父類中繼續尋找;

        執行時系統會一直在類層次結構中尋找該例項方法,直到找到為止;

      訊息[atom loginfo]

            |

            |____>OC執行時系統進行如下處理:

                    1)確定訊息接收器型別(動態型別)

                    2)確定實現方法(動態繫結);注意:無論是動態型別還是動態繫結都是一種功能,可別認為動態型別只是一種型別;

                    3)明確了型別,找到了方法,剩下的就是呼叫方法了;

      動態繫結是OC的一種繼承特性:

        不需要任何特定的API;

        使用動態繫結甚至可以將訊息選擇器設定為在執行程式時確定的變數;

     7.5 動態方法決議

      使用動態方法決議能夠以動態方式實現方法;

        使用OC中的@dynamic指令,可以告知編譯器與屬性關聯的方法會以動態的方式實現;

        @dynamic propertyName;//表示編譯器須動態地生成該屬性對應的方法

        CoreData框架使用@dynamic指令,為管理物件類生成高效屬性訪問器方法和關係訪問器方法;

      NSObject類中含有以下兩個方法:

        resolveInstanceMethod:

        resolveClassMethod:

        它們能夠以動態方式分別為指定的例項和類方法選擇器提供實現程式碼;

        你可以重寫這些方法,以動態方式實現例項/類方法;

      接下來看看這些方法的使用,來以動態方式為選擇器實現方法;

      以動態方式實現方法

      接下來我們通過更新C7Calculator來以動態方式實現方法,來展示動態方法決議;

        通過重寫resolveInstanceMethod:類方法更新類的實現程式碼;

        C7Calculator.m中的新增程式碼如下:

      (Code1)      

//
//  C7Calculator.m
//  精通Objective-C
//
//  Created by 花強 on 2017/11/28.
//  Copyright © 2017年 花強. All rights reserved.
//

#import <objc/runtime.h>
#import "C7Calculator.h"

@implementation C7Calculator

-(NSNumber *)sumAddend1:(NSNumber *)adder1 addend2:(NSNumber *)adder2{
    NSLog(@"Invoking method on %@ object with selector %@",NSStringFromClass([self class]),NSStringFromSelector(_cmd));
    return [NSNumber numberWithInteger:([adder1 integerValue]+[adder2 integerValue])];
}
-(NSNumber *)sumAddend1:(NSNumber *)adder1 :(NSNumber *)adder2{//這個方法的第二個引數使用空引數
    NSLog(@"Invoking method on %@ object with selector %@",NSStringFromClass([self class]),NSStringFromSelector(_cmd));
    return [NSNumber numberWithInteger:([adder1 integerValue]+[adder2 integerValue])];
}

id absoluteValue(id self , SEL _cmd , id value){
    NSInteger intVal = [value integerValue];
    if (intVal < 0) {
        NSLog(@"para object class:%@\npara method SEL:%@",NSStringFromClass([self class]),NSStringFromSelector(_cmd));
        return [NSNumber numberWithInteger:(intVal * -1)];
    }
    return value;
}
+(BOOL)resolveInstanceMethod:(SEL)sel{
    NSString * method = NSStringFromSelector(sel);
    if ([method hasPrefix:@"absoluteValue"]) {
        class_addMethod([self class],sel,(IMP)absoluteValue,"@@:@");
        NSLog(@"Dynamic added instance method %@ to class %@",method,NSStringFromClass([self class]));
        return YES;
    }
    return [super resolveInstanceMethod:sel];
}

@end

      我們來分析一下這段程式碼:

        首先我們需要一條執行時系統庫的匯入指令(#import <objc/runtime.h>);這樣會將執行時系統的API新增到你編寫的程式碼中;

        這裡class_addMethod()就是一個執行時系統API,使用它能夠以動態方式將函式作為例項方法新增到類中;

            class_addMethod([self class],sel,(IMP)absoluteValue,"@@:@");

            該API的輸入引數為新增方法的目標類、新方法的選擇器、函式的地址和描述方法引數的資料型別的字串;

            這裡,被新增為例項方法的函式為absoluteValue,接收型別為id的輸入引數並返回型別id的結果,對應字串'@@:@';

            如果是返回值為空,無引數的函式可以表示為‘[email protected]:’這樣;

        再看一下實現的函式id absoluteValue(id self , SEL _cmd , id value)

            該函式的輸入引數含有隱式引數self和_cmd,self用於設定接受物件,而_cmd用於設定方法的選擇器;

      我們來實際操作下:

    C7Calculator * calculator = [[C7Calculator alloc]init];
    id sum = [calculator performSelector:NSSelectorFromString(@"absoluteValue") withObject:@-2];
    NSLog(@"%@",sum);

     log:

     2017-12-05 16:29:58.508652+0800 精通Objective-C[56598:18002143] Dynamic added instance method absoluteValue to class C7Calculator

     2017-12-05 16:29:58.508763+0800 精通Objective-C[56598:18002143] para object class:C7Calculator

     para method SEL:absoluteValue

     2017-12-05 16:29:58.508851+0800 精通Objective-C[56598:18002143] 2

     來看看這段程式碼:

        這段程式碼以動態方式為新建方法建立了一個選擇器:NSSelectorFromString(@"absoluteValue")

        然後,使用該選擇器呼叫了一個例項方法,通過動態方法決議可以在執行程式時 新增和呼叫 這個方法,並獲得他的返回值;

        當使用選擇器的訊息通過performSelector:withObject:方法被以動態方式呼叫時,OC執行時系統會將新方法新增到C7Calculator類中;

     可以看到動態方法決議可用於在執行程式時向類中新增方法;

     7.6 動態載入

     OC程式通過 動態載入 功能可以根據需要載入可執行程式碼和原始碼,而無需在啟動程式時就載入程式的所有元件;

     延遲載入(lazy-loading):

        可執行程式碼(在載入前就連結好的)可以含有新的類,並使這些新的類在執行程式時整合到整個程式中;

        這種程式程式碼和資料資源的延遲載入方式可以提高程式的整體效能,因為它降低了系統記憶體的需求;

        該方式還提高了程式的可擴充套件性,因為能使新軟體在不更改已存在程式的情況下,以動態方式將新增程式碼新增到程式中;

        比如以動態方式載入軟體的包bundle機制;

     包是一種軟體的交付機制;

        包由具有標準層次結構的目錄以及該目錄中的可執行程式碼和原始碼構成;

        包可以包含可執行程式碼、影象、音訊檔案和其他型別的程式碼以及資源資料;

        還含有一個執行時配置檔案,即資訊屬性列表Info.plist;

     包定義了組織與軟體有關的程式碼和資料資源的基本結構,可分為一下幾種型別:

     1)應用程式包:

        應用程式包 管理與程序(程式)有關的程式碼和資料資源;

     2)框架包:

        框架包 管理以動態方式共享的軟體庫及相關的資料資源(如標頭檔案);

        應用程式可以連線一個或多個框架,如Foundation框架;

     3)可選載入包:

        可選載入包(也稱外掛)是用於以動態方式載入自定義程式碼的包;

     可以使用Foundation框架中的NSBundle類管理 包;

        一個NSBundle物件就代表檔案系統中的一個儲存位置,該位置儲存著可在程式中使用的程式碼和資料資源;

        比如我們可以這樣載入當前應用程式的資訊屬性列表(Info.plist)的路徑;

    NSBundle * bundle = [NSBundle mainBundle];
    NSString * bundlePath = [bundle pathForResource:@"Info" ofType:@"plist"];
    NSLog(@"Info plist path:%@",bundlePath);

     log:

     2017-12-05 17:46:36.815789+0800 精通Objective-C[57256:18059881] Info plist path:/Users/huaqiang/Library/Developer/CoreSimulator/Devices/3FB29963-72AE-4642-A3E0-5732A40C6267/data/Containers/Bundle/Application/2F0A7F42-1DDA-4B0E-A6B1-DAE8C78157C1/精通Objective-C.app/Info.plist

     前面說到了使用包能夠動態載入可執行程式碼,你可以這樣做:

        使用一個NSBundle物件,以動態的方式載入了一個框架包,再通過該框架建立一個例項;

        NSBundle * testBundle = [NSBundel bundleWithPath:@"/Test.bundle"];

        id tester = [[[testBundle classNamed:@"Tester"] alloc] init];

     7.7 內省

     前面我們提到“如檢查動態設定型別的匿名物件屬於哪個類”用的就是自省;

     Foundation框架中的NSObject類的API含有非常多用於執行物件內省的方法;

     使用這些方法能夠以動態方式在程式執行時查詢下列資訊:

     1)與方法有關的資訊;

     2)測試物件的繼承性、行為和一致性的資訊;

     因為OC執行時行為與編譯連線時行為差異大,所以物件內省就成了一種關鍵功能;

        使用它可以避免執行時錯誤,如訊息分派錯誤、對物件相等的錯誤假設以及其他問題;

     [object isKindOfClass:[ClassA class]];

        這個NSObject的例項方法可以用來測試接收器是ClassA類的例項還是其子類的例項;

     [object respondsToSelector:@selector(selector)];

        這個方法可以檢查某個物件是否會對選擇器做出迴應,即該物件是否實現了或繼承了能夠對指定訊息做出迴應的方法;

     [object conformsToProtocol:@protocol(protocol)];

        檢查物件是否遵循了指定的協議;

     我們可以這樣為選擇器提取方法簽名:

    NSMethodSignature * signature = [atom methodSignatureForSelector:@selector(massNumber)];
    
    NSLog(@"signature:%ld",[signature numberOfArguments]);

     log:

     2017-12-05 18:08:40.597009+0800 精通Objective-C[57482:18078532] @

     2017-12-05 18:08:40.597102+0800 精通Objective-C[57482:18078532] :

     2017-12-05 18:08:40.597200+0800 精通Objective-C[57482:18078532] returnType:Q ,returnLen:8

     2017-12-05 18:08:40.597329+0800 精通Objective-C[57482:18078532] signature:<NSMethodSignature: 0x60000026a840>

     2017-12-05 18:08:40.597446+0800 精通Objective-C[57482:18078532] signature:2

     這些都是使用NSObject類的方法進行物件內省的簡單示例,後續我們還會介紹NSObject類的API;

     7.8 小結

     本章介紹了OC執行時系統的特性和關鍵元件;

     本章要點:

     1)使用OC中的訊息傳遞(物件訊息傳遞)特性何以呼叫類和物件中的方法;

        物件訊息傳遞是一種動態特性,及接收器和接收器中的方法是在執行程式時確定的;

     2)訊息傳遞表示式包含接收器(接收訊息的物件/類)和訊息,而訊息又由選擇器和相應的輸入引數構成;

     3)選擇器是一種分段的文字字串,每個分段以冒號結尾並且後跟引數;

        含有一個以上分段的選擇器可以擁有空選擇器分段(即不帶名稱的引數);

     4)選擇器資料型別(SEL)是一種特殊的OC資料型別,它用於在編譯原始碼時使用具有唯一性的識別符號替換選擇器;

        使用@selector關鍵字或Foundation框架中的NSSelectorFromString()函式,可以建立型別為SEL的變數;

     5)方法簽名定義了方法的輸入引數和返回值的資料型別;

        方法簽名不匹配是指編譯器無法為物件訊息確定適當的方法,或者方法宣告與執行時實際執行的方法不匹配;

     6)使用動態型別功能可以在執行程式時決定物件的型別,能夠由執行時因素決定在程式中使用哪種型別的物件;

        OC通過id資料型別支援動態型別;

     7)動態繫結指在程式執行時(不是編譯時)將訊息與方法對應起來的處理過程;

        動態繫結實現了OOP的多型性;

        可以在不影響已有程式碼的情況下,將新程式碼和物件新增到程式中,降低耦合度;

     8)使用動態方法決議能夠以動態的方式實現方法;

        使用OC的@dynamic指令可以告知編譯器與某個屬性關聯的方法會以動態方式實現;

        可以使用NSObject例項方法resolveInstanceMethod: 、resolveClassMethod:以動態方式分別實現由選擇器指定的例項和類方法;

     9)使用動態載入功能可以根據需要載入OC程式的可執行程式碼和原始碼,而無需在啟動應用程式時載入它的所有元件;

        系統提供的包機制,該機制支援在平臺上以動態的方式載入軟體;

        可以使用NSBundle類管理包

     10)Foundation框架提供了很對執行物件內省的方法;

        執行程式時,這些API能夠以動態的方式查詢方法的資訊;

        他們還可以測試物件的繼承性、行為和一致性;

     本章介紹了執行時系統的特性和在程式彙總使用的方式;下一章介紹執行時系統的結構和實現方式;

     推薦大家讀一下《Objecttice-C 2.0執行時系統程式設計指南》我之前讀過,眼下重新讀的話,估計能收穫會更多些;


相關推薦

iOS程式設計基礎-OC-執行系統

第7章 執行時系統  7.4 動態繫結      動態繫結(dynamic binding):         是指在執行程式時(而不是在編譯時)將訊息與方法對應起來的處理過程;

iOS程式設計基礎-OC-執行系統

 第7章 執行時系統      終於到了執行時這一章,讓我們來一步一步揭開它神祕的面紗吧;      OC擁有相當多的動態特性,這些特性在執行程式時發揮作用,而不是在編譯或連結程式碼時發

iOS程式設計基礎-OC-專家級技巧:使用執行系統API

     第九章 專家級技巧:使用執行時系統API      第7章和第8章介紹了OC的動態特性和用於實現這些特性的執行時系統結構;      本章將通過幾個示例程式使你獲得使用執行時系統功能及其API的實踐經驗;         你將使用NSInvocati

iOS程式設計基礎-OC-專家級技巧:使用ARC

     第6章 專家級技巧:使用ARC      本章是第一部分的最後一章;          本章介紹ARC記憶體管理中的細微之處;          如直接橋接物件使用AR

《多執行程式設計》學習之:等待/通知機制

         今天起開始學習執行緒之間的通訊。等待/通知機制是通過Object類的wait()與notify()方法來實現的,這兩個方法在同步方法或同步程式碼塊中才能被執行。wait()方法將當前執行緒放入“等待執行佇列”中,使執行緒在wait()方法所處程式碼處停止執

《多執行程式設計》學習之:等待/通知機制

1、生產者與消費者問題           利用等待/通知機制,實現一生產與一消費:操作棧。生產者向棧中生產資料,消費者從棧中消費資料,棧的最大容量為1。           可見容器的size不會大於1,生產與消費這兩個過程交替進行。 2、通過管道進行執行緒之間的

網路程式設計基礎【day08】:簡單socket例項

本節內容 1、概述 2、socket例項 3、總結 一、概述   之前我們只是介紹了soket的概念和一些邏輯圖表,下面我們來看看,socket的客戶端和服務端到底是怎麼用的? 二、socket例項 2.1 客戶端 2.1.1 客戶端程式碼邏輯圖 2.1.2 客戶端程式碼

程式設計基礎31 tips 圖的最短路徑

1072 Gas Station (30 分) A gas station has to be built at such a location that the minimum distance between the station and any of the residential ho

程式設計基礎30 tips 圖的最短路徑

1018 Public Bike Management (30 分) There is a public bike service in Hangzhou City which provides great convenience to the tourists from all over th

js基礎梳理-究竟什麼是執行上下文棧執行棧執行上下文(可執行程式碼

日常在群裡討論一些概念性的問題,比如變數提升,作用域和閉包相關問題的時候,經常會聽一些大佬們給別人解釋的時候說執行上下文,呼叫上下文巴拉巴拉,總有點似懂非懂,不明覺厲的感覺。今天,就對這兩個概念梳理一下,加深對js基礎核心的理解。 1. 執行上下文(execution context)與可執行程式碼(exe

程式設計基礎》實驗題目2 c檔案讀取反序列化? 連結串列排序

題目:   每個學生的資訊卡片包括學號、姓名和成績三項。定義儲存學生資訊的單向連結串列的結點型別;編寫函 數,由檔案依次讀入 n(n≥0)個學生的資訊,建立一個用於管理學生資訊的單向連結串列;編寫函式,對 該連結串列進行整理,保證該單向連結串列的結點順序滿足學號從小到大的順序。  演算法的設計與

java併發程式設計 執行緒安全1

最近想了解併發程式設計,二執行緒安全是它的基礎。所以看了下java相關的執行緒安全知識。 執行緒安全的核心是程式碼正確性(一般是輸出的結果); 首先無狀態的物件是執行緒安全的;因為一個無狀態的物件即不包含其他域;也沒有對其他域的引用; (1)原子性    原子性:即程式碼不

第一次項目上Linux服務器:——Tomcat+ngnix+域名的簡單配置

src 簡單 一次 str b2b 準備工作 分享圖片 TP 服務器 1、準備工作   安裝好jdk+tomcat+nginx相關配置,請參考歷史博客 2、修改Nginx配置文件   修改配置文件如下: 3、修改Tomcat配置文件   修改配置文件如下:

Xcode10閃退編譯、執行輸入資料後或沒輸入資料直接閃退問題

頻繁的崩潰讓我敲程式碼的心情都受到了影響,然後今天還是10.1更新了,更新完以為會有所好轉,但是頻繁閃退這個問題還是沒有修復,簡直時醉了。 崩潰 向這種頻繁崩潰的情況,直接暴力解決。 首先右鍵主專案檔案即xcodeproj檔案,顯示包內容。找到找到project.xcworks

spring學習總結——高階裝配學習四執行:值注入

  前言:   當討論依賴注入的時候,我們通常所討論的是將一個bean引用注入到另一個bean的屬性或構造器引數中。bean裝配的另外一個方面指的是將一個值注入到bean的屬性或者構造器引數中。在沒有學習使用怎麼注入外部值時,我們正常是直接將值寫死在程式碼中。如將專輯的名字裝配到BlankDis

在cm安裝的大資料管理平臺中整合impala之後讀取hive表中的資料的設定hue當中執行impala的資料查詢

今天裝了CM叢集,在叢集當中集成了impala,hive。然後一直覺得認為impala自動共享hive的元資料,最後發現好像並不是這樣的,需要經過一個同步元資料的操作才能實現資料的同步。 具體的做法如下: (1)安裝好hive和impala,然後在hive當中建立目標資料庫,建立一張表  

執行異常和非執行異常一般異常的區別

一,異常的概念 Java異常類層次結構圖: Throwable: 有兩個重要的子類:Exception(異常)和 Error(錯誤),二者都是 Java 異常處理的重要子類,各自都包含大量子類。 Error(錯誤):是程式無法處理的錯誤,表示執行應用程式中較嚴重問題。大多數錯誤與程式碼編

深入理解JVM——執行的資料區域

Java與C++的圍牆:記憶體動態分配,垃圾收集技術 程式計數器 當前執行緒所執行的位元組碼的行號指示器,通過改變這個計數器的值來選擇下一條執行的位元組碼指令,分支,迴圈,跳轉,異常處理,執行緒恢復等依賴計數器。 執行緒私有,唯一不會OutOfMemory的區域。 執行Jav

Hadoop學習記錄、MapReduce檔案分解與合成

1.將若干個小檔案打包成順序檔案 public class SmallFilesToSequenceFileConverter extends Configured implements Tool { static class SequenceFileMapper

大資料系列之——hive、hive詳解及應用

目錄 2.HQL 一、HIVE概述 1.Hadoop分散式計算遇到的問題 MapReduce只能用java開發(也支援其他語言,但是不是主流)需要對Hadoop的底層原理 api比較瞭解才能順暢的開發出分散式的處