使用RPC接口新建EOS账户

##1、POST http://127.0.0.1:8888/v1/chain/abi_json_to_bin (序列化新建账号的 json)

{
	"code": "eosio",
	"action": "newaccount",
	"args": {
		"creator": "bitcoin",
		"name": "eason",
		"owner": {
			"threshold": 1,
			"keys": [{
				"key": "EOS4ufZoTw95yHJS6Cyz3h4w5a2W4cyYpMYRnd7gbFZuCfPxUFS6r",
				"weight": 1
			}],
			"accounts": [],
			"waits": []
		},
		"active": {
			"threshold": 1,
			"keys": [{
				"key": "EOS4ufZoTw95yHJS6Cyz3h4w5a2W4cyYpMYRnd7gbFZuCfPxUFS6r",
				"weight": 1
			}],
			"accounts": [],
			"waits": []
		}
	}
}

##2、POST http://127.0.0.1:8888/v1/wallet/sign_transaction(签名新建账号的交易)

[
  {
    "expiration" : "2018-05-17T09:54:06.500",
    "signatures" : [

    ],
    "actions" : [
      {
        "account" : "eosio",
        "data" : "000000603a8ab23b000000ca3d364dfb0100000001000202aba1b7d9fc5de9dd93308dc5ebedcb066c8e5b36970bfd82ae715d9e8c3060010000000100000001000202aba1b7d9fc5de9dd93308dc5ebedcb066c8e5b36970bfd82ae715d9e8c306001000000",
        "authorization" : [
          {
            "actor" : "bitcoin",
            "permission" : "active"
          }
        ],
        "name" : "newaccount"
      }
    ],
    "ref_block_prefix" : 4033496171,
    "ref_block_num" : 363759
  },
  [
    "EOS5wQ4HaFFDxyfc23dZNXUTGBHepM1vXGfr1vkfWHfRfvAMXP7VV"
  ],
  ""
]

##3、http://127.0.0.1:8888/v1/chain/push_transaction (把签名后的交易push 推送到 EOS 系统中,即新建账号完成

{
  "compression" : "none",
  "transaction" : {
    "ref_block_prefix" : 4033496171,
    "actions" : [
      {
        "account" : "eosio",
        "data" : "000000603a8ab23b000000ca3d364dfb0100000001000202aba1b7d9fc5de9dd93308dc5ebedcb066c8e5b36970bfd82ae715d9e8c3060010000000100000001000202aba1b7d9fc5de9dd93308dc5ebedcb066c8e5b36970bfd82ae715d9e8c306001000000",
        "authorization" : [
          {
            "actor" : "bitcoin",
            "permission" : "active"
          }
        ],
        "name" : "newaccount"
      }
    ],
    "expiration" : "2018-05-17T09:54:06.500",
    "ref_block_num" : 363759
  },
  "signatures" : [
    "SIG_K1_KY58QhP4jWLJWr7cVkahgL3JAjC8QMK5jnHurFUmn8xU71v6Mh4DmgjY75DxmWE6Je457N6MRM7GapxU43hywnAWKEmC1W"
  ]
}

(代币转账 和 新建账号的 sign_transaction、push_transaction 类似,主要就是 智能合约的不同 和 调用的action 的不同 以及 action 中具体的参数不同。)

新建账号(newaccount)需要用 「已有的账号」 创建「 新账号」

内部是 已有账号 调用系统智能合约eosio中的 newaccount 的 action

新建账号的交易需要用 创建者 的 私钥签名交易(sign_transaction),然后 推送签名后的交易 (push_transaction)到区块链中。

a. sign_transaction 图示

b. push_transaction 图示

#具体接口

1、POST http://127.0.0.1:8888/v1/chain/abi_json_to_bin (序列化新建账号的 json)

请求参数

请求参数:

参数名称 参数类型 描述
code string 系统智能合约,默认填写“eosio”
action string 智能合约中的action,默认填写“newaccount”
creator string 创建者
name string 新建账号名
key string 新建账号的公钥
请求示例:

{
  "code": "eosio",
  "action": "newaccount",
  "args": {
    "creator": "bitcoin",
    "name": "eason",
    "owner": {
      "threshold": 1,
      "keys": [
        {
          "key": "EOS4ufZoTw95yHJS6Cyz3h4w5a2W4cyYpMYRnd7gbFZuCfPxUFS6r", //owner public key
          "weight": 1
        }
      ],
      "accounts": [],   
      "waits": []      
    },
    "active": {
      "threshold": 1,
      "keys": [
        {
          "key": "EOS4ufZoTw95yHJS6Cyz3h4w5a2W4cyYpMYRnd7gbFZuCfPxUFS6r", //active public key
          "weight": 1
        }
      ],
      "accounts": [],    
      "waits": []        
    }
  }
}

响应参数

参数名称 参数类型 描述
binargs string 序列化的结果,在sign_transaction 和 push_transaction 中作为 data 请求参数
响应示例

{
    "binargs": "000000603a8ab23b000000ca3d364dfb0100000001000202aba1b7d9fc5de9dd93308dc5ebedcb066c8e5b36970bfd82ae715d9e8c3060010000000100000001000202aba1b7d9fc5de9dd93308dc5ebedcb066c8e5b36970bfd82ae715d9e8c306001000000"
}

2、GET http://127.0.0.1:8888/v1/chain/get_info (获取 EOS 区块链的最新区块号)

响应参数

参数名称 参数类型 描述
head_block_num number 最新区块号
响应示例

{
    "server_version": "13952d45",
    "head_block_num": 359934,
    "last_irreversible_block_num": 359934,
    "last_irreversible_block_id": "a69af2c4aa56b5c4bd1cdf9c2acb1a7796bbc3043954e36da182a144ddcf58fb",
    "head_block_id": "a69af2c4aa56b5c4bd1cdf9c2acb1a7796bbc3043954e36da182a144ddcf58fb",
    "head_block_time": "2018-05-17T09:02:12",
    "head_block_producer": "eosio",
    "virtual_block_cpu_limit": 100000000,
    "virtual_block_net_limit": 1048576000,
    "block_cpu_limit": 99900,
    "block_net_limit": 1048576
}

3、POST http://127.0.0.1:8888/v1/chain/get_block (获取最新区块的具体信息)

请求参数

参数名称 参数类型 描述
block_num_or_id number 最新区块号,上一个响应结果中的 head_block_num

{
  "block_num_or_id":359934
}

响应参数

参数名称 参数类型 描述
timestamp string 最新区块的生成时间
block_num number 区块号,作为sign_transaction 和 push_transaction中的 ref_block_num请求参数
ref_block_prefix number 作为sign_transaction 和 push_transaction中的 ref_block_prefix 请求参数

响应示例

{
    "timestamp": "2018-05-17T09:02:12.500",
    "producer": "eosio",
    "confirmed": 0,
    "previous": "00057dfd5044aba0d750eff1fbb84ac92cbf29db1354968816fd2a9aefb0a0b4",
    "transaction_mroot": "0000000000000000000000000000000000000000000000000000000000000000",
    "action_mroot": "dee87e5d025383574ac12c310faf6b759fba52bd19977399b7ebf6ccdd81c7fa",
    "schedule_version": 0,
    "header_extensions": [],
    "producer_signature": "SIG_K1_KVX3RRTS4ch9m6bWDctsAhDWtFydTrg3mW7PaqCXnBZZWezBW23enggeW4ijuWBHBVsDoxzjMvspoFtPsU5nmau4ZYomZo",
    "transactions": [],
    "block_extensions": [],
    "id": "a69af2c4aa56b5c4bd1cdf9c2acb1a7796bbc3043954e36da182a144ddcf58fb",
    "block_num": 359934,
    "ref_block_prefix": 1943477914
}

4、POST http://127.0.0.1:8888/v1/wallet/unlock (解锁钱包,签名交易前,需要解锁账号所在的钱包)

请求参数

参数名称 参数类型 描述
string 钱包名称
string 钱包密码
请求示例

["liu","PW5KjWHnhL5kSRxpWyHQj321dFsZN62HAbZjVSqnDvzKMuEKBZ1T9"]

响应示例

{}  //成功解锁钱包,返回{}

5、POST http://127.0.0.1:8888/v1/wallet/sign_transaction(签名新建账号的交易)

请求的参数

参数名称 参数类型 描述
ref_block_num number 上面获得的最新区块号
ref_block_prefix number 上面获得的最新区块号相关信息
expiration string 过期时间 = timestamp 加上 一段时间 ,例如1分钟
account string 调用系统智能合约账号名,默认为 eosio
name string 新建账号的action,默认为 newaccount
actor string 创建者 账户名
data string abi_json_to_bin 序列化后的 值 binargs
string 创建者的 公钥
请求示例

[
  {
    "ref_block_num": 363759,
    "ref_block_prefix": 4033496171,
    "expiration": "2018-05-17T09:54:06.500",
    "actions": [
      {
        "account": "eosio",  //有 newaccount 的 action 的智能合约账号
        "name": "newaccount",
        "authorization": [
          {
            "actor": "bitcoin",
            "permission": "active"
          }
        ],
        "data": "000000603a8ab23b000000ca3d364dfb0100000001000202aba1b7d9fc5de9dd93308dc5ebedcb066c8e5b36970bfd82ae715d9e8c3060010000000100000001000202aba1b7d9fc5de9dd93308dc5ebedcb066c8e5b36970bfd82ae715d9e8c306001000000" // //abi_json_to_bin 的响应参数 binargs
      }
    ],
    "signatures": []
  },
  [
    "EOS5wQ4HaFFDxyfc23dZNXUTGBHepM1vXGfr1vkfWHfRfvAMXP7VV" //创建者的公钥(交易发起者的公钥),其实是用的公钥对应的私钥进行签名的,签名前需要先解锁包含此私钥的钱包
  ],
  ""
]

