本文假设读者已经基本了解boost线程库的使用方法。

boost是个开源工程,线程这一块也在不断完善之中,到现在这个阶段,boost::thread仅仅实现了一个完美的技术框架,但是读者在实际使用中会发现一些新的技术问题:
1.boost::thread::join开启一个线程以后,怎样主动结束子线程?
2.boost线程之间怎样实现消息传递?
作者在这里描述怎样一步步扩展这些功能。
 
一. Janitor 异常安全处理
  本文的janitor.hpp是针对异常安全处理的封装类,在后面的扩展类里面有使用到,异常安全的目的是为了保证程序的一段事务的完整性,关于异常安全不是本文的重点,感兴趣的话可以参见http://dev.csdn.net/article/6/6883.shtm
  1. // janitor.hpp : 安全执行类库
  2. //
  3. #pragma once
  4. #include <list>
  5. template <class T>
  6. class RefHolder
  7. {
  8. T& ref_;
  9. public:
  10. RefHolder(T& ref) : ref_(ref) {}
  11. operator T& () const
  12. {
  13. return ref_;
  14. }
  15. private:
  16. // Disable assignment - not implemented
  17. RefHolder& operator=(const RefHolder&);
  18. };
  19. template <class T>
  20. inline RefHolder<T> ByRef(T& t)
  21. {
  22. return RefHolder<T>(t);
  23. }
  24. class ScopeGuardImplBase
  25. {
  26. ScopeGuardImplBase& operator =(const ScopeGuardImplBase&);
  27. protected:
  28. ~ScopeGuardImplBase()
  29. {
  30. }
  31. ScopeGuardImplBase(const ScopeGuardImplBase& other) throw()
  32. : dismissed_(other.dismissed_)
  33. {
  34. other.Dismiss();
  35. }
  36. template <typename J>
  37. static void SafeExecute(J& j) throw()
  38. {
  39. if (!j.dismissed_)
  40. try
  41. {
  42. j.Execute();
  43. }
  44. catch(...)
  45. {
  46. }
  47. }
  48. mutable bool dismissed_;
  49. public:
  50. ScopeGuardImplBase() throw() : dismissed_(false)
  51. {
  52. }
  53. void Dismiss() const throw()
  54. {
  55. dismissed_ = true;
  56. }
  57. };
  58. typedef const ScopeGuardImplBase& ScopeGuard;
  59. template <typename F>
  60. class ScopeGuardImpl0 : public ScopeGuardImplBase
  61. {
  62. public:
  63. static ScopeGuardImpl0<F> MakeGuard(F fun)
  64. {
  65. return ScopeGuardImpl0<F>(fun);
  66. }
  67. ~ScopeGuardImpl0() throw()
  68. {
  69. SafeExecute(*this);
  70. }
  71. void Execute()
  72. {
  73. fun_();
  74. }
  75. protected:
  76. ScopeGuardImpl0(F fun) : fun_(fun)
  77. {
  78. }
  79. F fun_;
  80. };
  81. template <typename F>
  82. inline ScopeGuardImpl0<F> MakeGuard(F fun)
  83. {
  84. return ScopeGuardImpl0<F>::MakeGuard(fun);
  85. }
  86. template <typename F, typename P1>
  87. class ScopeGuardImpl1 : public ScopeGuardImplBase
  88. {
  89. public:
  90. static ScopeGuardImpl1<F, P1> MakeGuard(F fun, P1 p1)
  91. {
  92. return ScopeGuardImpl1<F, P1>(fun, p1);
  93. }
  94. ~ScopeGuardImpl1() throw()
  95. {
  96. SafeExecute(*this);
  97. }
  98. void Execute()
  99. {
  100. fun_(p1_);
  101. }
  102. protected:
  103. ScopeGuardImpl1(F fun, P1 p1) : fun_(fun), p1_(p1)
  104. {
  105. }
  106. F fun_;
  107. const P1 p1_;
  108. };
  109. template <typename F, typename P1>
  110. inline ScopeGuardImpl1<F, P1> MakeGuard(F fun, P1 p1)
  111. {
  112. return ScopeGuardImpl1<F, P1>::MakeGuard(fun, p1);
  113. }
  114. template <typename F, typename P1, typename P2>
  115. class ScopeGuardImpl2: public ScopeGuardImplBase
  116. {
  117. public:
  118. static ScopeGuardImpl2<F, P1, P2> MakeGuard(F fun, P1 p1, P2 p2)
  119. {
  120. return ScopeGuardImpl2<F, P1, P2>(fun, p1, p2);
  121. }
  122. ~ScopeGuardImpl2() throw()
  123. {
  124. SafeExecute(*this);
  125. }
  126. void Execute()
  127. {
  128. fun_(p1_, p2_);
  129. }
  130. protected:
  131. ScopeGuardImpl2(F fun, P1 p1, P2 p2) : fun_(fun), p1_(p1), p2_(p2)
  132. {
  133. }
  134. F fun_;
  135. const P1 p1_;
  136. const P2 p2_;
  137. };
  138. template <typename F, typename P1, typename P2>
  139. inline ScopeGuardImpl2<F, P1, P2> MakeGuard(F fun, P1 p1, P2 p2)
  140. {
  141. return ScopeGuardImpl2<F, P1, P2>::MakeGuard(fun, p1, p2);
  142. }
  143. template <typename F, typename P1, typename P2, typename P3>
  144. class ScopeGuardImpl3 : public ScopeGuardImplBase
  145. {
  146. public:
  147. static ScopeGuardImpl3<F, P1, P2, P3> MakeGuard(F fun, P1 p1, P2 p2, P3 p3)
  148. {
  149. return ScopeGuardImpl3<F, P1, P2, P3>(fun, p1, p2, p3);
  150. }
  151. ~ScopeGuardImpl3() throw()
  152. {
  153. SafeExecute(*this);
  154. }
  155. void Execute()
  156. {
  157. fun_(p1_, p2_, p3_);
  158. }
  159. protected:
  160. ScopeGuardImpl3(F fun, P1 p1, P2 p2, P3 p3) : fun_(fun), p1_(p1), p2_(p2), p3_(p3)
  161. {
  162. }
  163. F fun_;
  164. const P1 p1_;
  165. const P2 p2_;
  166. const P3 p3_;
  167. };
  168. template <typename F, typename P1, typename P2, typename P3>
  169. inline ScopeGuardImpl3<F, P1, P2, P3> MakeGuard(F fun, P1 p1, P2 p2, P3 p3)
  170. {
  171. return ScopeGuardImpl3<F, P1, P2, P3>::MakeGuard(fun, p1, p2, p3);
  172. }
  173. //************************************************************
  174. template <class Obj, typename MemFun>
  175. class ObjScopeGuardImpl0 : public ScopeGuardImplBase
  176. {
  177. public:
  178. static ObjScopeGuardImpl0<Obj, MemFun> MakeObjGuard(Obj& obj, MemFun memFun)
  179. {
  180. return ObjScopeGuardImpl0<Obj, MemFun>(obj, memFun);
  181. }
  182. ~ObjScopeGuardImpl0() throw()
  183. {
  184. SafeExecute(*this);
  185. }
  186. void Execute()
  187. {
  188. (obj_.*memFun_)();
  189. }
  190. protected:
  191. ObjScopeGuardImpl0(Obj& obj, MemFun memFun)
  192. : obj_(obj), memFun_(memFun) {}
  193. Obj& obj_;
  194. MemFun memFun_;
  195. };
  196. template <class Obj, typename MemFun>
  197. inline ObjScopeGuardImpl0<Obj, MemFun> MakeObjGuard(Obj& obj, MemFun memFun)
  198. {
  199. return ObjScopeGuardImpl0<Obj, MemFun>::MakeObjGuard(obj, memFun);
  200. }
  201. template <class Obj, typename MemFun, typename P1>
  202. class ObjScopeGuardImpl1 : public ScopeGuardImplBase
  203. {
  204. public:
  205. static ObjScopeGuardImpl1<Obj, MemFun, P1> MakeObjGuard(Obj& obj, MemFun memFun, P1 p1)
  206. {
  207. return ObjScopeGuardImpl1<Obj, MemFun, P1>(obj, memFun, p1);
  208. }
  209. ~ObjScopeGuardImpl1() throw()
  210. {
  211. SafeExecute(*this);
  212. }
  213. void Execute()
  214. {
  215. (obj_.*memFun_)(p1_);
  216. }
  217. protected:
  218. ObjScopeGuardImpl1(Obj& obj, MemFun memFun, P1 p1)
  219. : obj_(obj), memFun_(memFun), p1_(p1) {}
  220. Obj& obj_;
  221. MemFun memFun_;
  222. const P1 p1_;
  223. };
  224. template <class Obj, typename MemFun, typename P1>
  225. inline ObjScopeGuardImpl1<Obj, MemFun, P1> MakeObjGuard(Obj& obj, MemFun memFun, P1 p1)
  226. {
  227. return ObjScopeGuardImpl1<Obj, MemFun, P1>::MakeObjGuard(obj, memFun, p1);
  228. }
  229. template <class Obj, typename MemFun, typename P1, typename P2>
  230. class ObjScopeGuardImpl2 : public ScopeGuardImplBase
  231. {
  232. public:
  233. static ObjScopeGuardImpl2<Obj, MemFun, P1, P2> MakeObjGuard(Obj& obj, MemFun memFun, P1 p1, P2 p2)
  234. {
  235. return ObjScopeGuardImpl2<Obj, MemFun, P1, P2>(obj, memFun, p1, p2);
  236. }
  237. ~ObjScopeGuardImpl2() throw()
  238. {
  239. SafeExecute(*this);
  240. }
  241. void Execute()
  242. {
  243. (obj_.*memFun_)(p1_, p2_);
  244. }
  245. protected:
  246. ObjScopeGuardImpl2(Obj& obj, MemFun memFun, P1 p1, P2 p2)
  247. : obj_(obj), memFun_(memFun), p1_(p1), p2_(p2) {}
  248. Obj& obj_;
  249. MemFun memFun_;
  250. const P1 p1_;
  251. const P2 p2_;
  252. };
  253. template <class Obj, typename MemFun, typename P1, typename P2>
  254. inline ObjScopeGuardImpl2<Obj, MemFun, P1, P2> MakeObjGuard(Obj& obj, MemFun memFun, P1 p1, P2 p2)
  255. {
  256. return ObjScopeGuardImpl2<Obj, MemFun, P1, P2>::MakeObjGuard(obj, memFun, p1, p2);
  257. }
  258. #define CONCATENATE_DIRECT(s1, s2) s1##s2
  259. #define CONCATENATE(s1, s2) CONCATENATE_DIRECT(s1, s2)
  260. #define ANONYMOUS_VARIABLE(str) CONCATENATE(str, __LINE__)
  261. #define ON_BLOCK_EXIT ScopeGuard ANONYMOUS_VARIABLE(scopeGuard) = MakeGuard
  262. #define ON_BLOCK_EXIT_OBJ ScopeGuard ANONYMOUS_VARIABLE(scopeGuard) = MakeObjGuard
  263. //////////////////////////////////////////////////////////////////////////////////////////
  264. //      janitor
  265. struct ICmd_
  266. {
  267. virtual void Dismiss() const throw() = 0;
  268. virtual ~ICmd_() throw() {}
  269. };
  270. template<typename T>
  271. class CmdAdaptor : public ICmd_, protected T
  272. {
  273. public:
  274. template<typename Fun>
  275. CmdAdaptor(Fun fun) : T(fun) {}
  276. template<typename Fun, typename P1>
  277. CmdAdaptor(Fun fun, P1 p1) : T(fun, p1) {}
  278. template<typename Fun, typename P1, typename P2>
  279. CmdAdaptor(Fun fun, P1 p1, P2 p2) : T(fun, p1, p2) {}
  280. template<typename Fun, typename P1, typename P2, typename P3>
  281. CmdAdaptor(Fun fun, P1 p1, P2 p2, P3 p3) : T(fun, p1, p2, p3) {}
  282. void Dismiss() const throw()
  283. {
  284. T::Dismiss();
  285. }
  286. };
  287. class Janitor
  288. {
  289. public:
  290. Janitor() throw() {}
  291. template <typename F>
  292. Janitor(F pFun) : spCmd_(
  293. new CmdAdaptor<ScopeGuardImpl0<F> >(pFun)) {}
  294. template <typename F, typename P1>
  295. Janitor(F pFun, P1 p1) : spCmd_(
  296. new CmdAdaptor<ScopeGuardImpl1<F, P1> >(pFun, p1)) {}
  297. template <typename F, typename P1, typename P2>
  298. Janitor(F pFun, P1 p1, P2 p2) : spCmd_(
  299. new CmdAdaptor<ScopeGuardImpl2<F, P1, P2> >(pFun, p1, p2)) {}
  300. template <typename F, typename P1, typename P2, typename P3>
  301. Janitor(F pFun, P1 p1, P2 p2, P3 p3) : spCmd_(
  302. new CmdAdaptor<ScopeGuardImpl3<F, P1, P2, P3> >(pFun, p1, p2, p3)) {}
  303. Janitor(const Janitor& other) throw() : spCmd_(other.spCmd_) {} //VC++, Comeau need it!
  304. Janitor& operator =(const Janitor& other) throw()
  305. {
  306. if (spCmd_.get())
  307. spCmd_->Dismiss();
  308. spCmd_ = other.spCmd_;
  309. return *this;
  310. }
  311. void Dismiss() const throw()
  312. {
  313. spCmd_->Dismiss();
  314. }
  315. protected:
  316. mutable std::auto_ptr<ICmd_> spCmd_;
  317. };
  318. template<typename T>
  319. class ObjCmdAdaptor : public ICmd_, protected T
  320. {
  321. public:
  322. template<typename Obj, typename MemFun>
  323. ObjCmdAdaptor(Obj& obj, MemFun memFun) : T(obj, memFun) {}
  324. template<typename Obj, typename MemFun, typename P1>
  325. ObjCmdAdaptor(Obj& obj, MemFun memFun, P1 p1) : T(obj, memFun, p1) {}
  326. template<typename Obj, typename MemFun, typename P1, typename P2>
  327. ObjCmdAdaptor(Obj& obj, MemFun memFun, P1 p1, P2 p2) : T(obj, memFun, p1, p2) {}
  328. void Dismiss() const throw()
  329. {
  330. T::Dismiss();
  331. }
  332. };
  333. class ObjJanitor : protected Janitor
  334. {
  335. public:
  336. using Janitor::Dismiss;
  337. ObjJanitor() throw() {}
  338. template <typename Obj, typename MemFun>
  339. ObjJanitor(Obj& obj, MemFun memFun)
  340. {
  341. std::auto_ptr<ICmd_> spTmp(
  342. new ObjCmdAdaptor<ObjScopeGuardImpl0<Obj, MemFun> >(obj, memFun));
  343. spCmd_ = spTmp;
  344. }
  345. template <typename Obj, typename MemFun, typename P1>
  346. ObjJanitor(Obj& obj, MemFun memFun, P1 p1)
  347. {
  348. std::auto_ptr<ICmd_> spTmp(
  349. new ObjCmdAdaptor<ObjScopeGuardImpl1<Obj, MemFun, P1> >(obj, memFun, p1));
  350. spCmd_ = spTmp;
  351. }
  352. template <typename Obj, typename MemFun, typename P1, typename P2>
  353. ObjJanitor(Obj& obj, MemFun memFun, P1 p1, P2 p2)
  354. {
  355. std::auto_ptr<ICmd_> spTmp(
  356. new ObjCmdAdaptor<ObjScopeGuardImpl2<Obj, MemFun, P1, P2> >(obj, memFun, p1, p2));
  357. spCmd_ = spTmp;
  358. }
  359. };

