iOS運行時-使用Runtime向Category中添加屬性以及運行時介紹

前言

了解OC的都應該知道,在一般情況下,我們是不能向Category中添加屬性的,只能添加方法,但有些情況向,我們確實需要向Category中添加屬性,而且很多系統的API也有一些在Category添加屬性的情況,例如我們屬性的UITableViewsectionrow屬性,就是定義在一個名為NSIndexPath的分類里的,如下?
這里寫圖片描述

那這到底是怎么實現的呢?

iOS運行時機制簡介

iOS運行時機制,簡單來說,就是蘋果給開發這提供的一套在運行時動態創建類、添加屬性/方法(不止這些,還有一些其他功能)的API,它是一套純C語言的API,使用相應的API就可以通過Category給一個原本存在的類添加屬性。

實例:使用Runtime向Category中添加屬性

<code class="hljs objectivec has-numbering" style="display: block; padding: 0px; color: inherit; box-sizing: border-box; font-family: "Source Code Pro", monospace;font-size:undefined; white-space: pre; border-radius: 0px; word-wrap: normal; background: transparent;"><span class="hljs-preprocessor" style="color: rgb(68, 68, 68); box-sizing: border-box;">#import <span class="hljs-title" style="box-sizing: border-box;"><Foundation/Foundation.h></span></span>
<span class="hljs-preprocessor" style="color: rgb(68, 68, 68); box-sizing: border-box;">#import <span class="hljs-title" style="box-sizing: border-box;"><objc/runtime.h></span></span><span class="hljs-class" style="box-sizing: border-box;"><span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">@interface</span> <span class="hljs-title" style="box-sizing: border-box; color: rgb(102, 0, 102);">NSObject</span> (<span class="hljs-title" style="box-sizing: border-box; color: rgb(102, 0, 102);">CategoryWithProperty</span>)</span><span class="hljs-comment" style="color: rgb(136, 0, 0); box-sizing: border-box;">/***  要在Category中擴展的屬性*/</span>
<span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">@property</span> (<span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">nonatomic</span>, <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">strong</span>) <span class="hljs-built_in" style="color: rgb(102, 0, 102); box-sizing: border-box;">NSObject</span> *property;<span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">@end</span><span class="hljs-class" style="box-sizing: border-box;"><span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">@implementation</span> <span class="hljs-title" style="box-sizing: border-box; color: rgb(102, 0, 102);">NSObject</span> (<span class="hljs-title" style="box-sizing: border-box; color: rgb(102, 0, 102);">CategoryWithProperty</span>)</span>- (<span class="hljs-built_in" style="color: rgb(102, 0, 102); box-sizing: border-box;">NSObject</span> *)property {<span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">return</span> objc_getAssociatedObject(<span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">self</span>, <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">@selector</span>(property));
}- (<span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">void</span>)setProperty:(<span class="hljs-built_in" style="color: rgb(102, 0, 102); box-sizing: border-box;">NSObject</span> *)value {objc_setAssociatedObject(<span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">self</span>, <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">@selector</span>(property), value, OBJC_ASSOCIATION_RETAIN_NONATOMIC);
}<span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">@end</span></code><ul class="pre-numbering" style="box-sizing: border-box; position: absolute; width: 50px; top: 0px; left: 0px; margin: 0px; padding: 6px 0px 40px; border-right: 1px solid rgb(221, 221, 221); list-style: none; text-align: right; background-color: rgb(238, 238, 238);"><li style="box-sizing: border-box; padding: 0px 5px;">1</li><li style="box-sizing: border-box; padding: 0px 5px;">2</li><li style="box-sizing: border-box; padding: 0px 5px;">3</li><li style="box-sizing: border-box; padding: 0px 5px;">4</li><li style="box-sizing: border-box; padding: 0px 5px;">5</li><li style="box-sizing: border-box; padding: 0px 5px;">6</li><li style="box-sizing: border-box; padding: 0px 5px;">7</li><li style="box-sizing: border-box; padding: 0px 5px;">8</li><li style="box-sizing: border-box; padding: 0px 5px;">9</li><li style="box-sizing: border-box; padding: 0px 5px;">10</li><li style="box-sizing: border-box; padding: 0px 5px;">11</li><li style="box-sizing: border-box; padding: 0px 5px;">12</li><li style="box-sizing: border-box; padding: 0px 5px;">13</li><li style="box-sizing: border-box; padding: 0px 5px;">14</li><li style="box-sizing: border-box; padding: 0px 5px;">15</li><li style="box-sizing: border-box; padding: 0px 5px;">16</li><li style="box-sizing: border-box; padding: 0px 5px;">17</li><li style="box-sizing: border-box; padding: 0px 5px;">18</li><li style="box-sizing: border-box; padding: 0px 5px;">19</li><li style="box-sizing: border-box; padding: 0px 5px;">20</li><li style="box-sizing: border-box; padding: 0px 5px;">21</li><li style="box-sizing: border-box; padding: 0px 5px;">22</li><li style="box-sizing: border-box; padding: 0px 5px;">23</li></ul><ul class="pre-numbering" style="box-sizing: border-box; position: absolute; width: 50px; top: 0px; left: 0px; margin: 0px; padding: 6px 0px 40px; border-right: 1px solid rgb(221, 221, 221); list-style: none; text-align: right; background-color: rgb(238, 238, 238);"><li style="box-sizing: border-box; padding: 0px 5px;">1</li><li style="box-sizing: border-box; padding: 0px 5px;">2</li><li style="box-sizing: border-box; padding: 0px 5px;">3</li><li style="box-sizing: border-box; padding: 0px 5px;">4</li><li style="box-sizing: border-box; padding: 0px 5px;">5</li><li style="box-sizing: border-box; padding: 0px 5px;">6</li><li style="box-sizing: border-box; padding: 0px 5px;">7</li><li style="box-sizing: border-box; padding: 0px 5px;">8</li><li style="box-sizing: border-box; padding: 0px 5px;">9</li><li style="box-sizing: border-box; padding: 0px 5px;">10</li><li style="box-sizing: border-box; padding: 0px 5px;">11</li><li style="box-sizing: border-box; padding: 0px 5px;">12</li><li style="box-sizing: border-box; padding: 0px 5px;">13</li><li style="box-sizing: border-box; padding: 0px 5px;">14</li><li style="box-sizing: border-box; padding: 0px 5px;">15</li><li style="box-sizing: border-box; padding: 0px 5px;">16</li><li style="box-sizing: border-box; padding: 0px 5px;">17</li><li style="box-sizing: border-box; padding: 0px 5px;">18</li><li style="box-sizing: border-box; padding: 0px 5px;">19</li><li style="box-sizing: border-box; padding: 0px 5px;">20</li><li style="box-sizing: border-box; padding: 0px 5px;">21</li><li style="box-sizing: border-box; padding: 0px 5px;">22</li><li style="box-sizing: border-box; padding: 0px 5px;">23</li></ul>

