RUN LOOP 是个什么东西?

本文详细介绍了RunLoop的概念及作用,包括事件接收和分发机制的实现方式,何时及如何使用RunLoop,RunLoop的工作原理及其组成部分如inputsources和timersources等。此外还讨论了RunLoop模式的选择和应用。

RUN Loop是什么?


1。runloop是事件接收和分发机制的一个实现。

2。什么时候使用runloop

当需要和该线程进行交互的时候。主线程默认有runloop。当自己启动一个线程,如果只是用于处理单一的事件,则该线程在执行完之后就退出了。所以当我们需要让该线程即监听某项事务事,就得让线程一直不退出,runloop就是这么一个循环,没有事件的时候,一直卡着,有事件来临了,执行其对应的函数

3。 run loop需要处理的event source 有两种:input sources(常是其他线程的异步的event)和 timer sources(定时器)。

Anatomy of a Run Loop

run loop,正如其名称所示,是线程进入和被线程用来响应事件以及调用事件处理函数的地方。需要在代码中使用控制语句实现run loop的循环,也就是说,需要代码提供while 或者 for循环来驱动run loop。在这个循环中,使用一个runloop对象[NSRunloop currentRunloop]执行接收消息,调用对应的处理函数。

runloop接收来自两种源事件,input sources和timer sources。前者传递异步事件,通常是来自其他线程和不同的程序中的消息;后者传递同步事件(重复执行或者在特定时间上触发)。所有这两种sources都有特定的代码来处理。

input sources在调用了响应的处理函数之后会调用runUntilDate:使得runloop退出,而Timer sources不会调用runUntilDate:使得runloop退出。

除了处理input sources,runloop 也会产生一些关于本身行为的notificaiton。注册成为runloop的observer,可以接收到这些notification,做一些额外的处理。(使用CoreFoundation来成为runloop的observer)。

下面部分介绍runloop的组成部分以及runloop所处的runmode。同时也描述了不同时期产生的不同事件消息。

Run Loop Modes

一个runloop mode就是input sources、timer和observers的集合。每次执行runloop,都需要指定一个mode。在次期间,只有与该mode关联的source才会被监管和传递他们的事件,同样只有相关的observer被通知。其他mode下的sourceshold新的事件,直到得到运行????Sourcesassociated with other modes hold on to any new events until subsequent passesthrough the loop in the appropriate mode.

在代码中,mode的命名用string表示,有一些default mode和其他常用的modes。 可以使用字符串来标识一个自定义的mode。新建的自定义mode,至少需要一个inputsources或者timers或者 observers。

mode用来过滤掉你不想监听的sources,使得你想要的事件通过你代码中的循环。大数情况运行在default mode中,对于辅助线程,可以使用自定义mode来防止低优先级的sources传递事件,这样如果当前操作是time-critical,可以省下资源。

Input Sources

input sources异步地传递事件给当前线程。input source 分两类,Port-based和custom 。


  1. "code"class="html"> 
    
  1. "code" class="html">  
    
 
 



4。run loop 启动顺序
  1. 通知观察者,run loop启动
  2. 通知观察者任何即将要开始的定时器
  3. 通知观察者任何非基于端口的源即将启动
  4. 启动任何准备好的非基于端口的源
  5. 如果基于端口的源准备好并处于等待状态,立即启动;并进入步骤9。
  6. 通知观察者线程进入休眠
  7. 将线程之于休眠直到任一下面的事件发生
  • 某一事件到达基于端口的源
  • 定时器启动
  • 设置了run loop的终止时间
  • run loop唤醒
  1. 通知观察者线程将被唤醒。
  2. 处理未处理的事件
  • 如果用户定义的定时器启动,处理定时事件并重启run loop。进入步骤2
  • 如果输入源启动,传递相应的消息
  • run loop唤醒但未终止,重启。进入步骤2
  1. 通知观察者run loop结束。
    1. 通知观察者,run loop启动
    2. 通知观察者任何即将要开始的定时器
    3. 通知观察者任何非基于端口的源即将启动
    4. 启动任何准备好的非基于端口的源
    5. 如果基于端口的源准备好并处于等待状态,立即启动;并进入步骤9。
    6. 通知观察者线程进入休眠
    7. 将线程之于休眠直到任一下面的事件发生
    • 某一事件到达基于端口的源
    • 定时器启动
    • 设置了run loop的终止时间
    • run loop唤醒
    1. 通知观察者线程将被唤醒。
    2. 处理未处理的事件
    • 如果用户定义的定时器启动,处理定时事件并重启run loop。进入步骤2
    • 如果输入源启动,传递相应的消息
    • run loop唤醒但未终止,重启。进入步骤2
  • 通知观察者run loop结束。