使用范例

  1. #include "stdafx.h"
  2. #include <iostream>
  3. #include <string>
  4. using namespace std;
  5. #include "janitor.hpp"
  6. class class1
  7. {
  8. public:
  9. class1()
  10. {
  11. }
  12. ~class1()
  13. {
  14. }
  15. public:
  16. void test()
  17. {
  18. ObjJanitor ja(*this,&class1::testJanitor);
  19. }
  20. void testJanitor()
  21. {
  22. cout << "hello world" << endl;
  23. }
  24. };
  25. int _tmain(int argc, _TCHAR* argv[])
  26. {
  27. class1 c;
  28. c.test();
  29. return 0;
  30. }

二.controlled_module 可被关闭的线程类

 该类原型来自于一个叫“阿修罗”的原始类
  1. // controlled_module.hpp : 可主动关闭的boost线程类
  2. //
  3. #pragma once
  4. #include   <boost/utility.hpp>
  5. #include   <boost/thread/condition.hpp>
  6. #include   <boost/thread/mutex.hpp>
  7. #include <boost/thread/thread.hpp>
  8. #include <boost/bind.hpp>
  9. #include "janitor.hpp"
  10. class   controlled_module_implement   :   boost::noncopyable   {
  11. public:
  12. controlled_module_implement()   :active_(false),command_exit_(false){}
  13. boost::condition   module_is_exit;
  14. boost::mutex   monitor;
  15. void   active(bool   ac)
  16. {boost::mutex::scoped_lock   lk(monitor);   if(!(active_=ac))module_is_exit.notify_all();else   command_exit_=false;}
  17. bool   command_exit(){boost::mutex::scoped_lock   lk(monitor);   return   command_exit_;}
  18. bool   active_,command_exit_;
  19. };
  20. class   controlled_module   :   boost::noncopyable   {
  21. public:
  22. virtual   void   run()
  23. {
  24. ObjJanitor   janitor(*impl_,&controlled_module_implement::active,false);
  25. impl_->active(true);
  26. {
  27. ObjJanitor   janitor(*this,&controlled_module::release);
  28. if(this->initialize())
  29. {
  30. m_live = true;
  31. SetEvent(m_event_init);
  32. while(!impl_->command_exit()   &&  this->islive() &&  this->work())
  33. {
  34. }
  35. }
  36. else
  37. {
  38. m_live = false;
  39. SetEvent(m_event_init);
  40. }
  41. }
  42. }
  43. bool   exit(unsigned   long   sec=0)
  44. {
  45. boost::mutex::scoped_lock   lk(impl_->monitor);
  46. impl_->command_exit_   =   true;
  47. while(impl_->active_)
  48. {
  49. if(sec)
  50. {
  51. boost::xtime   xt;
  52. boost::xtime_get(&xt,   boost::TIME_UTC);
  53. xt.sec   +=   sec;
  54. if(!impl_->module_is_exit.timed_wait(lk,xt))
  55. return   false;
  56. }
  57. else
  58. impl_->module_is_exit.wait(lk);
  59. }
  60. return   true;
  61. }
  62. protected:
  63. controlled_module()
  64. :impl_(new   controlled_module_implement)
  65. ,m_live(false)
  66. ,m_event_init(0)
  67. ,m_sleeptime(10)
  68. {
  69. }
  70. virtual   ~controlled_module()
  71. {
  72. if(m_live)
  73. stop();
  74. delete   impl_;
  75. }
  76. private:
  77. virtual   bool   initialize(){return true;}
  78. virtual   void   release(){}
  79. protected:
  80. virtual   bool   work()
  81. {
  82. Sleep(this->m_sleeptime);
  83. return true;
  84. }
  85. int m_sleeptime;
  86. private:
  87. bool m_live;
  88. void * m_event_init;
  89. controlled_module_implement*   impl_;
  90. public:
  91. bool start()
  92. {
  93. m_event_init = CreateEvent(NULL,FALSE,FALSE,"");
  94. boost::thread thd(boost::bind(&controlled_module::run,this));
  95. ::WaitForSingleObject(m_event_init,INFINITE);
  96. CloseHandle(m_event_init);
  97. m_event_init = 0;
  98. return m_live;
  99. }
  100. void stop()
  101. {
  102. m_live = false;
  103. exit(1);
  104. }
  105. bool islive(){return m_live;}
  106. void die()
  107. {
  108. m_live = false;
  109. SetEvent(m_event_init);
  110. }
  111. void setsleeptime(int n)
  112. {
  113. m_sleeptime = n;
  114. }
  115. };
  virtual   bool   initialize();   初始化
  virtual   void   release();   释放
  virtual   bool   work();   工作函数
  如果我们要创建一个可被关闭的线程,可以如下步骤:
  1)创建一个controlled_module 继承类
  2)实现3个虚拟函数
  3)用start(),stop()启动和停止线程