這樣就可以在Category中添加屬性了。

一、什么是運行時(Runtime)?

  • 運行時是蘋果提供的純C語言的開發庫(運行時是一種非常牛逼、開發中經常用到的底層技術)

二、運行時的作用?

  • 能獲得某個類的所有成員變量
  • 能獲得某個類的所有屬性
  • 能獲得某個類的所有方法
  • 交換方法實現
  • 能動態添加一個成員變量
  • 能動態添加一個屬性
  • 能動態添加一個方法

三、案例:運行時獲取成員變量名稱

  • 1、分析
#import <Foundation/Foundation.h>
#import "XMGPerson.h"
#import <objc/runtime.h>int main(int argc, const char * argv[]) {@autoreleasepool {// 成員變量的數量unsigned int outCount = 0;// 獲得所有的成員變量// ivars是一個指向成員變量的指針// ivars默認指向第0個成員變量(最前面)Ivar *ivars = class_copyIvarList([XMGPerson class], &outCount);// 遍歷所有的成員變量for (int i = 0; i<outCount; i++) {// 取出i位置對應的成員變量
//            Ivar ivar = *(ivars + i);Ivar ivar = ivars[i];// 獲得成員變量的名字NSLog(@"%s", ivar_getName(ivar));}// 如果函數名中包含了copy\new\retain\create等字眼,那么這個函數返回的數據就需要手動釋放free(ivars);//        Ivar ivar = *ivars;
//        Ivar ivar2 = *(ivars + 1);
//        NSLog(@"%s %s", ivar_getName(ivar), ivar_getName(ivar2));// 一個Ivar就代表一個成員變量// int *p; 指向int類型的變量// Ivar *ivars; 指向Ivar類型的變量}return 0;
}
  • 2、獲取UITextFiled成員變量的名稱

 // 成員變量的數量unsigned int outCount = 0;// 獲得所有的成員變量Ivar *ivars = class_copyIvarList([UITextField class], &outCount);// 遍歷所有的成員變量for (int i = 0; i<outCount; i++) {// 取出i位置對應的成員變量Ivar ivar = ivars[i];// 獲得成員變量的名字NSLog(@"%s", ivar_getName(ivar));}// 如果函數名中包含了copy\new\retain\create等字眼,那么這個函數返回的數據就需要手動釋放free(ivars);