响应参数

参数名称 参数类型 描述
signatures string 新建账号的交易 的签名结果,最后 push_transaction 中使用
响应示例

{
    "expiration": "2018-05-17T09:54:06",
    "ref_block_num": 36079,
    "ref_block_prefix": 4033496171,
    "max_net_usage_words": 0,
    "max_cpu_usage_ms": 0,
    "delay_sec": 0,
    "context_free_actions": [],
    "actions": [
        {
            "account": "eosio",
            "name": "newaccount",
            "authorization": [
                {
                    "actor": "bitcoin",
                    "permission": "active"
                }
            ],
            "data": "000000603a8ab23b000000ca3d364dfb0100000001000202aba1b7d9fc5de9dd93308dc5ebedcb066c8e5b36970bfd82ae715d9e8c3060010000000100000001000202aba1b7d9fc5de9dd93308dc5ebedcb066c8e5b36970bfd82ae715d9e8c306001000000" 
        }
    ],
    "transaction_extensions": [],
    "signatures": [
    "SIG_K1_KY58QhP4jWLJWr7cVkahgL3JAjC8QMK5jnHurFUmn8xU71v6Mh4DmgjY75DxmWE6Je457N6MRM7GapxU43hywnAWKEmC1W"   // 签名 用在 push_transaction 中
    ],
    "context_free_data": []
}

6、http://127.0.0.1:8888/v1/chain/push_transaction (把签名后的交易push 推送到 EOS 系统中,即新建账号完成)

请求参数

参数名称 参数类型 描述
compression string 默认 none
data string abi_json_to_bin 序列化后的 值 binargs
signatures string 交易签名后的结果
请求示例

{
  "compression": "none",
  "transaction": {
    "expiration": "2018-05-17T09:54:06.500",
    "ref_block_num": 363759,
    "ref_block_prefix": 4033496171,
    "actions": [
      {
        "account": "eosio",
        "name": "newaccount",
        "authorization": [
          {
            "actor": "bitcoin",
            "permission": "active"
          }
        ],
        "data": "000000603a8ab23b000000ca3d364dfb0100000001000202aba1b7d9fc5de9dd93308dc5ebedcb066c8e5b36970bfd82ae715d9e8c3060010000000100000001000202aba1b7d9fc5de9dd93308dc5ebedcb066c8e5b36970bfd82ae715d9e8c306001000000"    //abi_json_to_bin 的响应参数 binargs
      }
    ]
  },
  "signatures": ["SIG_K1_KY58QhP4jWLJWr7cVkahgL3JAjC8QMK5jnHurFUmn8xU71v6Mh4DmgjY75DxmWE6Je457N6MRM7GapxU43hywnAWKEmC1W"]
}

响应示例

{
    "transaction_id": "2047702bfdc4678aabe123f335b4b5f604203edf7b4de8e42fa2c9211d4de075",
    "processed": {
        "id": "2047702bfdc4678aabe123f335b4b5f604203edf7b4de8e42fa2c9211d4de075",
        "receipt": {
            "status": "executed",
            "cpu_usage_us": 390,
            "net_usage_words": 25
        },
        "elapsed": 390,
        "net_usage": 200,
        "scheduled": false,
        "action_traces": [
            {
                "receipt": {
                    "receiver": "eosio",
                    "act_digest": "ae18e275184e7defe81be175711cd24206990518963f857715e98755f713957c",
                    "global_sequence": 365444,
                    "recv_sequence": 365419,
                    "auth_sequence": [
                        [
                            "bitcoin",
                            27
                        ]
                    ]
                },
                "act": {
                    "account": "eosio",
                    "name": "newaccount",
                    "authorization": [
                        {
                            "actor": "bitcoin",
                            "permission": "active"
                        }
                    ],
                    "data": {
                        "creator": "bitcoin",
                        "name": "zhangjie",
                        "owner": {
                            "threshold": 1,
                            "keys": [
                                {
                                    "key": "EOS4ufZoTw95yHJS6Cyz3h4w5a2W4cyYpMYRnd7gbFZuCfPxUFS6r",
                                    "weight": 1
                                }
                            ],
                            "accounts": [],
                            "waits": []
                        },
                        "active": {
                            "threshold": 1,
                            "keys": [
                                {
                                    "key": "EOS4ufZoTw95yHJS6Cyz3h4w5a2W4cyYpMYRnd7gbFZuCfPxUFS6r",
                                    "weight": 1
                                }
                            ],
                            "accounts": [],
                            "waits": []
                        }
                    },
                    "hex_data": "000000603a8ab23b000000ca3d364dfb0100000001000202aba1b7d9fc5de9dd93308dc5ebedcb066c8e5b36970bfd82ae715d9e8c3060010000000100000001000202aba1b7d9fc5de9dd93308dc5ebedcb066c8e5b36970bfd82ae715d9e8c306001000000"
                },
                "elapsed": 163,
                "cpu_usage": 0,
                "console": "",
                "total_cpu_usage": 0,
                "trx_id": "2047702bfdc4678aabe123f335b4b5f604203edf7b4de8e42fa2c9211d4de075",
                "inline_traces": []
            }
        ],
        "except": null
    }
}

From:使用RPC接口新建EOS账户 - 实战

08/15/2018 16:23 下午 posted in  EOS

EOS测试账号

EOS测试网络
API接口文档

节点:192.168.50.159,
其中nodeosd相关API请访问 http://192.168.50.159:8888,
Wallet相关API请访问 http://192.168.50.159:3000

##1.测试网

http://jungle.cryptolions.io/#accountInfo

##2.测试API

https://github.com/CryptoLions/EOS-Jungle-Testnet

##2.测试账号
liulian12345

Description EOS Token Sale Claim Key
Public key   EOS7LAzv5dbrzprL8DZuHYMKfTjQQ9AUXPAKzQfSFUed1GoJuTXmd
Private key  5Hsk6wB2MPqGPrU53jSVGHm3uvoqWJk3rCrnkGzEGc7HrH28n7t

localhost

Private key: 5JUJHBJF18Qs8WMdXnb5488AFzdAZcfKZ5QAX1srMLWa62rsL4e
Public key: EOS6BQZmWhPKZLVg4YDcR6KdrYb1dqjDAWxyGPHRTD6mpqJ1dACAp

Private key: 5KXZjxLz4qczfCGmWYUzxhiKyRN1eYaGCRZaT1hHAFJs9xsmCKS
Public key: EOS5uLxwJQgpEJteBxBTKiqWnyWiJTQAzAqx71M5AuMZ917oMv4g4

Jungle:

测试网络账号创建的block: 13658485

