目录
一、前言
二、View函数节省Gas
1、讲解
2、实战
1.要求
2.代码
三、在内存中声明数组
1、讲解
2、实战
1.要求
2.代码
一、前言
看了一些区块链的教程,论文,在网上刚刚找到了一个项目实战,CryptoZombies。
前面我们讲到了Gas,今天我们再来讲一下如何节约Gas。
如果你想了解更多有关于机器学习、深度学习、区块链、计算机视觉等相关技术的内容,想与更多大佬一起沟通,那就扫描下方二维码加入我们吧!
二、View函数节省Gas
1、讲解
当玩家从外部调用一个view
函数,是不需要支付一分 gas 的。原因如下:
view
函数不会真正改变区块链上的任何数据 - 它们只是读取。因此用 view
标记一个函数,意味着告诉 web3.js
,运行这个函数只需要查询你的本地以太坊节点,而不需要在区块链上创建一个事务(事务需要运行在每个节点上,因此花费 gas)。
所以在所能只读的函数上标记上表示“只读”的“external view
声明,就能减少在 DApp 中 gas 用量。
但是我们要注意一种情况:
如果一个 view
函数在另一个函数的内部被调用,而调用函数与 view
函数的不属于同一个合约,也会产生调用成本。这是因为如果主调函数在以太坊创建了一个事务,它仍然需要逐个节点去验证。所以标记为 view
的函数只有在外部调用时才是免费的。
2、实战
1.要求
我们来写一个”返回某玩家的整个僵尸军团“的函数。当我们从 web3.js
中调用它,即可显示某一玩家的个人资料页。
1.创建一个名为 getZombiesByOwner
的新函数。它有一个名为 _owner
的 address
类型的参数。
2.
将其申明为 external view
函数,这样当玩家从 web3.js
中调用它时,不需要花费任何 gas。
3.函数需要返回一个uint []
(uint
数组)。
2.代码
pragma solidity >&#61;0.5.0 <0.6.0;
import "./zombiefeeding.sol";
contract ZombieHelper is ZombieFeeding {modifier aboveLevel(uint _level, uint _zombieId) {require(zombies[_zombieId].level >&#61; _level);_;}function changeName(uint _zombieId, string calldata _newName) external aboveLevel(2, _zombieId) {require(msg.sender &#61;&#61; zombieToOwner[_zombieId]);zombies[_zombieId].name &#61; _newName;}function changeDna(uint _zombieId, uint _newDna) external aboveLevel(20, _zombieId) {require(msg.sender &#61;&#61; zombieToOwner[_zombieId]);zombies[_zombieId].dna &#61; _newDna;}// Create your function herefunction getZombiesByOwner(address _owner) external view returns(uint[] memory) {}}
三、在内存中声明数组
1、讲解
在之前&#xff0c;我们已经入门了解过函数修饰符。
Solidity 使用storage
(存储)是相当昂贵的&#xff0c;”写入“操作尤其贵。这是因为&#xff1a;
无论是写入还是更改一段数据&#xff0c; 这都将永久性地写入区块链。这需要在全球数千个节点的硬盘上存入这些数据&#xff0c;随着区块链的增长&#xff0c;拷贝份数更多&#xff0c;存储量也就越大。这是需要成本的&#xff01;
为了降低成本&#xff0c;不到万不得已&#xff0c;避免将数据写入存储。当然这也会导致效率低下的编程逻辑 - 比如每次调用一个函数&#xff0c;都需要在 memory
(内存) 中重建一个数组&#xff0c;而不是简单地将上次计算的数组给存储下来以便快速查找。
在数组后面加上 memory
关键字&#xff0c; 表明这个数组是仅仅在内存中创建&#xff0c;不需要写入外部存储&#xff0c;并且在函数调用结束时它就解散了。与在程序结束时把数据保存进 storage
的做法相比&#xff0c;内存运算可以大大节省gas开销 -- 把这数组放在view
里用&#xff0c;完全不用花钱。
function getArray() external pure returns(uint[]) {// 初始化一个长度为3的内存数组uint[] memory values &#61; new uint[](3);// 赋值values.push(1);values.push(2);values.push(3);// 返回数组return values;
}
在使用过程中要注意&#xff1a;内存数组 必须 用长度参数&#xff08;在本例中为3
&#xff09;创建。目前不支持 array.push()
之类的方法调整数组大小&#xff0c;在未来的版本可能会支持长度修改。
这个也很好理解&#xff0c;比如在C&#43;&#43;中&#xff0c;我们如果不用new关键字创建数组&#xff0c;那么数组长度在定义过程中&#xff0c;必须是一个常量。不能是一个变量。
2、实战
1.要求
定义一个修饰符&#xff0c;通过传入的level
参数来限制僵尸使用某些特殊功能。
1.声明一个名为result
的uint [] memory&#39;
&#xff08;内存变量数组&#xff09;。
2.将其设置为一个新的 uint
类型数组。数组的长度为该 _owner
所拥有的僵尸数量&#xff0c;这可通过调用 ownerZombieCount [_ owner]
来获取。
3.函数结束&#xff0c;返回 result
。目前它只是个空数列。
2.代码
pragma solidity >&#61;0.5.0 <0.6.0;import "./zombiefeeding.sol";contract ZombieHelper is ZombieFeeding {modifier aboveLevel(uint _level, uint _zombieId) {require(zombies[_zombieId].level >&#61; _level);_;}function changeName(uint _zombieId, string calldata _newName) external aboveLevel(2, _zombieId) {require(msg.sender &#61;&#61; zombieToOwner[_zombieId]);zombies[_zombieId].name &#61; _newName;}function changeDna(uint _zombieId, uint _newDna) external aboveLevel(20, _zombieId) {require(msg.sender &#61;&#61; zombieToOwner[_zombieId]);zombies[_zombieId].dna &#61; _newDna;}function getZombiesByOwner(address _owner) external view returns(uint[] memory) {// Start hereuint[] memory result &#61; new uint[](ownerZombieCount[_owner]);return result;}}