四、iOS底層

1、The Runtime 簡單介紹

  • Objective-C是一門簡單的語言,95%是C。只是在語言層面上加了些關鍵字和語法。真正讓Objective-C如此強大的是它的運行時。它很小但卻很強大。它的核心是消息分發。

Messages

  • 執行一個方法,有些語言,編譯器會執行一些額外的優化和錯誤檢查,因為調用關系很直接也很明顯。但對于消息分發來說,就不那么明顯了。在發消息前不必知道某個對象是否能夠處理消息。你把消息發給它,它可能會處理,也可能轉給其他的Object來處理。一個消息不必對應一個方法,一個對象可能實現一個方法來處理多條消息。
  • 在Objective-C中,消息是通過objc_msgSend()這個runtime方法及相近的方法來實現的。這個方法需要一個target,selector,還有一些參數。理論上來說,編譯器只是把消息分發變成objc_msgSend來執行。比如下面這兩行代碼是等價的。
[array insertObject:foo atIndex:5];
objc_msgSend(array, @selector(insertObject:atIndex:), foo, 5);

Objects, Classes, MetaClasses

  • 大多數面向對象的語言里有 classes 和 objects 的概念。Objects通過Classes生成。但是在Objective-C中,classes本身也是objects,也可以處理消息,這也是為什么會有類方法和實例方法。具體來說,Objective-C中的Object是一個結構體(struct),第一個成員是isa,指向自己的class。這是在objc/objc.h中定義的。
typedef struct objc_object {Class isa;
} *id;
  • object的class保存了方法列表,還有指向父類的指針。但classes也是objects,也會有isa變量,那么它又指向哪兒呢?這里就引出了第三個類型: metaclasses。一個 metaclass被指向class,class被指向object。它保存了所有實現的方法列表,以及父類的metaclass。如果想更清楚地了解objects,classes以及metaclasses是如何一起工作地,可以閱讀這篇文章。

Methods, Selectors and IMPs

  • 我們知道了運行時會發消息給對象。我們也知道一個對象的class保存了方法列表。那么這些消息是如何映射到方法的,這些方法又是如何被執行的呢?

  • 第一個問題的答案很簡單。class的方法列表其實是一個字典,key為selectors,IMPs為value。一個IMP是指向方法在內存中的實現。很重要的一點是,selector和IMP之間的關系是在運行時才決定的,而不是編譯時。這樣我們就能玩出些花樣。

  • IMP通常是指向方法的指針,第一個參數是self,類型為id,第二個參數是_cmd,類型為SEL,余下的是方法的參數。這也是self和_cmd被定義的地方。下面演示了Method和IMP

- (id)doSomethingWithInt:(int)aInt{}id doSomethingWithInt(id self, SEL _cmd, int aInt){}
  • 現在我們知道了objects,classes,selectors,IMPs以及消息分發,那么運行時到底能做什么呢?

運行時到底能做什么呢?

  • 作用:

    • 創建、修改、自省classes和objects
    • 消息分發
  • 之前已經提過消息分發,不過這只是一小部分功能。所有的運行時方法都有特定的前綴。下面是一些有意思的方法:

class

  • class開頭的方法是用來修改和自省classes。
  • 方法如:
    • 能拿到一個class的所有內容
      class_addIvar, class_addMethod, class_addProperty和class_addProtocol允許重建classes。class_copyIvarList, class_copyMethodList, class_copyProtocolList和class_copyPropertyList
    • 返回單個內容
      class_getClassMethod, class_getClassVariable, class_getInstanceMethod, class_getInstanceVariable, class_getMethodImplementation和class_getProperty
    • 一些通用的自省方法
      class_conformsToProtocol, class_respondsToSelector, class_getSuperclass
    • 創建一個object
      class_createInstance來創建一個object