范例:
 
  1. //controlled_module demo
  2. #include "controlled_module.hpp"
  3. class thd: public controlled_module
  4. {
  5. public:
  6. virtual bool initialize()
  7. {
  8. cout << "thd init" << endl;
  9. return true;
  10. }
  11. virtual void release()
  12. {
  13. cout << "thd release" << endl;
  14. }
  15. virtual bool work()
  16. {
  17. //your work........
  18. return controlled_module::work();
  19. }
  20. };
  21. int _tmain(int argc, _TCHAR* argv[])
  22. {
  23. thd t;
  24. t.start();
  25. char buf[10];
  26. gets_s(buf,sizeof buf);
  27. t.stop();
  28. return 0;
  29. }

thd线程在启动以后,将循环执行work()函数,直到线程退出

BOOST 线程完全攻略 - 扩展 - 可被关闭的线程类的更多相关文章

  1. BOOST 线程完全攻略 - 扩展 - 线程消息通讯

      // controlled_module_ex.hpp : controlled_module类的扩展 // 增强线程之间消息通讯 // 增加线程安全启动和安全关闭功能 // 增加定时器功能 #p ...

  2. BOOST 线程完全攻略 - 扩展 - 事务线程

    扩展threadtimermoduleexceptionsocket 什么叫事务线程 举个例子: 我们写一个IM客户端的登录子线程,则该子线程会有这么几个事务要处理 No.1 TCP Socket物理 ...

  3. BOOST 线程完全攻略 - 基础篇

    http://blog.csdn.net/iamnieo/article/details/2908621 2008-09-10 12:48 9202人阅读 评论(3) 收藏 举报 thread多线程l ...

  4. BOOST 线程完全攻略

    1 创建线程 首先看看boost::thread的构造函数吧,boost::thread有两个构造函数: (1)thread():构造一个表示当前执行线程的线程对象: (2)explicit thre ...

  5. 【C/C++】BOOST 线程完全攻略 - 基础篇

    C++多线程开发是一个复杂的事情,mfc下提供了CWinThread类,和AfxBeginThread等等函数,但是在使用中会遇到很多麻烦事情,例如线程之间参数传递的问题,我们一般都是把参数new一个 ...

  6. BOOST 线程完全攻略 - 结束语

    modulethread扩展多线程破解通讯 全文介绍了3个boost::thread的扩展类,希望能给大家书写多线程代码带来便捷. thread -> controlled_module_ex ...

  7. Chrome插件(扩展)开发全攻略

    [干货]Chrome插件(扩展)开发全攻略:https://www.cnblogs.com/liuxianan/p/chrome-plugin-develop.html

  8. 零基础攻略!如何使用kubectl和HPA扩展Kubernetes应用程序

    现如今,Kubernetes已经完全改变了软件开发方式.Kubernetes作为一个管理容器化工作负载及服务的开源平台,其拥有可移植.可扩展的特性,并促进了声明式配置和自动化,同时它还证明了自己是管理 ...

  9. Linux shell 脚本攻略之根据扩展名切分文件名

    摘自:<Linux shell 脚本攻略>Page61-62  