{
    "timestamp": "2018-09-10T09:33:29.000",
    "producer": "funnyhamster",
    "confirmed": 0,
    "previous": "00d06974a6485b5e6c80d804313d6c0058870848f103735fb24ae63851a61f66",
    "transaction_mroot": "00eb3fbea28360f5ab26cae9cf50c081baf5579800aa6062f059808ad9b44d7a",
    "action_mroot": "10a2e5ea43847603f2d68bc5b403eea5ec744b70d38ff1d73e859fbe9413e6c6",
    "schedule_version": 217,
    "new_producers": null,
    "header_extensions": [],
    "producer_signature": "SIG_K1_K26xvdaxUhQcE6Cga1fQfDLKxVYKpAG9nNk8isA4Pby8ZH2Jp8y8Z6ki8p3ZHAvuBFTJDrNTAW6va9Xbd1R7E8DoosQdXE",
    "transactions": [
        {
            "status": "executed",
            "cpu_usage_us": 7238,
            "net_usage_words": 43,
            "trx": {
                "id": "929ecaca3aa6aa09a515239ff79e78b4df62bf87809a45a7baf2848696c3f094",
                "signatures": [
                    "SIG_K1_KVJz9woQUmHRCot6prUeeY3H8vjZBSStUhefQhrmzsCDKS8MkqYV2ANgfPeSpdEgmAnqX39WnKraCGiwJYedzKkbu5nBnS"
                ],
                "compression": "none",
                "packed_context_free_data": "",
                "context_free_data": [],
                "packed_trx": "e03a965b2868e3b6720100000000030000000000ea305500409e9a2264b89a0150c810611a17b58b00000000a8ed32326650c810611a17b58b208269a69996b1ca01000000010002aa1bf6ea68794eb7ad364ea9e3438171866a557f1db49f596c67eab2a6a0d7230100000001000000010002aa1bf6ea68794eb7ad364ea9e3438171866a557f1db49f596c67eab2a6a0d723010000000000000000ea3055000000004873bd3e0150c810611a17b58b00000000a8ed32322050c810611a17b58b208269a69996b1caa08601000000000004454f53000000000000000000ea305500003f2a1ba6a24a0150c810611a17b58b00000000a8ed32323150c810611a17b58b208269a69996b1caa08601000000000004454f5300000000a08601000000000004454f53000000000100",
                "transaction": {
                    "expiration": "2018-09-10T09:35:28",
                    "ref_block_num": 26664,
                    "ref_block_prefix": 24295139,
                    "max_net_usage_words": 0,
                    "max_cpu_usage_ms": 0,
                    "delay_sec": 0,
                    "context_free_actions": [],
                    "actions": [
                        {
                            "account": "eosio",
                            "name": "newaccount",
                            "authorization": [
                                {
                                    "actor": "liulian12345",
                                    "permission": "active"
                                }
                            ],
                            "data": {
                                "creator": "liulian12345",
                                "name": "testhahaha12",
                                "owner": {
                                    "threshold": 1,
                                    "keys": [
                                        {
                                            "key": "EOS6BQZmWhPKZLVg4YDcR6KdrYb1dqjDAWxyGPHRTD6mpqJ1dACAp",
                                            "weight": 1
                                        }
                                    ],
                                    "accounts": [],
                                    "waits": []
                                },
                                "active": {
                                    "threshold": 1,
                                    "keys": [
                                        {
                                            "key": "EOS6BQZmWhPKZLVg4YDcR6KdrYb1dqjDAWxyGPHRTD6mpqJ1dACAp",
                                            "weight": 1
                                        }
                                    ],
                                    "accounts": [],
                                    "waits": []
                                }
                            },
                            "hex_data": "50c810611a17b58b208269a69996b1ca01000000010002aa1bf6ea68794eb7ad364ea9e3438171866a557f1db49f596c67eab2a6a0d7230100000001000000010002aa1bf6ea68794eb7ad364ea9e3438171866a557f1db49f596c67eab2a6a0d72301000000"
                        },
                        {
                            "account": "eosio",
                            "name": "buyram",
                            "authorization": [
                                {
                                    "actor": "liulian12345",
                                    "permission": "active"
                                }
                            ],
                            "data": {
                                "payer": "liulian12345",
                                "receiver": "testhahaha12",
                                "quant": "10.0000 EOS"
                            },
                            "hex_data": "50c810611a17b58b208269a69996b1caa08601000000000004454f5300000000"
                        },
                        {
                            "account": "eosio",
                            "name": "delegatebw",
                            "authorization": [
                                {
                                    "actor": "liulian12345",
                                    "permission": "active"
                                }
                            ],
                            "data": {
                                "from": "liulian12345",
                                "receiver": "testhahaha12",
                                "stake_net_quantity": "10.0000 EOS",
                                "stake_cpu_quantity": "10.0000 EOS",
                                "transfer": 1
                            },
                            "hex_data": "50c810611a17b58b208269a69996b1caa08601000000000004454f5300000000a08601000000000004454f530000000001"
                        }
                    ],
                    "transaction_extensions": []
                }
            }
        }
    ],
    "block_extensions": [],
    "id": "00d06975eb6fb6c7c281c3f68819489c27a2f876e3820f8bc37bb09f09f6d4ef",
    "block_num": 13658485,
    "ref_block_prefix": 4140007874
}
08/15/2018 15:00 下午 posted in  EOS

EOSIO Dawn 4.0 系统合约解读

From:EOSIO Dawn 4.0 系统合约解读

Dawn 4.0 发布后,基本上白皮书上提到的特性都得到了实现,特别是投票、资源抵押和内存买卖。

EOS 的网络带宽和计算资源是通过抵押代币的方式获得,如果不需要了可以解除抵押收回原先的代币。

投票的权重和抵押代币所获得的带宽和计算资源相关,抵押了越多,投票的权重越大。同时投票的权重会随着时间的推移减少,这个设定鼓励用户持续更新自己的投票。

而存储资源则是通过购买来得到,但是并不会消耗,当不再需要存储资源的时候可以出售。但是存储资源相对于EOS代币的价格会随着市场对于存储资源的需求而变动。

而这些功能都是在 eosio.system 这个智能合约中实现。

本文试图通过分析 eosio.system 中所有的功能函数来解读 eos 系统功能。

#函数列表
在 eosio.system.hpp 头文件中包含以下函数声明:

##delegatebw

void onblock( block_timestamp timestamp, account_name producer );

void delegatebw( account_name from, account_name receiver,asset stake_net_quantity, asset stake_cpu_quantity, bool transfer );

delegatebw 函数用来实现抵押代币获取 cpu 和带宽资源。参数定义:

  • from : 从哪个账号扣除用来抵押的代币
  • receiver : 抵押的代币的接受者,表示抵押获取的资源作用在哪个账号上
  • stake_net_quantity : 用来抵押带宽资源的代币数量
  • stake_cpu_quantity : 用来抵押计算资源的代币数量
  • transfer : 是否接受者可以主动解除抵押获得代币,如果不是,只有发起者能够解除抵押收回代币

##undelegatebw

void undelegatebw( account_name from, account_name receiver,asset unstake_net_quantity, asset unstake_cpu_quantity );
undelegatebw 函数用来解除抵押,释放资源,收回代币,参数定义:
  • from : 解除用哪个账号所抵押的代币
  • receiver : 解除作用在哪个账号上的抵押代币
  • unstake_net_quantity : 解除用来获取带宽资源的代币数量
  • unstake_cpu_quantity : 解除用来获取计算资源的代币数量
  • 解除抵押之后,资源会马上释放,同时投票权重也相应消失。但是代币需要等待一定时间才能回到账号里,目前的设定是3天。

##buyram

void buyram( account_name buyer, account_name receiver, asset tokens );

void buyrambytes( account_name buyer, account_name receiver, uint32_t bytes );

这两个函数的作用是购买存储资源,区别是买特定数量的代币还是买特定大小的内容。参数定义:

  • buyer : 购买存储资源的账号
  • receiver : 接受存储资源的账号
  • tokens : 购买存储资源所用的代币数量
  • bytes : 都买存储资源空间大小的数值

##sellram

void sellram( account_name receiver, int64_t bytes );

sellram函数功能是出售不需要的存储资源。参数定义:

  • receiver : 出售资源代币的接受账号
  • bytes : 出售多少空间的存储资源
    出售后资源会马上释放,收入的代币也会马上入账。

##refund

void refund( account_name owner );

refund 函数 在 undelegatebw 函数解除抵押后调用,作用是把抵押的代币退回账户。

##regproducer

void regproducer( const account_name producer, const public_key& producer_key, const std::string& url, uint16_t location );

regproducer 函数的作用是注册成为超级节点候选人。参数定义:

  • producer : 候选节点的账户名
  • producer_key : 候选节点的账户公钥
  • url : 候选节点的网站地址
  • location : 候选节点的机房地理位置
    注册成为候选人后就可以接受用户的投票了。

##unregprod

void unregprod( const account_name producer );

unregprod 函数的作用的取消成为候选人。

##voteproducer

void setram( uint64_t max_ram_size );

void voteproducer( const account_name voter, const account_name proxy, const std::vector<account_name>& producers );

voteproducer 函数的作用是投票。参数定义:

  • voter : 投票人
  • proxy : 代理投票人
  • producers : 得票人列表
    有两种投票模式,代理模式和直接投票模式。代理模式是将投票权重委托给一个代理人,让他帮你投票。直接投票模式就是直接投票给你信任的超级节点,最多30个。

#regproxy

void regproxy( const account_name proxy, bool isproxy );

regproxy 函数的作用是注册成为代理人,接受其他用户的委托。

##claimrewards

void claimrewards( const account_name& owner );

claimrewards 函数的作用是支付超级节点的奖励。

void setpriv( account_name account, uint8_t ispriv );

#测试方法
开发者可以通过 cleos system子命令来测试这些功能。

如果要加入一个测试网络成为出块人,步骤应该是这样:

  1. 注册账号
  2. 启动节点同步区块
  3. 注册成为候选人
  4. 抵押代币获得资源和投票权重
  5. 投票给自己
  6. 当得票足够后,等待一个周期,就可以成为出块人了。
08/15/2018 14:24 下午 posted in  EOS

区块链开发之确定性算法bip32,bip39,bip44

##引言

随着比特币区块链的发展,人们已经不满足于,只有一个账号的情况,有些人会有好几个账户,但是这就出现一个问题,我有几个账号,就要保存几个私钥,这就特别麻烦和不友好,所以,就出现了bip32确定性算法,该算法可以让你只有同一个种子,就可以生成无数个私钥和地址,这就大大方便了用户的使用。但是这个种子,也不较长,用户使用起来也比较繁琐,这就出现了bip39,它是使用助记词的方式,生成种子的,这样用户只需要记住,12个单词(3,6,9,12,15,18,21,24支持这些单词数,目前使用较广泛的是12和24),这就有大大提高了用户使用的便利性。又随着区块链发展,市面上出现了很多币种,之前的确定性算法只是针对比特币的,也就是说只支持一种币种,用户想用同一个种子,管理不同币种,这就促使了bip39协议的出现,它是基于bip32协议的,它给bip32的路径,赋予了不同的意义,很好的解决了多币种,多地址的问题。

协议官方链接

如果想详细的了解这些协议,请查看官方文档(解析的最清楚)
big32:
https://github.com/bitcoin/bips/blob/master/bip-0032.mediawiki

bip39:https://github.com/bitcoin/bips/blob/master/bip-0039.mediawiki

bip44:https://github.com/bitcoin/bips/blob/master/bip-0044.mediawiki

验证网站:https://iancoleman.io/bip39/

#BIP44简介

##路径级别

bip44给bip32定义了5各级别

m / purpose’ / coin_type’ / account’ / change / address_index

##m是固定的

Purpose也是固定的,值为44(或者 0x8000002C)

##Coin type