ivar

  • 這些方法能讓你得到名字,內存地址和Objective-C type encoding。

method

  • 這些方法主要用來自省,比如:
method_getName, method_getImplementation, method_getReturnType等等
  • 也有一些修改的方法,包括:
    method_setImplementation和method_exchangeImplementations

objc

  • 一旦拿到了object,你就可以對它做一些自省和修改。你可以get/set ivar, 使用object_copy和object_dispose來copy和free object的內存。不僅是拿到一個class,而是可以使用object_setClass來改變一個object的class。

property

  • 屬性保存了很大一部分信息。除了拿到名字,你還可以使用property_getAttributes來發現property的更多信息,如返回值、是否為atomic、getter/setter名字、是否為dynamic、背后使用的ivar名字、是否為弱引用。

protocol

  • Protocols有點像classes,但是精簡版的,運行時的方法是一樣的。你可以獲取method, property, protocol列表, 檢查是否實現了其他的protocol。

sel

  • 最后我們有一些方法可以處理 selectors,比如獲取名字,注冊一個selector等等。

2、運行時能干什么?(舉例)

2.1 Classes And Selectors From Strings

  • 比較基礎的一個動態特性是通過String來生成Classes和Selectors。Cocoa提供了NSClassFromString和NSSelectorFromString方法,使用起來很簡單:

    Class stringclass = NSClassFromString(@"NSString")
  • 于是我們就得到了一個string class。接下來:

    NSString *myString = [stringclass stringWithString:@"Hello World"];
  • 為什么要這么做呢?直接使用Class不是更方便?通常情況下是,但有些場景下這個方法會很有用。首先,可以得知是否存在某個class,NSClassFromString 會返回nil,如果運行時不存在該class的話。

  • 另一個使用場景是根據不同的輸入返回不同的class或method。比如你在解析一些數據,每個數據項都有要解析的字符串以及自身的類型(String,Number,Array)。你可以在一個方法里搞定這些,也可以使用多個方法。其中一個方法是獲取type,然后使用if來調用匹配的方法。另一種是根據type來生成一個selector,然后調用之。以下是兩種實現方式:

- (void)parseObject:(id)object {for (id data in object) {if ([[data type] isEqualToString:@"String"]) {[self parseString:[data value]];} else if ([[data type] isEqualToString:@"Number"]) {[self parseNumber:[data value]];} else if ([[data type] isEqualToString:@"Array"]) {[self parseArray:[data value]];}}
}
- (void)parseObjectDynamic:(id)object {for (id data in object) {[self performSelector:NSSelectorFromString([NSString stringWithFormat:@"parse%@:", [data type]]) withObject:[data value]];}
}
- (void)parseString:(NSString *)aString {}
- (void)parseNumber:(NSString *)aNumber {}
- (void)parseArray:(NSString *)aArray {}
  • 可一看到,你可以把7行帶if的代碼變成1行。將來如果有新的類型,只需增加實現方法即可,而不用再去添加新的 else if。

2.2 Method Swizzling

  • 之前我們講過,方法由兩個部分組成。Selector相當于一個方法的id;IMP是方法的實現。這樣分開的一個便利之處是selector和IMP之間的對應關系可以被改變。比如一個 IMP 可以有多個 selectors 指向它。

  • 而 Method Swizzling 可以交換兩個方法的實現。或許你會問“什么情況下會需要這個呢?”。我們先來看下Objective-C中,兩種擴展class的途徑。首先是 subclassing。你可以重寫某個方法,調用父類的實現,這也意味著你必須使用這個subclass的實例,但如果繼承了某個Cocoa class,而Cocoa又返回了原先的class(比如 NSArray)。這種情況下,你會想添加一個方法到NSArray,也就是使用Category。99%的情況下這是OK的,但如果你重寫了某個方法,就沒有機會再調用原先的實現了。

  • Method Swizzling 可以搞定這個問題。你可以重寫某個方法而不用繼承,同時還可以調用原先的實現。通常的做法是在category中添加一個方法(當然也可以是一個全新的class)。可以通過method_exchangeImplementations這個運行時方法來交換實現。來看一個demo,這個demo演示了如何重寫addObject:方法來紀錄每一個新添加的對象。

