手把手教创建你的第一个以太智能合约:ETHEREUM PET SHOP(译)
手把手教创建你的第一个以太智能合约:ETHEREUM PET SHOP(译)
原文地址 : http://truffleframework.com/tutorials/pet-shop
译者:lucia3
译者steemit主页:++https://steemit.com/@lucia3++
译者以太地址:++0x2a703d8ae21d5f23d6ffab3a10c62f0a64825867++
如果觉得这个教程对你有用,请不要吝啬打赏哟~
这个系列的教程将会手把手带你搭建你的第一个dapp应用——一个宠物商店的领养追踪系统。
这个教程是为那些有基本的以太链和智能合约知识,懂得一些HTML和JavaScript,但是从来没开发过dapp的人准备的。
注意:如果想要补充以太坊基础,请在继续学习本教程前阅读 Truffle的教程Ethereum Overview
在这个教程里,我们将会学习以下内容:
- 搭建开发环境(Truffle框架下开发以太坊智能合约)
- 通过使用Truffle Box创建一个Truffle项目
- 编写一个智能合约
- 编译合约以及将合约迁移到区块链上
- 测试智能合约
- 创建一个与智能合约交互的UI
- 在浏览器里使用你创建的dapp
背景
皮特对于使用以太坊技术处理他们店里的宠物领养非常有兴趣。这家店有16只宠物等待领养,这些宠物已经录入了数据库。作为一个概念的初步证明,Pete希望看到一个将一个以太坊地址与一只个宠物关联起来的dapp。
本项目的网站结构和样式已经提供。 我们的工作是为其编写智能合同和前端逻辑。
搭建开发环境
在我们开始之前, 请安装以下内容:
- ++Node.js v6+ LTS and npm (comes with Node)++
- ++Git++
一旦我们安装了这些,我们只需要一个命令来安装Truffle:
npm install -g truffle
要验证Truffle是否正确安装,请在终端上输入truffle version
:
truffle version
如果您看到错误,请确保您的npm模块已添加到您的路径中。
通过使用Truffle Box创建一个Truffle项目
- Truffle会在当前的目录中初始化,所以首先在你选择的开发文件夹中创建一个目录,然后进入这个目录。
mkdir pet-shop-tutorialcd pet-shop-tutorial
- 我们已经为这个
pet-shop
教程创建了一个特殊的++Truffle Box++,其中包括基本的项目结构以及用户界面的代码。 使用truffle unbox
命令解压这个Truffle Box。
truffle unbox pet-shop
> ##### 注意:truffle可以通过几种不同的方式进行初始化。 另一个有用的初始化命令是`truffle init`,它创建一个空的Truffle项目,不包含任何示例合同。 有关更多信息,请参阅[++创建项目文档++]( http://truffleframework.com/docs/getting_started/project )。
项目目录结构
默认的Truffle目录结构包含以下内容:
contracts/
: 包含了我们的项目的智能合约的源文件(++Solidity++语言开发的)。 在这里有一个重要的合约Migrations.sol,我们稍后再讨论。migrations/
: Truffle uses a migration system to handle smart contract deployments. A migration is an additional special smart contract that keeps track of changes.truffle使用迁移系统来处理智能合约部署。 迁移是追踪变化的一种额外的特殊智能合约。test/
: contracts包含了对我们智能合约中JavaScript和Solidity的测试truffle.js
: Truffle配置文件
pet-shop
Truffle Box项目里还有些其它的文件和文件夹,但我们目前不用关注它们。
编写一个智能合约
我们将从编写充当后端逻辑和存储的智能合约开始,来开发我们的dapp。
- 在
contracts /
目录下创建一个名为Adoption.sol
的新文件。 - 将以下内容添加到文件中:
pragma solidity ^0.4.4;contract Adoption {}
注意事项:
在合同的顶部注明了所需的最低版本:
pragma solidity ^0.4.4;
。pragma
意味着“只有编译器关心的附加信息”,而脱字符号(^)表示“指定的版本或更高版本”。像JavaScript或PHP一样,语句以分号结尾
定义变量
Solidity是一种静态类型的语言,意味着像字符串,整数和数组等数据类型必须被定义。 Solidity有一个称为address的独特类型。 Addresses址是以太坊地址,一个存储为20个字节的值。 以太坊区块链上的每个账户和智能合约都有一个地址,并可以通过此地址发送和接收以太币。
在contract Adoption {
之后,在下一行添加以下变量。
address[16] public adopters;
注意事项:
- 我们已经定义了一个变量:
adopters
。 这是一个以太坊地址数组。 数组包含一种类型,可以有一个固定的或可变的长度。 在这个例子中,类型是address
,长度是16。 - 你还会注意到
adopters
是公共的。 公共变量具有自动getter方法,但是如果在公共变量是数组的情况下,键是必需的,给定了key的数组只会返回数组中的一个值。 稍后,我们将编写一个函数来返回整个数组,供我们的用户界面使用。
你的第一个函数:领养宠物
首先要让用户可以领养宠物。
- 在上面设置的变量声明之后,将下面的函数添加到智能合约中。
// Adopting a pet
function adopt(uint petId) public returns (uint) {require(petId >= 0 && petId <= 15);adopters[petId] = msg.sender;return petId;
}
注意事项:
在Solidity中,必须指定函数参数和输出的类型。 在这种情况下,我们将获取一个
petId
(整数)并返回一个整数。首先要检查
petId
,以确保petId
不会超出我们定义的数组范围。 Solidity中的数组起始索引为0,因此ID值将需要介于0和15之间。我们使用require()
语句来确保ID在数组范围内。如果ID在范围内,我们往
adopters
数组中添加 address。 调用此函数的人员或智能合约的地址由msg.sender
表示。最后,我们返回传入的
petId
作为确认。
你的第二个函数:获取领养人
如上所述,数组getters方法只从给定的键返回一个单一的值。 我们的UI需要更新所有的宠物收养状态,但是调用16次getters方法并不明智。 所以我们下一步是编写一个函数来返回整个数组。
将以下getAdopters()
函数添加到智能合约中,在我们上面添加的adopt()
函数之后:
// Retrieving the adopters
function getAdopters() public returns (address[16]) {return adopters;
}
由于adopters
已经声明,我们可以简单地返回它。 确保将返回类型(在这个例子中是adopters
的类型)指定为address[16]
。
编译合约以及将合约迁移到区块链上
现在我们已经编写了我们的智能合约,接下来的步骤是编译合约以及将合约迁移到区块链上。
Truffle有一个内置的开发者控制台,我们称之为Truffle Develop,它生成一个开发区块链,我们可以用来测试部署合同。 它还能够直接从控制台运行Truffle命令。 我们将使用Truffle Develop在本教程中执行我们合同中的大部分操作。
编译
Solidity是一种编译语言,这意味着我们需要将我们的Solidity编译为用于以太坊虚拟机(EVM)执行的字节码。 把它看作是将我们人类可读的“固体”(Solidity)翻译成EVM所理解的东西。
- 登陆Truffle Develop。 确保你在包含dapp的目录中执行下列命令。
truffle develop
你会看到一个提示,显示你现在在Truffle Develop中。 下文除非另有说明,否则所有命令都将从此控制台运行。
truffle(develop)>
注意:如果你在Windows上并遇到运行此命令的问题,请参阅有关++解决Windows上的命名冲突++的文档。
- 编译dapp
compile
在执行完上面那条命令后,你应该看到类似于下面的输出:
Compiling ./contracts/Migrations.sol...
Compiling ./contracts/Adoption.sol...
Writing artifacts to ./build/contracts
注意:如果你没有使用Truffle Develop,这些命令可以在你的终端上使用
truffle
前缀。 如在编译中,在终端上运行truffle compile
。
但是,如果你不使用Truffle Develop,你将不得不使用另一个测试区块链,如++TestRPC++。
将合约迁移到区块链上
现在我们已经成功编译了我们的合同,现在是时候将它们迁移到区块链了!
迁移是一个部署脚本,旨在改变应用程序合同的状态,将其从一个状态转移到另一个状态。 对于第一次迁移,您可能只是部署新的代码,但随着时间的推移,其他迁移可能会移动数据或用新的代码替换合同。
注意:在Truffle文档中关于++迁移++的信息。
你将在migrations /
目录中看到一个JavaScript文件:1_initial_migration.js
。 这将处理部署Migrations.sol
合同以观察后续的智能合同迁移,并确保将来不会对未更改的合同进行双重迁移。
现在我们准备创建我们自己的迁移脚本。
在
migrations /
目录下创建一个名为2_deploy_contracts.js
的新文件。将以下内容添加到
2_deploy_contracts.js
文件中:
var Adoption = artifacts.require("Adoption");module.exports = function(deployer) {deployer.deploy(Adoption);
};
- 回到我们的控制台,将合同迁移到区块链。
migrate
在执行完上面那条命令后,你应该看到类似于下面的输出:
Using network 'develop'.Running migration: 1_initial_migration.jsDeploying Migrations...Migrations: 0x75175eb116b36ff5fef15ebd15cbab01b50b50d1
Saving successful migration to network...
Saving artifacts...
Running migration: 2_deploy_contracts.jsDeploying Adoption...Adoption: 0xb9f485451a945e65e48d9dd7fc5d759af0a89e21
Saving successful migration to network...
Saving artifacts...
您可以按顺序看到正在执行的迁移,然后是每个部署的合同的区块链地址。 (你的地址会有所不同。)
您现在已经写好了您的第一个智能合约,并将其部署到本地运行的测试区块链中。 现在是时候与我们的智能合约进行互动,以确保它符合我们的要求。
测试智能合约
在智能合约测试方面,Truffle非常灵活,因为测试可以用JavaScript或Solidity编写。 在本教程中,我们将在Solidity中编写我们的测试。
在
test /
目录下创建一个名为TestAdoption.sol
的新文件。将以下内容添加到
TestAdoption.sol
文件中:
pragma solidity ^0.4.11;import "truffle/Assert.sol";
import "truffle/DeployedAddresses.sol";
import "../contracts/Adoption.sol";contract TestAdoption {Adoption adoption = Adoption(DeployedAddresses.Adoption());}
我们用三个imports开始合同:
Assert.sol
:使我们在测试中使用的各种断言。 在测试中,一个断言会检查如平等,不平等或判空之类的事情,以便从我们的测试中返回通过/失败。 ++truffle包含的断言的完整列表++。DeployedAddresses.sol
:运行测试时,Truffle会将正在测试的合约的新实例部署到TestRPC。 这个智能合约获得了已经被部署的合约的地址。Adoption.sol
:我们想要测试的智能合约。
注意:
前两个imports是引用自全局Truffle文件,而不是truffle
目录。 你不会看到你的test/
目录里有truffle
目录。
然后我们定义一个包含要测试的智能合约的合同范围的变量,调用DeployedAddresses
智能合约来获得它的地址。
测试 adopt()函数
要测试adopt()
函数,成功时返回给定的petId
。 我们可以通过比较采用的返回值和我们传入的ID来确保返回的ID是正确的。
- 在
Adoption
的声明之后,在TestAdoption.sol
智能合同中添加以下函数:
// Testing the adopt() function
function testUserCanAdoptPet() {uint returnedId = adoption.adopt(8);uint expected = 8;Assert.equal(returnedId, expected, "Adoption of pet ID 8 should be recorded.");
}
注意事项:
- 我们调用前面声明的智能合约
adoption
,传入的参数ID为8。 - 然后我们声明我们预期的函数返回值
expected
为8。 - 最后,我们将实际得到的返回值
returnedId
,期望值expected
和失败消息(如果测试未通过,将打印到控制台)作为入参传递给Assert.equal()。
测试根据给定宠物id获得领养人的函数
由于数组只能给定一个键返回一个值,所以我们为整个数组创建了自己的getter。
- 在
TestAdoption.sol
中先前添加的函数下添加此函数。
// Testing retrieval of all pet owners
function testGetAdopterAddressByPetIdInArray() {// Expected owner is this contractaddress expected = this;// Store adopters in memory rather than contract's storageaddress[16] memory adopters = adoption.getAdopters();Assert.equal(adopters[8], expected, "Owner of pet ID 8 should be recorded.");
}
请注意adopters
的memory
属性。 memory
属性告诉Solidity将数据临时存储在内存中,而不是将其保存到合同的存储。 由adopters
是一个数组,我们从第一个领养函数的测试中知道我们领养了petId
为 8的宠物,所以我们将测试合约地址与数组中的index
为8的位置存储的地址进行比较。
执行测试函数
- 回到Truffle Develop中,执行下列命令:
test
- 如果所有的测试都通过了,你会看到类似这样的控制台输出:
Using network 'develop'.Compiling ./contracts/Adoption.sol...Compiling ./test/TestAdoption.sol...Compiling truffle/Assert.sol...Compiling truffle/DeployedAddresses.sol...TestAdoption✓ testUserCanAdoptPet (91ms)✓ testGetAdopterAddressByPetId (70ms)✓ testGetAdopterAddressByPetIdInArray (89ms)3 passing (670ms)
创建一个与智能合约交互的UI
现在,我们已经创建了智能合约,将其部署到我们的本地测试区块链中,并确认我们可以通过控制台与它进行交互,现在是时候创建一个UI,让Pete有一些东西可以用于他的宠物店!
这个应用程序的前端代码在pet-shop
项目目录里。 存在于src /
目录中。
本项目的前端不使用构建系统(webpack,grunt等),尽可能简单地开始。 该应用程序的结构已经提供; 我们将专注于编写以太坊特有的函数。 这样,您就可以将这些知识应用到您自己的前端开发中。
初始化 web3
在文本编辑器中打开
/src/js/app.js
。检查文件。 请注意,有一个全局
App
对象来管理我们的应用程序,在init()
中加载宠物数据,然后调用函数initWeb3()
。 ++web3 JavaScript++库与以太坊区块链交互。 它可以检索用户帐户,发送交易,与智能合约交互等等。从
initWeb3
中删除多行注释并将其替换为以下内容:
// Is there is an injected web3 instance?
if (typeof web3 !== 'undefined') {App.web3Provider = web3.currentProvider;
} else {// If no injected web3 instance is detected, fallback to the TestRPCApp.web3Provider = new Web3.providers.HttpProvider('http://localhost:8545');
}
web3 = new Web3(App.web3Provider);
注意事项:
首先,我们检查web3实例是否已经存在。 (以太坊浏览器(如++Mist++或者带有++MetaMask++扩展的Chrome)将注入自己的web3实例。)如果注入的web3实例存在,我们将获取它的提供者并使用它创建我们的web3对象。
如果没有web3实例存在,我们将基于我们的本地提供者创建我们的web3对象。 (对于开发环境来说,这种回退很好,但不安全,不适合生产。)
初始化智能合约
现在我们可以通过web3与以太坊互动,我们需要实例化我们的智能合约,以便web3知道在哪里找到它,以及它如何工作。 Truffle有一个库来帮助实现这些——truffle-contract
。 它将有关合同的信息与迁移保持同步,因此您不需要手动更改合同的部署地址。
- 仍然在
/src/js/app.js
中,从initContract
中删除多行注释并将其替换为以下内容:
$.getJSON('Adoption.json', function(data) {// Get the necessary contract artifact file and instantiate it with truffle-contractvar AdoptionArtifact = data;App.contracts.Adoption = TruffleContract(AdoptionArtifact);// Set the provider for our contractApp.contracts.Adoption.setProvider(App.web3Provider);// Use our contract to retrieve and mark the adopted petsreturn App.markAdopted();
});
注意事项:
首先我们获取我们的智能合约的
artifact
文件。artifact
是关于我们的合同的信息,例如其部署的地址和应用程序二进制接口(ABI)。 ABI是一个JavaScript对象,定义了如何与契约进行交互,包括变量,函数和参数。一旦我们在回调中获得了
artifact
,我们将它们传递给TruffleContract()
。 这创建了一个我们可以与之交互的合同实例。实例化我们的合约之后,我们将合约提供者设置成
App.web3Provider
,这是之前设置web3提供者时存储的值。然后我们调用应用程序的
markAdopted()
函数,标记已经被领养的宠物。 我们把它封装在一个单独的函数中,因为我们需要在更改智能合约的数据时更新UI。
获取已经领养的宠物&更新UI
- 仍然在
/src/js/app.js
中,从markAdopted
中删除多行注释,并将其替换为以下内容:
var adoptionInstance;App.contracts.Adoption.deployed().then(function(instance) {adoptionInstance = instance;return adoptionInstance.getAdopters.call();
}).then(function(adopters) {for (i = 0; i < adopters.length; i++) {if (adopters[i] !== '0x0000000000000000000000000000000000000000') {$('.panel-pet').eq(i).find('button').text('Success').attr('disabled', true);}}
}).catch(function(err) {console.log(err.message);
});
注意事项:
我们访问已部署的
Adoption
合同,然后在该实例上调用getAdopters()
。我们首先在智能合约调用之外声明变量
adoptionInstance
,这样我们可以在初始化实例之后的函数里访问实例。使用
call()
允许我们从区块链读取数据,而不必发送完整的交易,这意味着我们不必花费任何代价。在调用
getAdopters()
之后,我们循环遍历所有的宠物,检查每个宠物是否存储了领养人的地址。 由于数组包含地址类型,以太坊使用16个空地址初始化数组。 这就是为什么我们检查一个空的地址字符串,而不是null或其他错误的值。一旦找到了一个有相应地址的
petId
,我们禁用其领养按钮,并将按钮文本改为“成功”,这样用户就知道哪些宠物已经被领养了。任何错误都被记录到控制台。
处理adopt()
函数
- 仍然在
/src/js/app.js
中,从handleAdopt中删除多行注释,并将其替换为以下内容:
var adoptionInstance;web3.eth.getAccounts(function(error, accounts) {if (error) {console.log(error);}var account = accounts[0];App.contracts.Adoption.deployed().then(function(instance) {adoptionInstance = instance;// Execute adopt as a transaction by sending accountreturn adoptionInstance.adopt(petId, {from: account});}).then(function(result) {return App.markAdopted();}).catch(function(err) {console.log(err.message);});
});
注意事项:
- 我们使用web3来获取用户的帐户。 在错误检查后的回调中,我们然后选择第一个帐户。
- 从那里,我们像上面那样得到已部署的合约,并将实例存储在
adoptionInstance
中。 这一次,我们将发送一个交易,而不是一个调用。 交易需要"from"地址,并会产生相关费用。 这个费用是用以太币支付的,被称为gas
。gas
是在智能合约中执行计算和/或存储数据产生的费用。
我们通过执行adopt()
函数来发送交易,函数的入参是宠物ID和一个包含我们先前存储在账户中的账户地址的对象。 - 发送交易的返回是一个交易对象。 如果没有错误,我们继续调用我们的
markAdopted()
函数来同步UI和我们新存储的数据。
在浏览器里使用你创建的dapp
现在你可以开始使用你的dapp啦!
安装和配置MetaMask
在浏览器中与我们的dapp交互的最简单的方法是通过MetaMask,Chrome的扩展插件。
在您的浏览器中安装MetaMask。
安装完成后,您会看到地址栏旁边的MetaMask狐狸图标。 点击图标,你会看到这个屏幕出现
点击接受接受隐私声明。
那么你会看到使用条款。 阅读它们,滚动到底部,然后单击接受。
现在你会看到最初的MetaMask屏幕。 点击Import Existing DEN。
6.在标有“Wallet Seed”的框中,输入登陆Truffle Develop时显示的助记词:
candy maple cake sugar pudding cream honey rich smooth crumble sweet treat
警告:请勿在以太网主网络(mainnet)上使用此助记符。 如果您将ETH发送到由此助记符生成的任何帐户,您将会失去所有您发送到该地址上的ETH!
在下面输入密码,然后单击 OK。
- 现在我们需要将MetaMask连接到由Truffle Develop创建的区块链。 点击显示“Main Network”的菜单并选择Custom RPC。
- 在标题为“New RPC URL”的框中输入
http:// localhost:9545
,然后单击Save。 - 点击"Settings"旁边的向左箭头关闭页面并返回到“帐户”页面。Truffle Develop
创建的每个账户都有100个eth。 你会注意到它在第一个账户上稍微少一些,因为当合同本身被部署时使用了一些gas。
现在配置完成。
安装和配置lite-server
我们现在可以启动一个本地Web服务器并使用dapp。 我们正在使用lite-server
库来为我们的静态文件提供服务。 这是pet-shop
Truffle Box里已经有的服务器,但让我们来看看它是如何工作的。
- 在文本编辑器(在项目的根目录下)中打开
bs-config.json
并检查其内容:
{"server": {"baseDir": ["./src", "./build/contracts"]}
}
这告诉lite-server
哪些文件包含在我们的基础目录中。 我们为我们的网站文件添加./src
目录,为合同工件添加./build/contracts
目录。
我们还在项目的根目录下的package.json
文件中的scripts
对象中添加了一个dev
命令。 scripts
对象允许我们将控制台命令别名为单个npm
命令。 在这种情况下,我们只是做一个单一的命令,但可能有更复杂的配置。 比如像:
"scripts": {"dev": "lite-server","test": "echo \"Error: no test specified\" && exit 1"
},
这告诉npm在我们从控制台执行npm run dev
的时候运行我们的lite-server
的本地安装。
使用dapp
- 启动本地Web服务器:
npm run dev
开发服务器将启动并自动打开一个新的浏览器选项卡,其中包含您的dapp。
Pete's Pet Shop
要使用dapp,请点击您选择的宠物上的 Adopt 按钮。
系统将自动提示您通过MetaMask批准交易。 点击 Submit 以批准交易。
Adoption transaction review
就像我们期望的一样,你会看到被宠物宠物改变的旁边的按钮,说“成功”,并被禁用,因为宠物已经被领养。
Adoption success
注意:如果按钮不会自动改变为"Success",刷新浏览器中的应用程序应该会触发它。
MetaMask transaction
恭喜! 你已经迈出了一大步,成为一个成熟的dapp开发者。 为了在本地进行开发,您可以使用所有工具开始制作更高级的绘图。 如果您希望让您的dapp能够让其他人使用,请继续关注我们将来部署到Ropsten测试网络的教程。
(完)
手把手教创建你的第一个以太智能合约:ETHEREUM PET SHOP(译)相关推荐
- [译] 使用 Web3 和 Vue.js 来创建你的第一个以太坊 dAPP(第二部分)
原文地址:Create your first Ethereum dAPP with Web3 and Vue.JS (Part 2) 原文作者:Alt Street 译文出自:掘金翻译计划 本文永久链 ...
- 手把手教你在Imtoken上收录你的合约
1.配置安装Git环境 点击此处进行下载安装 安装过程中默认一直下一步即可. 2.Fork项目到你的仓库 进入Github项目下(地址点击这里),没有Github账号的话去注册一个,以后必须要使用的. ...
- 手把手教你将普通wifi路由器变为智能广告路由器
一步一步教你将普通的wifi路由器变为智能广告路由器 相信大家对WiFi智能广告路由器已经不再陌生了,现在很多公共WiFi上网,都需要登录并且验证,这也就是WiFi广告路由器的最重要的功能.大致就是下 ...
- 区块链智能合约Coursera(第一周)智能合约基础
课程链接 https://www.coursera.org/learn/smarter-contracts/home/week/1 这是区块链专项课程的第二门课 智能合约 Smart Contract ...
- 「不良视频」如何消灭?她手把手教你走出第一步
不严肃的开场白 视频社交已经成为了时下最in的社交方式,相较于传统的文字.语音聊天,使用亲身录制的短视频.幽默搞笑的图片.表情包与好友进行交流,不仅更加风趣且更具人情味. 而随着视频社交的流行,每天产 ...
- 「不良视频」如何消灭?她手把手教你走出第一步!
不严肃的开场白 视频社交已经成为了时下最in的社交方式,相较于传统的文字.语音聊天,使用亲身录制的短视频.幽默搞笑的图片.表情包与好友进行交流,不仅更加风趣且更具人情味. 而随着视频社交的流行,每天产 ...
- 手把手教你用Python搭建一个AI智能问答系统
导读:智能问答系统是自然语言处理的一个重要分支.今天我们将利用分词处理以及搜索引擎搭建一个智能问答系统. 本文经授权转自公众号CSDN(ID:CSDNnews) 作者:李秋键 具体的效果如下所示: 下 ...
- 手把手教你用Python搭建一个AI智能问答系统!
导读:智能问答系统是自然语言处理的一个重要分支.今天我们将利用分词处理以及搜索引擎搭建一个智能问答系统. 具体的效果如下所示: 私信小编01 领取完整代码! 下面简单了解下智能问答系统和自然语言处理 ...
- python做一个问答系统_手把手教你用Python搭建一个AI智能问答系统
导读:智能问答系统是自然语言处理的一个重要分支.今天我们将利用分词处理以及搜索引擎搭建一个智能问答系统. 本文经授权转自公众号CSDN(ID:CSDNnews) 作者:李秋键 具体的效果如下所示: 下 ...
最新文章
- 实时实例分割的Deep Snake:CVPR2020论文点评
- HTTP协议(HyperText Transfer Protocol,超文本传输协议)
- STM32之ADC实例(基于DMA方式)
- Netty详解(三):Netty 入门应用
- 关于养花---感叹一把
- android mvvm框架搭建_轻松搭建基于JetPack组件的MVVM框架
- ubuntu设置自动休眠
- malloc()与calloc区别
- 做自媒体和有没有文化没有太大关系
- linux中sar命令的选项很多,Linux sar 命令详解
- 【Hive】性能调优 - EXPLAIN
- 年龄是计数还是计量_MSA你只知道计量型和计数型?有哪些类型?分别是什么方法?...
- io 流 txt本地生成
- java面向对象面试怎么回答_Java面向对象面试题
- nachos中文教程java_Nachos3.4系列-2 Makefile 与Nachos中文教程 【转】
- java 6面骰子_Java实现的简单掷骰子游戏示例
- 生物信息学python常用脚本_生物信息工程师必备的编程语言清单
- 开发用于异构环境的可生存云多机器人框架
- 挂一张表,省的再瞎眼
- 笔记本电脑亮度无法调节问题
热门文章
- [转]避免PHP-FPM内存泄漏导致内存耗尽
- Java代码性能优化技巧
- self.view = nil 和[self.view release]的区别
- 基于马克思哲学原理论外在美与内在美2017-12-31
- [2DPIC调试笔记]parameter_antenna_radiation1013(3)
- 寻找免费的阿里云云盾隐秘购买入口,申请并下载ssl证书(https证书)的详细步骤
- Python-深拷贝-浅拷贝-时间消耗
- 轮廓线重建:二维平行轮廓线重建理论和方法
- VTK修炼之道19:图像基本操作_图像像素值的访问与修改
- javascript中的try finally