这个代表的是币种,0代表比特币,1代表比特币测试链,60代表以太坊

完整的币种列表地址:https://github.com/satoshilabs/slips/blob/master/slip-0044.md

##Account

代表这个币的账户索引,从0开始

##Change

常量0用于外部链,常量1用于内部链(也称为更改地址)。外部链用于在钱包外可见的地址(例如,用于接收付款)。内部链用于在钱包外部不可见的地址,用于返回交易变更。 (所以一般使用0)

##address_index

这就是地址索引,从0开始,代表生成第几个地址,并且官方建议,每个account下的address_index不要超过20

###示例

coin account change address path
Bitcoin first external first
Bitcoin first external second
Bitcoin first internal second
Bitcoin Testnet first external
Bitcoin Testnet second external
08/06/2018 17:45 下午 posted in  Ethereum

InHouse 下载

<?xml version="1.0" encoding="UTF-8"?>
    <!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
    <plist version="1.0">
    <dict>
        <key>items</key>
        <array>
            <dict>
                <key>assets</key>
                <array>
                    <dict>
                        <key>kind</key>
                        <string>software-package</string>
                        <key>url</key>
                        <string>https://www.xt.top/folder/xtoken-1.0.ipa</string>
                    </dict>
                    <dict>
                        <key>kind</key>
                        <string>full-size-image</string>
                        <key>needs-shine</key>
                        <true/>
                        <key>url</key>
                        <string>https://www.xt.top/folder/images/icon.57x57.png</string>
                    </dict>
                    <dict>
                        <key>kind</key>
                        <string>display-image</string>
                        <key>needs-shine</key>
                        <true/>
                        <key>url</key>
                        <string>https://www.xt.top/folder/images/icon.512x512.png</string>
                    </dict>
                </array>
                <key>metadata</key>
                <dict>
                    <key>bundle-identifier</key>
                    <string>com.biyuan.xtoken</string>
                    <key>bundle-version</key>
                    <string>1.0</string>
                    <key>kind</key>
                    <string>software</string>
                    <key>title</key>
                    <string>XToken</string>
                </dict>
            </dict>
        </array>
    </dict>
    </plist>

然后把 .ipa 和 .plist 文件都上传到支持HTTPS协议的服务器,在网页源码里写入:

<a href="itms-services://?action=download-manifest&url=https://s3-us-west-2.amazonaws.com/folder/appName-version.plist" id="text">Install the In-House App</a> 
07/25/2018 13:35 下午 posted in  杂七杂八

钱包测试账号信息:

##助记词:

live devote airport journey theme jealous syrup okay pyramid universe advice wall

##ETH钱包账户

###账户[0]:
地址[0]:0xC937F39FA384063E89752d61211186cB5D3912cC
私钥[0]:0x6de11341a4a3eee24980dcd907a2d9ea8702b43f7e5218c349db69748fb0ace4

###账户[1]:
第1个地址的私钥和keystore
地址[1]:0xd4357D40e9714e011a4e0D918b6E0B371Ad2f376
私钥[1]:1f5014b9c55b5f808f261d6cdd619e85a3fea70438111e3c6bdb4c7f7bc9ef36

keysotre[1]:

{"version":3,"id":"460c472e-7ad4-43d8-a6e5-716811ca9497","crypto":{"ciphertext":"a21f4f1cb8b4b38ac005e4a9f0f0fefdb1372c772f40930e9c009d3923ad55aa","cipherparams":{"iv":"7aef1d1cced46d7cc3131f63863d5203"},"kdf":"scrypt","kdfparams":{"r":6,"p":1,"n":4096,"dklen":32,"salt":"b4c8daf5e510dfd9de4e81aac1d381f4454a4da28f977b39dc218392c0ffe3d9"},"mac":"1c6a852298b4e4c17c40b1ebb5191ce08d498a42139f66714490683bb488edc7","cipher":"aes-128-ctr"},"address":"0xd4357d40e9714e011a4e0d918b6e0b371ad2f376"}

##BTC测试 m/44'/1'/0'/0
地址[0]:n44UNAeSMPLrZcr8W2QRn9jbZsW3NJ8YUj
私钥[1]:cUUGW6PXaV2RheeqmJdKvRN4PGCXBJwVmxmbdxKsUnsH33nAWCc9

地址[1]:mpgwNrt32sMtemgE6y4iH6UMk6tuAGQArn
私钥[1]:cRaf3jvs5rA9g84Pihb1dMaJaJqU4FZree7GYiW17zJ4vxN2vQtG

地址[101]:mqfWfmQJGDViWF4vENVkpMA2AbznHvtWNG
私钥[101]:cSxBRwJDENrqQK5c5oWzhvw3LpmsDs1cMr8B5EGwPwGjcBmpZdCt

##小虫测试ETH账号

1f5014b9c55b5f808f261d6cdd619e85a3fea70438111e3c6bdb4c7f7bc9ef36
07/25/2018 10:44 上午 posted in  Ethereum

时间的格式

##NSDateFormatter的作用

//NSString * -> NSDate *
- (nullable NSDate *)dateFromString:(NSString *)string;
//NSDate * -> NSString *
- (NSString *)stringFromDate:(NSDate *)date;

##常见的日期格式

http://www.cnblogs.com/mailingfeng/archive/2011/07/28/2120422.html

###NSString * -> NSDate *

"2016-10-03 14:01:00"

// 时间字符串
NSString *string = "2016-10-03 14:01:00";

// 日期格式化类
NSDateFormatter *fmt = [[NSDateFormatter alloc] init];
// 设置日期格式(为了转换成功)
fmt.dateFormat = @"yyyy-MM-dd HH:mm:ss";

// NSString * -> NSDate *
NSDate *date = [fmt dateFromString:string];

NSLog(@"%@", date);

10月-03号/2016年 09-10:05秒

// 时间字符串
NSString *string = @"10月-03号/2016年 09-10:05秒";

// 日期格式化类
NSDateFormatter *fmt = [[NSDateFormatter alloc] init];
fmt.dateFormat = @"MM月-dd号/yyyy年 HH-mm:ss秒";

NSLog(@"%@", [fmt dateFromString:string]);

Tue May 31 17:46:55 +0800 2011

// 时间字符串
NSString *string = @"Tue May 31 17:46:55 +0800 2011";

// 日期格式化类
NSDateFormatter *fmt = [[NSDateFormatter alloc] init];
fmt.dateFormat = @"EEE MMM dd HH:mm:ss Z yyyy";
// fmt.dateFormat = @"EEE MMM dd HH:mm:ss ZZZZ yyyy";
// 设置语言区域(因为这种时间是欧美常用时间)
fmt.locale = [[NSLocale alloc] initWithLocaleIdentifier:@"en_US"];

NSLog(@"%@", [fmt dateFromString:string]);

1745645645645

// 时间戳 : 从1970年1月1号 00:00:00开始走过的毫秒数

// 时间字符串 - 时间戳
NSString *string = @"1745645645645";
NSTimeInterval second = string.longLongValue / 1000.0;

// 时间戳 -> NSDate *
NSDate *date = [NSDate dateWithTimeIntervalSince1970:second];
NSLog(@"%@", date);

##NSCalendar的注意点

#define iOS(version) ([UIDevice currentDevice].systemVersion.doubleValue >= (version))

NSCalendar *calendar = nil;
if ([UIDevice currentDevice].systemVersion.doubleValue >= 8.0) {
    calendar = [NSCalendar calendarWithIdentifier:NSCalendarIdentifierGregorian];
} else {
    calendar = [NSCalendar currentCalendar];
}

NSCalendar *calendar = nil;
if ([NSCalendar respondsToSelector:@selector(calendarWithIdentifier:)]) {
    calendar = [NSCalendar calendarWithIdentifier:NSCalendarIdentifierGregorian];
} else {
    calendar = [NSCalendar currentCalendar];
}

NSDate * -> NSString *

NSDate *date = [NSDate date];

NSDateFormatter *fmt = [[NSDateFormatter alloc] init];
fmt.dateFormat = @"yyyy年MM月dd号 HH:mm:ss";

NSString *string = [fmt stringFromDate:date];

获得日期元素

NSString *string = @"2016-10-03 14:01:00";

NSString *month = [string substringWithRange:NSMakeRange(5, 2)];

NSLog(@"%@", month);
// 时间字符串
NSString *string = @"2016-10-03 14:01:00";

// 日期格式化类
NSDateFormatter *fmt = [[NSDateFormatter alloc] init];
// 设置日期格式(为了转换成功)
fmt.dateFormat = @"yyyy-MM-dd HH:mm:ss";

// NSString * -> NSDate *
NSDate *date = [fmt dateFromString:string];

// 利用NSCalendar处理日期
NSCalendar *calendar = [NSCalendar currentCalendar];
NSInteger month = [calendar component:NSCalendarUnitMonth fromDate:date];
NSInteger hour = [calendar component:NSCalendarUnitHour fromDate:date];
NSInteger minute = [calendar component:NSCalendarUnitMinute fromDate:date];

NSLog(@"%zd %zd %zd", month, hour, minute);
// 时间字符串
NSString *string = @"2016-10-03 14:01:00";

// 日期格式化类
NSDateFormatter *fmt = [[NSDateFormatter alloc] init];
// 设置日期格式(为了转换成功)
fmt.dateFormat = @"yyyy-MM-dd HH:mm:ss";

// NSString * -> NSDate *
NSDate *date = [fmt dateFromString:string];