#import  <objc/runtime.h>@interface NSMutableArray (LoggingAddObject)
- (void)logAddObject:(id)aObject;
@end@implementation NSMutableArray (LoggingAddObject)+ (void)load {Method addobject = class_getInstanceMethod(self, @selector(addObject:));Method logAddobject = class_getInstanceMethod(self, @selector(logAddObject:));method_exchangeImplementations(addObject, logAddObject);}- (void)logAddObject:(id)aobject {[self logAddObject:aObject];NSLog(@"Added object %@ to array %@", aObject, self);
}@end
  • 我們把方法交換放到了load中,這個方法只會被調用一次,而且是運行時載入。如果指向臨時用一下,可以放到別的地方。注意到一個很明顯的遞歸調用logAddObject:。這也是Method Swizzling容易把我們搞混的地方,因為我們已經交換了方法的實現,所以其實調用的是addObject:

動態繼承、交換

  • 我們可以在運行時創建新的class,這個特性用得不多,但其實它還是很強大的。你能通過它創建新的子類,并添加新的方法。

  • 但這樣的一個子類有什么用呢?別忘了Objective-C的一個關鍵點:object內部有一個叫做isa的變量指向它的class。這個變量可以被改變,而不需要重新創建。然后就可以添加新的ivar和方法了。可以通過以下命令來修改一個object的class.

    object_setClass(myObject, [MySubclass class]);
  • 這可以用在Key Value Observing。當你開始observing an object時,Cocoa會創建這個object的class的subclass,然后將這個object的isa指向新創建的subclass。

動態方法處理

  • 目前為止,我們討論了方法交換,以及已有方法的處理。那么當你發送了一個object無法處理的消息時會發生什么呢?很明顯,"it breaks"。大多數情況下確實如此,但Cocoa和runtime也提供了一些應對方法。

  • 首先是動態方法處理。通常來說,處理一個方法,運行時尋找匹配的selector然后執行之。有時,你只想在運行時才創建某個方法,比如有些信息只有在運行時才能得到。要實現這個效果,你需要重寫+resolveInstanceMethod: 和/或 +resolveClassMethod:。如果確實增加了一個方法,記得返回YES。

+ (BOOL)resolveInstanceMethod:(SEL)aSelector {if (aSelector == @selector(myDynamicMethod)) {class_addMethod(self, aSelector, (IMP)myDynamicIMP, "v@:");return YES;}return [super resolveInstanceMethod:aSelector];
}
  • 那Cocoa在什么場景下會使用這些方法呢?Core Data用得很多。NSManagedObjects有許多在運行時添加的屬性用來處理get/set屬性和關系。那如果Model在運行時被改變了呢?

消息轉發

  • 如果 resolve method 返回NO,運行時就進入下一步驟:消息轉發。有兩種常見用例。1) 將消息轉發到另一個可以處理該消息的object。2) 將多個消息轉發到同一個方法。

  • 消息轉發分兩步。首先,運行時調用-forwardingTargetForSelector:,如果只是想把消息發送到另一個object,那么就使用這個方法,因為更高效。如果想要修改消息,那么就要使用-forwardInvocation:,運行時將消息打包成NSInvocation,然后返回給你處理。處理完之后,調用invokeWithTarget:。

  • Cocoa有幾處地方用到了消息轉發,主要的兩個地方是代理(Proxies)和響應鏈(Responder Chain)。NSProxy是一個輕量級的class,它的作用就是轉發消息到另一個object。如果想要惰性加載object的某個屬性會很有用。NSUndoManager也有用到,不過是截取消息,之后再執行,而不是轉發到其他的地方。

  • 響應鏈是關于Cocoa如何處理與發送事件與行為到對應的對象。比如說,使用Cmd+C執行了copy命令,會發送-copy:到響應鏈。首先是First Responder,通常是當前的UI。如果沒有處理該消息,則轉發到下一個-nextResponder。這么一直下去直到找到能夠處理該消息的object,或者沒有找到,報錯。

使用Block作為Method IMP

  • iOS 4.3帶來了很多新的runtime方法。除了對properties和protocols的加強,還帶來一組新的以 imp 開頭的方法。通常一個 IMP 是一個指向方法實現的指針,頭兩個參數為 object(self)和selector(_cmd)。iOS 4.0和Mac OS X 10.6 帶來了block,imp_implementationWithBlock() 能讓我們使用block作為 IMP,下面這個代碼片段展示了如何使用block來添加新的方法。