5 。 何时需要在新线程中使用run loop
  • 使用端口或自定义输入源和其他线程通信
  • 使用定时器
  • cocoa中使用任何performSelector
  • 使线程履行周期性任务


a developer做的官方sdk文档中run loop的翻译:http://www.cnblogs.com/scorpiozj/archive/2011/05/26/2058167.html
#define BLINKER_WIFI #include #include #include // 硬件引脚定义(适配ESP-12F) #define RELAY_PIN D5 // 继电器控制引脚 #define LED_PIN D4 // 状态指示灯 #define BUTTON_PIN D3 // 物理按键(复用原清除配网按钮) // 创建WiFi配置对象 BGWiFiConfig wifipw; // 自定义配网页面HTML String html = String("<title>BGWiFiConfig</title>") + String("<form action="\"htmlresult\"" method="\"post\"">") + String("<br>WiFi名称:<br><input type="\"text\"" name="\"ssid\"">") + String("<br>WiFi密码:<br><input type="\"text\"" name="\"pwd\"">") + String("<br>auth:<br><input type="\"text\"" name="\"umsg1\"">") + String("<br>模式选择:<input type="\"radio\"" name="\"mode\"" value="\"1\"" checked="">mode:1") + String("  <input type="\"radio\"" name="\"mode\"" value="\"2\"">mode:2 <br><br>") + String("<input type="\"submit\"" value="\"开始配网\""></form>") + String("<p>如果你点击提交,表单数据会被发送到htmlresult处理,setZDYhtmlret函数设置回调页面</p>") + String("<p>BGWiFiConfig接入Blinker示例代码Beta"); // 配网结果页面 String htmlret = String("htmlresult回调"); // Blinker组件 BlinkerButton Button1("btn-abc"); BlinkerNumber Number1("num-abc"); int counter = 0; // 状态变量 bool switchState = false; unsigned long buttonPressTime = 0; bool buttonPressed = false; // 按钮清除WiFi配置中断服务程序 ICACHE_RAM_ATTR void keyCwifi() { // 防抖动 static unsigned long last_interrupt_time = 0; unsigned long interrupt_time = millis(); if (interrupt_time - last_interrupt_time > 200) { wifipw.clearWiFi(); Serial.println("WiFi信息清除成功!"); } last_interrupt_time = interrupt_time; } // 继电器控制函数 void setRelay(bool state) { digitalWrite(RELAY_PIN, state); switchState = state; digitalWrite(LED_PIN, !state); // LED通常是低电平点亮 Button1.print(state ? "on" : "off"); } // 按钮回调函数 void button1_callback(const String & state) { BLINKER_LOG("get button state: ", state); setRelay(!switchState); Blinker.vibrate(); } // 数据接收处理 void dataRead(const String & data) { BLINKER_LOG("Blinker readString: ", data); counter++; Number1.print(counter); } // 初始化硬件 void setupHardware() { // 设置引脚模式 pinMode(RELAY_PIN, OUTPUT); pinMode(LED_PIN, OUTPUT); pinMode(BUTTON_PIN, INPUT_PULLUP); // 初始化状态 setRelay(false); // 初始化物理按键中断 attachInterrupt(digitalPinToInterrupt(BUTTON_PIN), keyCwifi, CHANGE); } // 按键检测函数 void checkPhysicalButton() { if (digitalRead(BUTTON_PIN) == LOW) { if (!buttonPressed) { buttonPressTime = millis(); buttonPressed = true; } // 长按5秒重置配置 if (millis() - buttonPressTime > 5000) { wifipw.clearWiFi(); Serial.println("WiFi信息清除成功!"); // 重置后重启 ESP.restart(); } } else { if (buttonPressed) { // 短按切换开关状态 if (millis() - buttonPressTime < 1000) { setRelay(!switchState); } buttonPressed = false; } } } void setup() { Serial.begin(115200); // 初始化硬件 setupHardware(); // 初始化WiFi配置 wifipw.offConnectWiFi(true); wifipw.setZDYhtml(html); wifipw.setZDYhtmlret(htmlret); wifipw.setNumUMSG(1); wifipw.begin(); // 初始化Blinker if (wifipw.readWiFi(0) != "" && wifipw.readWiFi(0) != "NULL") { Serial.println(">>> blinker start <<<"); Blinker.begin(wifipw.readUMSG(1).c_str(), wifipw.readWiFi(0).c_str(), wifipw.readWiFi(1).c_str()); Blinker.attachData(dataRead); Button1.attach(button1_callback); } } void loop() { // WiFi配置循环 wifipw.Loop(); // 检测物理按键 checkPhysicalButton(); // Blinker循环 if (wifipw.readWiFi(0) != "" && wifipw.readWiFi(0) != "NULL") { Blinker.run(); } // 添加小延迟避免看门狗超时 delay(10); } 修正 头部文件匹配
最新发布
09-04
#define BLINKER_WIFI #include #include #include // 硬件引脚定义(适配ESP-12F) #define RELAY_PIN D5 // 继电器控制引脚 #define LED_PIN D4 // 状态指示灯 #define BUTTON_PIN D3 // 物理按键(复用原清除配网按钮) // 创建WiFi配置对象 BGWiFiConfig wifipw; // 自定义配网页面HTML String html = String("<title>BGWiFiConfig</title>") + String("<form action="\"htmlresult\"" method="\"post\"">") + String("<br>WiFi名称:<br><input type="\"text\"" name="\"ssid\"">") + String("<br>WiFi密码:<br><input type="\"text\"" name="\"pwd\"">") + String("<br>auth:<br><input type="\"text\"" name="\"umsg1\"">") + String("<br>模式选择:<input type="\"radio\"" name="\"mode\"" value="\"1\"" checked="">mode:1") + String("  <input type="\"radio\"" name="\"mode\"" value="\"2\"">mode:2 <br><br>") + String("<input type="\"submit\"" value="\"开始配网\""></form>") + String("<p>如果你点击提交,表单数据会被发送到htmlresult处理,setZDYhtmlret函数设置回调页面</p>") + String("<p>BGWiFiConfig接入Blinker示例代码Beta"); // 配网结果页面 String htmlret = String("htmlresult回调"); // Blinker组件 BlinkerButton Button1("btn-abc"); BlinkerNumber Number1("num-abc"); int counter = 0; // 状态变量 bool switchState = false; unsigned long buttonPressTime = 0; bool buttonPressed = false; // 按钮清除WiFi配置中断服务程序 ICACHE_RAM_ATTR void keyCwifi() { // 防抖动 static unsigned long last_interrupt_time = 0; unsigned long interrupt_time = millis(); if (interrupt_time - last_interrupt_time > 200) { wifipw.clearWiFi(); Serial.println("WiFi信息清除成功!"); } last_interrupt_time = interrupt_time; } // 继电器控制函数 void setRelay(bool state) { digitalWrite(RELAY_PIN, state); switchState = state; digitalWrite(LED_PIN, !state); // LED通常是低电平点亮 Button1.print(state ? "on" : "off"); } // 按钮回调函数 void button1_callback(const String & state) { BLINKER_LOG("get button state: ", state); setRelay(!switchState); Blinker.vibrate(); } // 数据接收处理 void dataRead(const String & data) { BLINKER_LOG("Blinker readString: ", data); counter++; Number1.print(counter); } // 初始化硬件 void setupHardware() { // 设置引脚模式 pinMode(RELAY_PIN, OUTPUT); pinMode(LED_PIN, OUTPUT); pinMode(BUTTON_PIN, INPUT_PULLUP); // 初始化状态 setRelay(false); // 初始化物理按键中断 attachInterrupt(digitalPinToInterrupt(BUTTON_PIN), keyCwifi, CHANGE); } // 按键检测函数 void checkPhysicalButton() { if (digitalRead(BUTTON_PIN) == LOW) { if (!buttonPressed) { buttonPressTime = millis(); buttonPressed = true; } // 长按5秒重置配置 if (millis() - buttonPressTime > 5000) { wifipw.clearWiFi(); Serial.println("WiFi信息清除成功!"); // 重置后重启 ESP.restart(); } } else { if (buttonPressed) { // 短按切换开关状态 if (millis() - buttonPressTime < 1000) { setRelay(!switchState); } buttonPressed = false; } } } void setup() { Serial.begin(115200); // 初始化硬件 setupHardware(); // 初始化WiFi配置 wifipw.offConnectWiFi(true); wifipw.setZDYhtml(html); wifipw.setZDYhtmlret(htmlret); wifipw.setNumUMSG(1); wifipw.begin(); // 初始化Blinker if (wifipw.readWiFi(0) != "" && wifipw.readWiFi(0) != "NULL") { Serial.println(">>> blinker start <<<"); Blinker.begin(wifipw.readUMSG(1).c_str(), wifipw.readWiFi(0).c_str(), wifipw.readWiFi(1).c_str()); Blinker.attachData(dataRead); Button1.attach(button1_callback); } } void loop() { // WiFi配置循环 wifipw.Loop(); // 检测物理按键 checkPhysicalButton(); // Blinker循环 if (wifipw.readWiFi(0) != "" && wifipw.readWiFi(0) != "NULL") { Blinker.run(); } // 添加小延迟避免看门狗超时 delay(10); } 修正代码中的错误点 D:\ESP8266程序\代码编辑\阿里智能插座APP 代码\sketch_aug23a\sketch_aug23a.ino:2:10: error: #include expects "FILENAME" or <FILENAME> D:\ESP8266程序\代码编辑\阿里智能插座APP 代码\sketch_aug23a\sketch_aug23a.ino:3:10: error: #include expects "FILENAME" or <FILENAME> D:\ESP8266程序\代码编辑\阿里智能插座APP 代码\sketch_aug23a\sketch_aug23a.ino:4:10: error: #include expects "FILENAME" or <FILENAME> exit status 1 Compilation error: #include expects "FILENAME" or <FILENAME>
09-04
#define BLINKER_WIFI #include <Blinker.h> #include <BGWiFiConfig.h> BGWiFiConfig wifipw; #define key D3 //按钮清除配网信息,其他esp8266/32开发板改这个中断io口即可。 String html = String("<html><head><meta charset=\"utf-8\"><title>BGWiFiConfig</title></head><body>") + String("<form action=\"htmlresult\" method=\"post\">") + String("<br>WiFi名称:<br><input type=\"text\" name=\"ssid\">") + String("<br>WiFi密码:<br><input type=\"text\" name=\"pwd\">") + String("<br>auth:<br><input type=\"text\" name=\"umsg1\">") + String("<br>模式选择:<input type=\"radio\" name=\"mode\" value=\"1\" checked>mode:1") + String("  <input type=\"radio\" name=\"mode\" value=\"2\" >mode:2 <br><br>") + String("<input type=\"submit\" value=\"开始配网\"></form>") + String("<p>如果你点击提交,表单数据会被发送到htmlresult处理,setZDYhtmlret函数设置回调页面</p>") + String("<p>BGWiFiConfig接入Blinker示例代码Beta</body></html>"); String htmlret = String("<html><head><meta charset=\"utf-8\"></head><body>htmlresult回调</body></html>"); // Blinker示例程序Hello_WiFi内容 BlinkerButton Button1("btn-abc"); BlinkerNumber Number1("num-abc"); int counter = 0; void button1_callback(const String & state) { BLINKER_LOG("get button state: ", state); digitalWrite(LED_BUILTIN, !digitalRead(LED_BUILTIN)); } void dataRead(const String & data) { BLINKER_LOG("Blinker readString: ", data); counter++; Number1.print(counter); } ICACHE_RAM_ATTR void keyCwifi() { wifipw.clearWiFi(); Serial.println("WiFi信息清除成功!"); } void setup() { Serial.begin(115200); pinMode(key, INPUT_PULLUP); attachInterrupt(digitalPinToInterrupt(key), keyCwifi, CHANGE); wifipw.offConnectWiFi(true); wifipw.setZDYhtml(html); wifipw.setZDYhtmlret(htmlret); wifipw.setNumUMSG(1); wifipw.begin(); if (wifipw.readWiFi(0) != "" && wifipw.readWiFi(0) != "NULL") { Serial.println(">>> blinker start <<<"); pinMode(LED_BUILTIN, OUTPUT); digitalWrite(LED_BUILTIN, HIGH); Blinker.begin( wifipw.readUMSG(1).c_str(), wifipw.readWiFi(0).c_str(), wifipw.readWiFi(1).c_str()); Blinker.attachData(dataRead); Button1.attach(button1_callback); } } void loop() { wifipw.Loop(); if (wifipw.readWiFi(0) != "" && wifipw.readWiFi(0) != "NULL") Blinker.run(); } 修正其中的错误代码能匹配ESP 12F开发板使用 保持其中功能正常 并添加智能插座 物理按键通断 长按重置功能 状态指示灯
09-04
<think>我们正在处理一个JavaScript字符串问题:如何去除字符串中的` `实体。` `在HTML中表示不换行空格(non-breaking space)。在JavaScript中,我们可以使用字符串的replace方法,结合正则表达式来替换所有出现的` `。 注意:` `在字符串中可能以两种形式存在:一种是作为字面字符串" ",另一种是已经被解析为空格(但这种情况我们无法直接通过字符串匹配,因为已经被解析为空格了)。根据问题,我们明确要去除的是实体,即字符串中的" "子串。 因此,我们可以使用以下代码: ```javascript let str = "这是一些文本 包含 实体"; let newStr = str.replace(/ /g, ''); ``` 但是,有时我们可能希望替换为普通空格(而不是完全删除),那么可以: ```javascript let newStr = str.replace(/ /g, ' '); ``` 另外,考虑到HTML实体可能有不同的表示(如大小写不敏感,或者使用数字编码),但` `通常是小写,且有时可能写成大写或混合大小写。为了更健壮,我们可以使用正则表达式忽略大小写: ```javascript let newStr = str.replace(/ /gi, ' '); // g表示全局替换,i表示忽略大小写 ``` 如果字符串中可能包含其他空格实体,比如` `或` `(这些是` `的字符实体编码),那么我们可以扩展正则表达式: ```javascript let newStr = str.replace(/ | | /gi, ' '); ``` 但根据问题,我们只需要去除` `实体,所以不需要考虑其他形式。 因此,针对问题,给出以下解决方案: 1. 如果要去除所有` `(不区分大小写)并替换为空字符串(即删除): ```javascript str = str.replace(/ /gi, ''); ``` 2. 如果替换为普通空格: ```javascript str = str.replace(/ /gi, ' '); ``` 根据需求选择。 注意:在JavaScript中,字符串是不可变的,所以replace方法会返回一个新的字符串,原始字符串不会被改变。 示例: ```javascript let original = "Hello World&Nbsp;!"; let cleaned = original.replace(/ /gi, ' '); console.log(cleaned); // 输出: "Hello World !" ``` 以上代码将所有的` `(不区分大小写)替换为空格。 因此,用户可以根据自己的需求选择替换方式。 相关问题: 1. 在JavaScript中如何去除字符串中的所有HTML实体? 2. 如何将字符串中的多个连续空格合并为一个? 3. JavaScript中除了replace方法,还有哪些常用的字符串操作方法?</think>在 JavaScript 中去除字符串中的 ` ` 实体(即 HTML 不换行空格),可以通过字符串替换操作实现。以下是几种常用方法: ### 方法 1:使用 `replace()` 全局替换 ```javascript const originalString = "文本 包含 实体"; const cleanedString = originalString.replace(/ /g, ''); // 完全删除 // 或替换为普通空格 const spaceReplaced = originalString.replace(/ /g, ' '); ``` ### 方法 2:处理大小写变体(更健壮) ```javascript const cleanedString = originalString.replace(/ |&NBSP;/gi, ''); ``` `/gi` 标志表示全局匹配 + 忽略大小写 ### 方法 3:结合 DOM 解析(处理编码字符) ```javascript function removeNbsp(str) { const div = document.createElement('div'); div.innerHTML = str.replace(/ /gi, ' '); // 先替换实体 return div.textContent || div.innerText; } ``` ### 示例效果 ```javascript const input = "Hello World&Nbsp;!"; console.log(input.replace(/ /gi, ' ')); // 输出: "Hello World !" ``` ### 注意事项 1. 如果字符串来自 HTML 解析,确保实体未被自动转换 2. ` ` 在 HTML 中渲染为空格,但作为字符串是 6 个独立字符 3. 替换后可能需要额外处理连续空格:`.replace(/\s+/g, ' ')` --- ### 相关问题 1. JavaScript 中如何去除字符串中的所有 HTML 实体? 2. 如何区分 JavaScript 字符串中的普通空格和 ` ` 实体? 3. 在 Node.js 环境下如何处理 HTML 实体解码? 4. 除了正则表达式,还有哪些安全的 HTML 实体处理方法? [^1]: 参考代码模式:`operateResult.Success = ...Service.Delete(id);` [^2]: 参考连接字符串处理方式:`&quot;` 实体转义处理
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值