// 利用NSCalendar处理日期
NSCalendar *calendar = [NSCalendar currentCalendar];

NSCalendarUnit unit = NSCalendarUnitYear | NSCalendarUnitMonth | NSCalendarUnitDay | NSCalendarUnitHour | NSCalendarUnitMinute | NSCalendarUnitSecond;
NSDateComponents *cmps = [calendar components:unit fromDate:date];

// NSLog(@"%zd %zd %zd", cmps.year, cmps.month, cmps.day);
NSLog(@"%@", cmps);
日期比较

// 时间字符串
NSString *createdAtString = @"2016-10-03 14:01:00";
NSDateFormatter *fmt = [[NSDateFormatter alloc] init];
fmt.dateFormat = @"yyyy-MM-dd HH:mm:ss";
NSDate *createdAtDate = [fmt dateFromString:createdAtString];

// 手机当前时间
NSDate *nowDate = [NSDate date];

/**
NSComparisonResult的取值
NSOrderedAscending = -1L, // 升序, 越往右边越大
NSOrderedSame, // 相等
NSOrderedDescending // 降序, 越往右边越小
*/
// 获得比较结果(谁大谁小)
NSComparisonResult result = [nowDate compare:createdAtDate];
if (result == NSOrderedAscending) { // 升序, 越往右边越大
NSLog(@"createdAtDate > nowDate");
} else if (result == NSOrderedDescending) { // 降序, 越往右边越小
NSLog(@"createdAtDate < nowDate");
} else {
NSLog(@"createdAtDate == nowDate");
}
// 时间字符串
NSString *createdAtString = @"2016-10-03 14:01:00";
NSDateFormatter *fmt = [[NSDateFormatter alloc] init];
fmt.dateFormat = @"yyyy-MM-dd HH:mm:ss";
NSDate *createdAtDate = [fmt dateFromString:createdAtString];

// 手机当前时间
// NSDate *nowDate = [NSDate date];

// 获得createdAtDate和nowDate的时间间隔(间隔多少秒)
// NSTimeInterval interval = [nowDate timeIntervalSinceDate:createdAtDate];
NSTimeInterval interval = [createdAtDate timeIntervalSinceNow];
NSLog(@"%f", interval);
NSDateFormatter *fmt = [[NSDateFormatter alloc] init];
fmt.dateFormat = @"yyyy-MM-dd HH:mm:ss";

// 时间字符串
NSString *createdAtString = @"2016-10-03 14:01:00";
NSDate *createdAtDate = [fmt dateFromString:createdAtString];

// 其他时间
NSString *otherString = @"2016-10-03 14:01:00";
NSDate *otherDate = [fmt dateFromString:otherString];

// 获得NSCalendar
NSCalendar *calendar = nil;
if ([NSCalendar respondsToSelector:@selector(calendarWithIdentifier:)]) {
calendar = [NSCalendar calendarWithIdentifier:NSCalendarIdentifierGregorian];
} else {
calendar = [NSCalendar currentCalendar];
}

// 获得日期之间的间隔
NSCalendarUnit unit = NSCalendarUnitYear | NSCalendarUnitMonth | NSCalendarUnitDay | NSCalendarUnitHour | NSCalendarUnitMinute | NSCalendarUnitSecond;
NSDateComponents *cmps = [calendar components:unit fromDate:createdAtDate toDate:otherDate options:0];

NSLog(@"%@", cmps);
条件判断的一些注意点

1.判断一个数组中是否有具体内容
1> 正确
if (array.count) {

}

2> 错误
if (array) {

}

2.判断一个字符串是否有具体内容
1> 正确
if (string.length) {

}

2> 错误
if (string) {

}

07/23/2018 20:38 下午 posted in  apple

钱包地址生成和扫描规则

##1.扫描类实例

iban:XE19RLALTH48S5S586M44YMOU1PBEVJ3BS4?amount=100&token=GNB
iban:XE19RLALTH48S5S586M44YMOU1PBEVJ3BS4?amount=0.001
iban:XE19RLALTH48S5S586M44YMOU1PBEVJ3BS4?amount=0.01&token=ETH

ethereum:0xeC34C8681972b22a79f641F42CFdb0e22BFFcd64?contractAddress=0x0d8775f648430679a709e98d2b0cb6250d2887ef&decimal=18&value=1000000000000000
07/19/2018 08:45 上午 posted in  Ethereum

Objective-C编码规范

原文链接: GitHub
原文作者: raywenderlich.com Team
译文出自: raywenderlich.com Objective-C编码规范

这篇编码风格指南概括了raywenderlich.com的编码规范,可能有些删减或修改。

介绍

我们制定Objective-C编码规范的原因是我们能够在我们的书,教程和初学者工具包的代码保持优雅和一致。即使我们有很多不同的作者来完成不同的书籍。

这里编码规范有可能与你看到的其他Objective-C编码规范不同,因为它主要是为了打印和web的易读性。

关于作者

这编码规范的创建是由很多来自raywenderlich.com团队成员在Nicholas Waynik的带领下共同完成的。团队成员有:Soheil Moayedi Azarpour, Ricardo Rendon Cepeda, Tony Dahbura, Colin Eberhardt, Matt Galloway, Greg Heo, Matthijs Hollemans, Christopher LaPollo, Saul Mora, Andy Pereira, Mic Pringle, Pietro Rea, Cesare Rocchi, Marin Todorov, Nicholas WaynikRay Wenderlich

我们也非常感谢New York TimesRobots & Pencils'Objective-C编码规范的作者。这两个编码规范为本指南的创建提供很好的起点。

背景

这里有些关于编码风格Apple官方文档,如果有些东西没有提及,可以在以下文档来查找更多细节:

目录

语言

应该使用US英语.

应该:

UIColor *myColor = [UIColor whiteColor];

不应该:

UIColor *myColour = [UIColor whiteColor];

代码组织

在函数分组和protocol/delegate实现中使用#pragma mark -来分类方法,要遵循以下一般结构:

#pragma mark - Lifecycle
- (instancetype)init {}
- (void)dealloc {}
- (void)viewDidLoad {}
- (void)viewWillAppear:(BOOL)animated {}
- (void)didReceiveMemoryWarning {}

#pragma mark - Custom Accessors
- (void)setCustomProperty:(id)value {}
- (id)customProperty {}

#pragma mark - IBActions/Event Response
- (IBAction)submitData:(id)sender {}
- (void)someButtonDidPressed:(UIButton*)button

#pragma mark - Protocol conformance
#pragma mark - UITextFieldDelegate
#pragma mark - UITableViewDataSource
#pragma mark - UITableViewDelegate

#pragma mark - Public
- (void)publicMethod {}

#pragma mark - Private
- (void)privateMethod {}

#pragma mark - NSCopying
- (id)copyWithZone:(NSZone *)zone {}

#pragma mark - NSObject
- (NSString *)description {}

空格

  • 缩进使用4个空格,确保在Xcode偏好设置来设置。(raywenderlich.com使用2个空格)
  • 方法大括号和其他大括号(if/else/switch/while 等.)总是在同一行语句打开但在新行中关闭。

应该:

if (user.isHappy) {
    //Do something
} else {
    //Do something else
}

不应该:

if (user.isHappy)
{
  //Do something
}
else {
  //Do something else
}
  • 在方法之间应该有且只有一行,这样有利于在视觉上更清晰和更易于组织。在方法内的空白应该分离功能,但通常都抽离出来成为一个新方法。
  • 优先使用auto-synthesis。但如果有必要,@synthesize@dynamic应该在实现中每个都声明新的一行。
  • 应该避免以冒号对齐的方式来调用方法。因为有时方法签名可能有3个以上的冒号和冒号对齐会使代码更加易读。请不要这样做,尽管冒号对齐的方法包含代码块,因为Xcode的对齐方式令它难以辨认。

应该:

// blocks are easily readable
[UIView animateWithDuration:1.0 animations:^{
  // something
} completion:^(BOOL finished) {
  // something
}];

不应该:

// colon-aligning makes the block indentation hard to read
[UIView animateWithDuration:1.0
                 animations:^{
                     // something
                 }
                 completion:^(BOOL finished) {
                     // something
                 }];

注释

当需要注释时,注释应该用来解释这段特殊代码为什么要这样做。任何被使用的注释都必须保持最新或被删除。

一般都避免使用块注释,因为代码尽可能做到自解释,只有当断断续续或几行代码时才需要注释。例外:这不应用在生成文档的注释

命名

Apple命名规则尽可能坚持,特别是与这些相关的memory management rules (NARC)。

长的,描述性的方法和变量命名是好的。

应该:

UIButton *settingsButton;

不应该:

UIButton *setBut;

三个字符前缀应该经常用在类和常量命名,但在Core Data的实体名中应被忽略。对于官方的raywenderlich.com书、初学者工具包或教程,前缀'RWT'应该被使用。

常量应该使用驼峰式命名规则,所有的单词首字母大写和加上与类名有关的前缀。

应该:

static NSTimeInterval const RWTTutorialViewControllerNavigationFadeAnimationDuration = 0.3;

不应该:

static NSTimeInterval const fadetime = 1.7;

属性也是使用驼峰式,但首单词的首字母小写。对属性使用auto-synthesis,而不是手动编写@ synthesize语句,除非你有一个好的理由。

应该:

@property (strong, nonatomic) NSString *descriptiveVariableName;

不应该:

id varnm;

下划线