随机推荐

  1. How to center anything with css

    1. 绝对居中定位技术 我经常用margin:0 auto来实现水平居中,而一直认为margin:auto不能实现垂直居中……实际上,实现垂直居中仅需要声明元素高度和下面的CSS 优点:  缺点: 1 ...

  2. 在WIN7系统下用Quartus ii 11.1 NIOS II 11.1 有时候会出现Nios II 的Run as hardware 中报错:Downloading ELF Process failed

    nios工程在编译通过后RUN的过程中出现Error Running Nios II Project: ‘Downloading ELF Process failed’问题原因: 1.nios2 cp ...

  3. js_day13

  4. iCIBA简单案例

    效果图: 代码: <!DOCTYPE html><html> <head> <meta charset="utf-8" /> < ...

  5. utf-8的mysql表笔记

    链接数据库指定编码集jdbc:mysql://192.168.2.33:3306/mybase?useUnicode=true&characterEncoding=UTF-8 mysql默认链 ...

  6. Bootstrap定制(二)less基础语法

    前几日花了一天去看less,这几日在捣鼓其他东西,项目也在有序的进行中,今天花点时间整理下less的基础语法,也分享实际中的一些经验,与众人共享. 本篇笔者以less的基础语法着手,并配合bootst ...

  7. Python学习(一) Python安装配置

    我本身是Java程序猿,听说Python很强大,所以准备学习一下Python,虽说语言都是相同的,但java跟python肯定还是有区别的.希望在此记录一下自己的学习过程. 目前,Python分2.X ...

  8. 了不起的分支和循环02 - 零基础入门学习Python008

    了不起的分支和循环02 让编程改变世界 Change the world by program 上节课,小甲鱼教大家如何正确的打飞机,其要点就是:判断和循环,判断就是该是不该做某事,循环就是持续做某事 ...

  9. 解决位于底端Edittext 输入时被软盘遮盖

    遇到这种情况我们首先到网上搜一圈,大概情况是需要设置<activity/> android:windowSoftInputMode="adjustResize" ,按照 ...

  10. CoreData学习-最好的一片文章

    CoreData学习-最好的一片文章 分类: IOS重新上路2014-05-25 18:00 1937人阅读 评论(0) 收藏 举报   目录(?)[+]   写的很好的一篇教程,我什么时候能写出这么 ...