IMP myIMP = imp_implementationWithBlock(^(id _self, NSString *string) {NSLog(@"Hello %@", string);
});
class_addMethod([MYclass class], @selector(sayHello:), myIMP, "v@:@");
  • 可以看到,Objective-C 表面看起來挺簡單,但還是很靈活的,可以帶來很多可能性。動態語言的優勢在于在不擴展語言本身的情況下做很多很靈巧的事情。比如Key Value Observing,提供了優雅的API可以與已有的代碼無縫結合,而不需要新增語言級別的特性。



本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/news/248021.shtml
繁體地址,請注明出處:http://hk.pswp.cn/news/248021.shtml
英文地址,請注明出處:http://en.pswp.cn/news/248021.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

Ajax — cropper (圖片剪裁)基本用法

jQuery-cropper插件完整的API&#xff1a;http://www.jq22.com/jquery-info9322 1. 基本使用步驟 在 <head> 中導入 cropper.css 樣式表&#xff1a; <link rel"stylesheet" href"/assets/lib/cropper/cropper.css" />在 <body> 的結…

「短篇小說」靈囚 540 天

轉載&#xff1a;知乎 - BIMBOX 孫彬 - https://zhuanlan.zhihu.com/p/24655832 「我們總有一天會逾越那條上帝劃好的界限&#xff0c;而最終我們將無法面對真實和虛假&#xff0c;正義與罪惡。」 「不幸的是&#xff0c;從伊甸園那一次&#xff0c;我們就已經越界了。」 第十天…

iOS 夠逼格的注釋總結

首先關于注意這里就不說什么VVDocument了&#xff0c;來點新鮮的&#xff01; 也許你使用過#warning 警告提示 也許你也使用過#pragma marks。 但是你見過或者使用過下面這個嗎&#xff1f; Comments containing:MARK:TODO:FIXME:!!!:???: 沒有&#xff0c;那么你就快速的看…

Git圖形化管理工具

Git圖形化管理工具 注意&#xff1a;必須在創建的倉庫中進行右鍵打開 復制這段內容后打開百度網盤App&#xff0c;操作更方便哦。 鏈接&#xff1a;https://pan.baidu.com/s/1eXIk01LXSmzmXvYfw3MnEA 提取碼&#xff1a;J166 --來自百度網盤超級會員V5的分享 分類 sourceTr…

TCP/IP(一):數據鏈路層

背景 這一系列的文章主要是為一般的、非專業開發崗位(如移動端)的工程師準備&#xff0c;一方面可以對網絡的基本知識有基本的了解&#xff0c;另一方面不至于面試中被問到相關問題時束手無策。知識以 TCP/IP 協議簇為主&#xff0c;也會有應用層和數據鏈路層的簡單介紹。 文…

富文本和封面制作

1. 富文本編輯器的實現步驟 添加如下的 layui 表單行&#xff1a; <div class"layui-form-item"><!-- 左側的 label --><label class"layui-form-label">文章內容</label><!-- 為富文本編輯器外部的容器設置高度 --><div…

Linux系統編程——線程(1)

目錄 線程概要Linux內核線程實現原理線程的共享/不共享資源線程優缺點線程控制原語pthread_selfpthread_createpthread_exitpthread_joinpthread_cancel終止線程方式控制原語對比前情提要&#xff1a; Linux用戶級線程和內核級線程區別 線程概要 Linux內核線程實現原理 類Unix系…

TCP/IP(二):IP協議

IP協議處于OSI參考模型的第三層——網絡層&#xff0c;網絡層的主要作用是實現終端節點間的通信。IP協議是網絡層的一個重要協議&#xff0c;網絡層中還有ARP(獲取MAC地址)和ICMP協議(數據發送異常通知) 數據鏈路層的作用在于實現同一種數據鏈路下的包傳遞&#xff0c;而網絡層…

Ajax — 大事件項目(第四天)

分類管理 添加分類 初步使用彈出層 給 “添加分類” 綁定一個單擊事件單擊事件中&#xff0c;使用 layer.open() 實現一個彈出層 type: 1, 彈層的類型是頁面層title, “添加文字分類”content: ‘字符串&#xff0c;DOM’,area: [‘500px’, ‘250px’] // ---------------…

redis學習(四)