当使用属性时,实例变量应该使用self.来访问和改变。这就意味着所有属性将会视觉效果不同,因为它们前面都有self.

但有一个特例:在初始化方法里,实例变量(例如,_variableName)应该直接被使用来避免getters/setters潜在的副作用。

局部变量不应该包含下划线。

方法

在方法签名中,应该在方法类型(-/+ 符号)之后有一个空格。在方法各个段之间应该也有一个空格(符合Apple的风格)。在参数之前应该包含一个具有描述性的关键字来描述参数。

"and"这个词的用法应该保留。它不应该用于多个参数来说明,就像initWithWidth:height以下这个例子:

应该:

- (void)setExampleText:(NSString *)text image:(UIImage *)image;
- (void)sendAction:(SEL)aSelector to:(id)anObject forAllCells:(BOOL)flag;
- (id)viewWithTag:(NSInteger)tag;
- (instancetype)initWithWidth:(CGFloat)width height:(CGFloat)height;

不应该:

-(void)setT:(NSString *)text i:(UIImage *)image;
- (void)sendAction:(SEL)aSelector :(id)anObject :(BOOL)flag;
- (id)taggedView:(NSInteger)tag;
- (instancetype)initWithWidth:(CGFloat)width andHeight:(CGFloat)height;
- (instancetype)initWith:(int)width and:(int)height;  // Never do this.

变量

变量尽量以描述性的方式来命名。单个字符的变量命名应该尽量避免,除了在for()循环。

星号表示变量是指针。例如, NSString *text 既不是 NSString* text 也不是 NSString * text,除了一些特殊情况下常量。

私有变量 应该尽可能代替实例变量的使用。尽管使用实例变量是一种有效的方式,但更偏向于使用属性来保持代码一致性。

通过使用'back'属性(_variable,变量名前面有下划线)直接访问实例变量应该尽量避免,除了在初始化方法(init, initWithCoder:, 等…),dealloc 方法和自定义的setters和getters。想了解关于如何在初始化方法和dealloc直接使用Accessor方法的更多信息,查看这里

应该:

@interface RWTTutorial : NSObject

@property (strong, nonatomic) NSString *tutorialName;

@end

不应该:

@interface RWTTutorial : NSObject {
  NSString *tutorialName;
}

属性特性

所有属性特性应该显式地列出来,有助于新手阅读代码。属性特性的顺序应该是storage、atomicity,与在Interface Builder连接UI元素时自动生成代码一致。

应该:

@property (weak, nonatomic) IBOutlet UIView *containerView;
@property (strong, nonatomic) NSString *tutorialName;

不应该:

@property (nonatomic, weak) IBOutlet UIView *containerView;
@property (nonatomic) NSString *tutorialName;

NSString应该使用copy 而不是 strong的属性特性。

为什么?即使你声明一个NSString的属性,有人可能传入一个NSMutableString的实例,然后在你没有注意的情况下修改它。

应该:

@property (copy, nonatomic) NSString *tutorialName;

不应该:

@property (strong, nonatomic) NSString *tutorialName;

点符号语法

点语法是一种很方便封装访问方法调用的方式。当你使用点语法时,通过使用getter或setter方法,属性仍然被访问或修改。想了解更多,阅读这里

点语法应该总是被用来访问和修改属性,因为它使代码更加简洁。[]符号更偏向于用在其他例子。

应该:

NSInteger arrayCount = [self.array count];
view.backgroundColor = [UIColor orangeColor];
[UIApplication sharedApplication].delegate;

不应该:

NSInteger arrayCount = self.array.count;
[view setBackgroundColor:[UIColor orangeColor]];
UIApplication.sharedApplication.delegate;

字面值

NSString, NSDictionary, NSArray, 和 NSNumber的字面值应该在创建这些类的不可变实例时被使用。请特别注意nil值不能传入NSArrayNSDictionary字面值,因为这样会导致crash。

应该:

NSArray *names = @[@"Brian", @"Matt", @"Chris", @"Alex", @"Steve", @"Paul"];
NSDictionary *productManagers = @{@"iPhone": @"Kate", @"iPad": @"Kamal", @"Mobile Web": @"Bill"};
NSNumber *shouldUseLiterals = @YES;
NSNumber *buildingStreetNumber = @10018;

不应该:

NSArray *names = [NSArray arrayWithObjects:@"Brian", @"Matt", @"Chris", @"Alex", @"Steve", @"Paul", nil];
NSDictionary *productManagers = [NSDictionary dictionaryWithObjectsAndKeys: @"Kate", @"iPhone", @"Kamal", @"iPad", @"Bill", @"Mobile Web", nil];
NSNumber *shouldUseLiterals = [NSNumber numberWithBool:YES];
NSNumber *buildingStreetNumber = [NSNumber numberWithInteger:10018];

常量

常量是容易重复被使用和无需通过查找和代替就能快速修改值。常量应该使用static来声明而不是使用#define,除非显式地使用宏。

应该:

static NSString * const RWTAboutViewControllerCompanyName = @"RayWenderlich.com";

static CGFloat const RWTImageThumbnailHeight = 50.0;

不应该:

#define CompanyName @"RayWenderlich.com"

#define thumbnailHeight 2

枚举类型

当使用enum时,推荐使用新的固定基本类型规格,因为它有更强的类型检查和代码补全。现在SDK有一个宏NS_ENUM()来帮助和鼓励你使用固定的基本类型。

例如:

typedef NS_ENUM(NSInteger, RWTLeftMenuTopItemType) {
  RWTLeftMenuTopItemMain,
  RWTLeftMenuTopItemShows,
  RWTLeftMenuTopItemSchedule
};

你也可以显式地赋值(展示旧的k-style常量定义):

typedef NS_ENUM(NSInteger, RWTGlobalConstants) {
  RWTPinSizeMin = 1,
  RWTPinSizeMax = 5,
  RWTPinCountMin = 100,
  RWTPinCountMax = 500,
};

旧的k-style常量定义应该避免除非编写Core Foundation C的代码。

不应该:

enum GlobalConstants {
  kMaxPinSize = 5,
  kMaxPinCount = 500,
};

Case语句

大括号在case语句中并不是必须的,除非编译器强制要求。当一个case语句包含多行代码时,大括号应该加上。

switch (condition) {
  case 1:
    // ...
    break;
  case 2: {
    // ...
    // Multi-line example using braces
    break;
  }
  case 3:
    // ...
    break;
  default: 
    // ...
    break;
}

有很多次,当相同代码被多个cases使用时,一个fall-through应该被使用。一个fall-through就是在case最后移除'break'语句,这样就能够允许执行流程跳转到下一个case值。为了代码更加清晰,一个fall-through需要注释一下。

switch (condition) {
  case 1:
    // ** fall-through! **
  case 2:
    // code executed for values 1 and 2
    break;
  default: 
    // ...
    break;
}

当在switch使用枚举类型时,'default'是不需要的。例如:

RWTLeftMenuTopItemType menuType = RWTLeftMenuTopItemMain;

switch (menuType) {
  case RWTLeftMenuTopItemMain:
    // ...
    break;
  case RWTLeftMenuTopItemShows:
    // ...
    break;
  case RWTLeftMenuTopItemSchedule:
    // ...
    break;
}

私有属性

私有属性应该在类的实现文件中的类扩展(匿名分类)中声明,命名分类(比如RWTPrivate private)应该从不使用除非是扩展其他类。匿名分类应该通过使用+Private.h文件的命名规则暴露给测试。

例如:

@interface RWTDetailViewController ()

@property (strong, nonatomic) GADBannerView *googleAdView;
@property (strong, nonatomic) ADBannerView *iAdView;
@property (strong, nonatomic) UIWebView *adXWebView;

@end

布尔值

Objective-C使用YESNO。因为truefalse应该只在CoreFoundation,C或C++代码使用。既然nil解析成NO,所以没有必要在条件语句比较。不要拿某样东西直接与YES比较,因为YES被定义为1和一个BOOL能被设置为8位。

这是为了在不同文件保持一致性和在视觉上更加简洁而考虑。

应该:

if (someObject) {}
if (![anotherObject boolValue]) {}

不应该:

if (someObject == nil) {}
if ([anotherObject boolValue] == NO) {}
if (isAwesome == YES) {} // Never do this.
if (isAwesome == true) {} // Never do this.

如果BOOL属性的名字是一个形容词,属性就能忽略"is"前缀,但要指定get访问器的惯用名称。例如:

@property (assign, getter=isEditable) BOOL editable;

文字和例子从这里引用Cocoa Naming Guidelines

条件语句

条件语句主体为了防止出错应该使用大括号包围,即使条件语句主体能够不用大括号编写(如,只用一行代码)。这些错误包括添加第二行代码和期望它成为if语句;还有,even more dangerous defect可能发生在if语句里面一行代码被注释了,然后下一行代码不知不觉地成为if语句的一部分。除此之外,这种风格与其他条件语句的风格保持一致,所以更加容易阅读。

应该:

if (!error) {
  return success;
}

不应该:

if (!error)
  return success;

if (!error) return success;

三元操作符

当需要提高代码的清晰性和简洁性时,三元操作符?:才会使用。单个条件求值常常需要它。多个条件求值时,如果使用if语句或重构成实例变量时,代码会更加易读。一般来说,最好使用三元操作符是在根据条件来赋值的情况下。

Non-boolean的变量与某东西比较,加上括号()会提高可读性。如果被比较的变量是boolean类型,那么就不需要括号。