一、Redis 鍵(key) 1、Redis 鍵命令用于管理 redis 的鍵。 2、Redis 鍵命令的基本語法如下&#xff1a;redis 127.0.0.1:6379> COMMAND KEY_NAME 3、常用key命令 keys * 獲取所有的keyselect 0 選擇第一個庫move myString 1 將當前的數據庫key移動到某個…

TCP/IP(三):IP協議相關技術

在前兩篇文章中&#xff0c;我分別介紹了數據鏈路層和網絡層的IP協議。雖然這個系列教程的重點是搞定 TCP/IP&#xff0c;不過不用著急&#xff0c;本文簡要介紹完與 IP 協議相關的技術&#xff0c;下一篇文章就會正式、詳細的介紹 傳輸層與 TCP 協議。這篇文章會介紹 DNS、ARP…

Node — 第一天

Node-01 會 JavaScript&#xff0c;就能學會 Node.js&#xff01;&#xff01;&#xff01; **Node.js 的官網地址&#xff1a; ** Node.js 的學習路徑&#xff1a; JavaScript 基礎語法 Node.js 內置 API 模塊&#xff08;fs、path、http等&#xff09; 第三方 API 模塊&…

TCP/IP(四):TCP 與 UDP 協議簡介

從本章開始&#xff0c;我們開始介紹最重要的傳輸層。傳輸層位于 OSI 七層模型的第四層&#xff08;由下往上&#xff09;。顧名思義&#xff0c;傳輸層的主要作用是實現應用程序之間的通信。網絡層主要是保證不同數據鏈路下數據的可達性&#xff0c;至于如何傳輸數據則是由傳輸…

Node — 第二天

http模塊 搭建服務器的步驟 ① 導入 http 模塊 ② 創建 web 服務器實例 ③ 為服務器實例綁定 request 事件&#xff0c;監聽客戶端的請求 ④ 啟動服務器 // ① 導入 http 模塊 const http require(http);// ② 創建 web 服務器實例 const server http.createServer();/…

《學習之道》第九章不要突擊工作

靈感從天而降的時刻確實是存在的。 這樣少見的創造性突破&#xff0c;通常是在經歷了一番神經緊張的準備、竭盡全力的努力&#xff0c;甚至包括熬夜工作后才姍姍來遲。這與 數學 和 科學標準的一天學習 是大不相同的。 它更像體育運動&#xff1a;每隔一陣子會有一天的比賽&…

TCP/IP(五):TCP 協議詳解

上一節 中講過&#xff0c;TCP 協議是面向有連接的協議&#xff0c;它具有丟包重發和流量控制的功能&#xff0c;這是它區別于 UDP 協議最大的特點。本文就主要討論這兩個功能。 數據包重發 數據發送 丟包重發的前提是發送方能夠知道接收方是否成功的接收了消息。所以&#…

nodeJS — 學習的筆記

Node介紹 為什么要學習Node.js 企業需求 具有服務端開發經驗更改front-endback-end全棧開發工程師基本的網站開發能力 服務端前端運維部署 多人社區 [外鏈圖片轉存失敗,源站可能有防盜鏈機制,建議將圖片保存下來直接上傳(img-MVqHkEIo-1588084625862)(C:\Users\A\AppData\R…

TCP/IP(六):HTTP 與 HTTPS 簡介

本文是準備面試過程中網絡部分總結整理的最后一篇文章&#xff0c;主要介紹以下知識&#xff1a; HTTP 協議概述POST 請求和 GET 請求Cookie 和 Session數據傳輸時的加密HTTPS 簡介 HTTP 協議 在 OSI 七層模型中&#xff0c;HTTP 協議位于最頂層的應用層中。通過瀏覽器訪問網…

Node — 第三天

模塊化 什么是模塊化 模塊化是指解決一個復雜問題時&#xff0c;自頂向下逐層把系統劃分成若干模塊的過程。 對于整個系統來說&#xff0c;模塊是可組合、分解和更換的單元。 生活中的模塊化 編程中的模塊化 編程領域中的模塊化&#xff0c;就是遵守固定的規則&#xff0c;…

FireDAC 中文字段過濾問題

當使用 FireDAC Filter 過濾數據的時候&#xff0c;通常這樣寫&#xff1a; FDMemTable.Filtered : False; FDMemTable1.Filter : 姓名 string(edtFilter.Text).QuotedString; FDMemTable.Filtered : True; 將會報錯&#xff1a;[FireDAC][Stan][Eval]-107. Invalid characte…