应该:

NSInteger value = 5;
result = (value != 0) ? x : y;

BOOL isHorizontal = YES;
result = isHorizontal ? x : y;

不应该:

result = a > b ? x = c > d ? c : d : y;

Init方法

Init方法应该遵循Apple生成代码模板的命名规则。返回类型应该使用instancetype而不是id

- (instancetype)init {
  self = [super init];
  if (self) {
    // ...
  }
  return self;
}

查看关于instancetype的文章Class Constructor Methods

类构造方法

当类构造方法被使用时,它应该返回类型是instancetype 而不是id。这样确保编译器正确地推断结果类型。

@interface Airplane
+ (instancetype)airplaneWithType:(RWTAirplaneType)type;
@end

关于更多instancetype信息,请查看NSHipster.com

CGRect函数

当访问CGRect里的x, y, width, 或 height时,应该使用CGGeometry函数而不是直接通过结构体来访问。引用Apple的CGGeometry :

在这个参考文档中所有的函数,接受CGRect结构体作为输入,在计算它们结果时隐式地标准化这些rectangles。因此,你的应用程序应该避免直接访问和修改保存在CGRect数据结构中的数据。相反,使用这些函数来操纵rectangles和获取它们的特性。

应该:

CGRect frame = self.view.frame;

CGFloat x = CGRectGetMinX(frame);
CGFloat y = CGRectGetMinY(frame);
CGFloat width = CGRectGetWidth(frame);
CGFloat height = CGRectGetHeight(frame);
CGRect frame = CGRectMake(0.0, 0.0, width, height);

不应该:

CGRect frame = self.view.frame;

CGFloat x = frame.origin.x;
CGFloat y = frame.origin.y;
CGFloat width = frame.size.width;
CGFloat height = frame.size.height;
CGRect frame = (CGRect){ .origin = CGPointZero, .size = frame.size };

黄金路径

当使用条件语句编码时,左手边的代码应该是"golden" 或 "happy"路径。也就是不要嵌套if语句,多个返回语句也是OK。

应该:

- (void)someMethod {
  if (![someOther boolValue]) {
	return;
  }

  //Do something important
}

不应该:

- (void)someMethod {
  if ([someOther boolValue]) {
    //Do something important
  }
}

错误处理

当方法通过引用来返回一个错误参数,判断返回值而不是错误变量。

应该:

NSError *error;
if (![self trySomethingWithError:&error]) {
  // Handle Error
}

不应该:

NSError *error;
[self trySomethingWithError:&error];
if (error) {
  // Handle Error
}

在成功的情况下,有些Apple的APIs记录垃圾值(garbage values)到错误参数(如果non-NULL),那么判断错误值会导致false负值和crash。

单例模式

单例对象应该使用线程安全模式来创建共享实例。

+ (instancetype)sharedInstance {
  static id sharedInstance = nil;

  static dispatch_once_t onceToken;
  dispatch_once(&onceToken, ^{
    sharedInstance = [[self alloc] init];
  });

  return sharedInstance;
}

这会防止possible and sometimes prolific crashes.

换行符

换行符是一个很重要的主题,因为它的风格指南主要为了打印和网上的可读性。

例如:

self.productsRequest = [[SKProductsRequest alloc] initWithProductIdentifiers:productIdentifiers];

一行很长的代码应该分成两行代码,下一行用两个空格隔开。

self.productsRequest = [[SKProductsRequest alloc] 
  initWithProductIdentifiers:productIdentifiers];

Xcode工程

物理文件应该与Xcode工程文件保持同步来避免文件扩张。任何Xcode分组的创建应该在文件系统的文件体现。代码不仅是根据类型来分组,而且还可以根据功能来分组,这样代码更加清晰。

尽可能在target的Build Settings打开"Treat Warnings as Errors,和启用以下additional warnings。如果你需要忽略特殊的警告,使用 Clang's pragma feature

其他Objective-C编码规范

如果我们的编码规范不符合你的口味,可以查看其他的编码规范:

07/03/2018 07:33 上午 posted in  Program

使用Objective-C实现自定义的RunLoop

//
//  main.m
//  ZCRunLoop
//
//  Created by Zenny Chen on 2016/11/21.
//  Copyright © 2016年 GreenGames Studio. All rights reserved.
//

@import Foundation;

#include <sys/event.h>
#include <fcntl.h>
#include <signal.h>
#include <sys/time.h>
#include <unistd.h>
#include <semaphore.h>

#include <stdbool.h>
#include <stdint.h>
#include <stdatomic.h>
#include <stdalign.h>


#if defined(__i386__) || defined(__x86_64__)
#define CPU_PAUSE()     asm("pause")
#elif defined(__arm__) || defined(__arm64__)
#define CPU_PAUSE()     asm("yield")
#else
#define CPU_PAUSE()
#endif


#pragma mark - ZCEvent

/** 自己定制的事件类 */
@interface ZCEvent : NSObject

/** 事件响应时,将消息所发送给的目标 */
@property (nonatomic, retain) id target;

/** 事件响应时,对目标所发送的消息,这里使用NSValue其实是对SEL类型的封装 */
@property (nonatomic, retain) NSValue *message;

/** 将消息发送给目标时,随带的用户自定义参数 */
@property (nonatomic, retain) id parameter;

@end

@implementation ZCEvent

@synthesize target, message, parameter;

- (void)dealloc
{
    self.target = nil;
    self.message = nil;
    self.parameter = nil;
    
    [super dealloc];
}

@end


#pragma mark - ZCTimerEvent

/** 自己定制的定时器事件类 */
@interface ZCTimerEvent : ZCEvent
{
@public
    
    /** 当前定时器到期时间 */
    struct timeval expireDate;
}

@end

@implementation ZCTimerEvent

@end


#pragma mark - ZCRunLoop

/** 自己定制的消息循环类 */
@interface ZCRunLoop : NSObject

/** 获取ZCRunLoop单例实例对象 */
+ (instancetype)runLoop;

/**
 * 添加定时器事件
 * @param target 消息接收者
 * @param selector 消息签名
 * @param param 用户自定义参数
 * @param timeoutInterval 超时时间,单位为秒
 */
- (void)addTimerEvent:(id)target message:(SEL)selector userParam:(id)param timeout:(NSTimeInterval)timeoutInterval;

/**
 * 添加消息事件,用于在当前消息队列中处理
 * @param target 消息接收者
 * @param selector 消息签名
 * @param param 用户自定义参数
 */
- (void)addMessageEvent:(id)target message:(SEL)selector param:(id)param;

/** 运行消息循环 */
- (void)run;

@end

static ZCRunLoop *sMainRunLoop;

@implementation ZCRunLoop
{
@private
    
    /** 用于对时间事件队列操作的循环锁的原子标志 */
    atomic_bool alignas(64) mTimerEventFlag;
    
    /** 定时器事件队列 */
    NSMutableArray<ZCTimerEvent*> *mTimerEvents;
    
    /** 用于即将处理的定时器事件队列 */
    NSArray<ZCTimerEvent*> *mTimerEventsForProcessing;
    
    /** 消息事件队列 */
    NSMutableArray<ZCEvent*> *mMessageEvents;
    
    /** 信号量,当当前没有消息时,将当前线程阻塞 */
    sem_t *mSemaphore;
    
    /** 当前即将到期的事件索引 */
    int mMinimumIntervalTimeEventIndex;
    
    /** 用于标识当前消息循环是否即将退出 */
    volatile BOOL mWillBeTerminated;
    
    /** 用于对消息事件队列操作的循环锁的原子标志 */
    atomic_bool alignas(64) mMessageEventFlag;
}

+ (instancetype)runLoop
{
    return sMainRunLoop;
}

/** 定时器响应函数 */
static void alarm_wakeup(int i)
{
    [[ZCRunLoop runLoop] addCurrentTimerEventsToProcess];
}

- (instancetype)init
{
    self = [super init];
    
    atomic_init(&mTimerEventFlag, true);
    atomic_init(&mMessageEventFlag, true);
    
    mTimerEvents = [[NSMutableArray alloc] initWithCapacity:128];
    mMessageEvents = [[NSMutableArray alloc] initWithCapacity:128];
    mMinimumIntervalTimeEventIndex = -1;
    mSemaphore = sem_open("My semaphore", O_CREAT, S_IRUSR | S_IWUSR, 0);
    
    signal(SIGALRM, alarm_wakeup);
    
    return self;
}

- (void)dealloc
{
    [mTimerEvents removeAllObjects];
    [mTimerEvents release];
    
    [mMessageEvents removeAllObjects];
    [mMessageEvents release];
    
    if(mTimerEventsForProcessing != nil)
        [mTimerEventsForProcessing release];
    
    sem_close(mSemaphore);
    
    NSLog(@"ZCRunLoop deallocated!");
    
    [super dealloc];
}

- (void)addMessageEvent:(id)target message:(SEL)selector param:(id)param
{
    ZCEvent *event = [ZCEvent new];
    event.target = target;
    event.message = [NSValue valueWithPointer:selector];
    event.parameter = param;
    
    while(!atomic_exchange(&mMessageEventFlag, false))
        CPU_PAUSE();
    
    [mMessageEvents addObject:event];
    [event release];
    
    atomic_store(&mMessageEventFlag, true);
    
    sem_post(mSemaphore);
}

- (void)addTimerEvent:(id)target message:(SEL)selector userParam:(id)param timeout:(NSTimeInterval)timeoutInterval
{
    ZCTimerEvent *newEvent = [ZCTimerEvent new];
    newEvent.target = target;
    newEvent.message = [NSValue valueWithPointer:selector];
    newEvent.parameter = param;
    
    struct timeval specDate;
    
    typeof(specDate.tv_sec) secInterval = (typeof(specDate.tv_sec))timeoutInterval;
    typeof(specDate.tv_usec) usecInterval = (timeoutInterval - (double)secInterval) * 1000000.0;
    specDate.tv_sec = secInterval;
    specDate.tv_usec = usecInterval;
    
    // 每添加了一个新的事件,说明当前run-loop一直会处于运行状态
    mWillBeTerminated = NO;
    
    // 上旋锁
    while(!atomic_exchange(&mTimerEventFlag, false))
        CPU_PAUSE();
    
    struct timeval currTime;
    gettimeofday(&currTime, NULL);
    
    // 将specDate设置为到期日期,根据指定的超时时间
    timeradd(&specDate, &currTime, &specDate);
    
    newEvent->expireDate = specDate;
    
    [mTimerEvents addObject:newEvent];
    [newEvent release];
    
    struct itimerval tout_val;
    tout_val.it_interval.tv_sec = 0;
    tout_val.it_interval.tv_usec = 0;
    tout_val.it_value.tv_sec = 0;
    tout_val.it_value.tv_usec = 0;
    
    if(mMinimumIntervalTimeEventIndex == -1)
    {
        // 用于处理加入第一个事件的时候
        mMinimumIntervalTimeEventIndex = 0;
        tout_val.it_value.tv_sec = secInterval;
        tout_val.it_value.tv_usec = usecInterval;
    }
    else
    {
        ZCTimerEvent *minEvent = mTimerEvents[mMinimumIntervalTimeEventIndex];
        
        // 将当前离到期日期最近的日期与新添加的到期日期进行比较
        if(timercmp(&minEvent->expireDate, &specDate, >))
        {
            // 倘若当前离到期日期最近的日期比新添加的到期日期要大,
            // 那么将新添加的到期日期作为最小超时时间,并重新设定定时器的值
            mMinimumIntervalTimeEventIndex = (int)(mTimerEvents.count - 1);
            tout_val.it_value.tv_sec = secInterval;
            tout_val.it_value.tv_usec = usecInterval;
        }
    }
    
    if((tout_val.it_value.tv_sec > 0 || tout_val.it_value.tv_usec > 0))
        setitimer(ITIMER_REAL, &tout_val, NULL);
    
    // 解旋锁
    atomic_store(&mTimerEventFlag, true);
}

- (void)addCurrentTimerEventsToProcess
{
    // 上旋锁
    while(!atomic_exchange(&mTimerEventFlag, false))
        CPU_PAUSE();
    
    if(mTimerEventsForProcessing != nil)
        [mTimerEventsForProcessing release];
    
    mTimerEventsForProcessing = [[NSArray alloc] initWithArray:mTimerEvents];
    
    // 解旋锁
    atomic_store(&mTimerEventFlag, true);
}

/** 处理定时器事件 */
- (void)processTimerHandler
{
    struct timeval currTime;
    gettimeofday(&currTime, NULL);
    
    @autoreleasepool {
        
        NSMutableArray *clearArray = [NSMutableArray arrayWithCapacity:128];
        
        // 遍历每一个事件,看看当前是否即将或已经到期的事件,整个处理过程无需上锁
        for(ZCTimerEvent *event in mTimerEventsForProcessing)
        {
            struct timeval eventTime;
            timersub(&event->expireDate, &currTime, &eventTime);
            
            // 计算当前事件的到期日期离当前日期相差多少微秒
            __auto_type interval = eventTime.tv_sec * 1000000L + eventTime.tv_usec;
            
            // 这里设定小于10微秒的事件作为到期时间
            if(interval < 10)
            {
                // 执行相应的消息发送
                [event.target performSelector:(SEL)[event.message pointerValue] withObject:event.parameter];
                
                // 准备将当前事件移除
                [clearArray addObject:event];
            }
        }
        
        [mTimerEventsForProcessing release];
        mTimerEventsForProcessing = nil;
        
        // 上旋锁
        while(!atomic_exchange(&mTimerEventFlag, false))
            CPU_PAUSE();
        
        [mTimerEvents removeObjectsInArray:clearArray];
        
        const NSUInteger length = mTimerEvents.count;
        
        // 如果事件队列中还存有事件,那么挑选出最小的到期时间,并重新设置定时器
        if(length > 0)
        {
            mMinimumIntervalTimeEventIndex = 0;
            struct timeval minimumTime = mTimerEvents[0]->expireDate;
            
            for(int i = 1; i < length; i++)
            {
                if(timercmp(&minimumTime, &mTimerEvents[i]->expireDate, >))
                {
                    mMinimumIntervalTimeEventIndex = i;
                    minimumTime = mTimerEvents[i]->expireDate;
                }
            }
            
            struct itimerval tout_val;
            tout_val.it_interval.tv_sec = 0;
            tout_val.it_interval.tv_usec = 0;
            timersub(&minimumTime, &currTime, &tout_val.it_value);
            
            setitimer(ITIMER_REAL, &tout_val, NULL);
        }
        else    // 否则即将退出当前消息循环
            mWillBeTerminated = YES;
    }
    
    // 解旋锁
    atomic_store(&mTimerEventFlag, true);
}

- (void)processMessages
{
    while(!atomic_exchange(&mMessageEventFlag, false))
        CPU_PAUSE();
    
    // 处理当前每一个消息事件
    for(ZCEvent *event in mMessageEvents)
    {
        // 将消息事件放到定时器事件队列中处理,默认延迟100微秒
        [self addTimerEvent:event.target message:event.message.pointerValue userParam:event.parameter timeout:0.0001];
    }
    
    // 最后将所有消息事件全都移除
    [mMessageEvents removeAllObjects];
    
    atomic_store(&mMessageEventFlag, true);
}

- (void)run
{
    // 如果当前不退出消息循环,则挂起当前线程
    while(!mWillBeTerminated)
    {
        sem_wait(mSemaphore);
        
        // 处理当前的定时器消息
        [self processTimerHandler];
        
        // 唤醒一次之后处理所有消息事件
        [self processMessages];
    }
}

@end


#pragma mark - ZCObject

@interface ZCObject : NSObject

@end

@implementation ZCObject

- (void)performSelector:(SEL)aSelector withObject:(id)anArgument afterDelay:(NSTimeInterval)delay
{
    [[ZCRunLoop runLoop] addTimerEvent:self message:aSelector userParam:anArgument timeout:delay];
}

- (void)performSelectorOnMainThread:(SEL)aSelector withObject:(id)arg
{
    // 延迟100微秒在主线程的run-loop中发送消息
    [[ZCRunLoop runLoop] addMessageEvent:self message:aSelector param:arg];
}

@end

#pragma mark - test

@interface MyObject : ZCObject

- (void)hello:(NSNumber*)delaySeconds;
- (void)hey:(NSNumber*)delaySeconds;

@end


@implementation MyObject

- (void)hello:(NSNumber*)delaySeconds
{
    NSLog(@"Hello, world! delayed: %@ seconds", delaySeconds);
}

- (void)hey:(NSNumber*)delaySeconds
{
    dispatch_async(dispatch_get_global_queue(QOS_CLASS_USER_INTERACTIVE, 0), ^void(void) {
        [self performSelectorOnMainThread:@selector(hello:) withObject:delaySeconds];
    });
}

- (void)hi:(NSNumber*)delaySeconds
{
    NSLog(@"Hi, The following operation will be delayed for %@ seconds", delaySeconds);
    
    [self performSelector:@selector(hello:) withObject:delaySeconds afterDelay:delaySeconds.doubleValue];
}

- (void)dealloc
{
    NSLog(@"MyObject deallocated!");
    
    [super dealloc];
}

@end


int main(int argc, const char * argv[])
{
    sMainRunLoop = [ZCRunLoop new];
    
    MyObject *obj = [MyObject new];
    [obj performSelector:@selector(hello:) withObject:@2.0 afterDelay:2.0];
    
    [obj performSelector:@selector(hello:) withObject:@1.5 afterDelay:1.5];
    
    [obj performSelector:@selector(hello:) withObject:@5.0 afterDelay:5.0];
    
    [obj performSelector:@selector(hello:) withObject:@4.0 afterDelay:4.0];
    
    [obj performSelector:@selector(hello:) withObject:@3.0 afterDelay:3.0];
    
    [obj performSelector:@selector(hi:) withObject:@8.5 afterDelay:8.5];
    
    [obj performSelector:@selector(hey:) withObject:@7.75 afterDelay:7.75];
    
    [obj performSelector:@selector(hello:) withObject:@22.0 afterDelay:22.0];
    
    [obj performSelectorOnMainThread:@selector(hey:) withObject:@0.1];
    
    dispatch_async(dispatch_get_global_queue(QOS_CLASS_USER_INTERACTIVE, 0), ^void(void) {
        [obj performSelectorOnMainThread:@selector(hello:) withObject:@6.5];
        [obj performSelectorOnMainThread:@selector(hello:) withObject:@6.7];
    });
    
    [obj release];
    
    NSLog(@"Running...");
    
    [[ZCRunLoop runLoop] run];
    
    [sMainRunLoop release];
    
    return 0;
}

06/19/2018 06:52 上午 posted in  RunLoop