仓酷云

 找回密码
 立即注册
搜索
热搜: 活动 交友 discuz
查看: 1773|回复: 19
打印 上一主题 下一主题

[学习教程] IOS设计Android操纵体系的内存接纳机制仓酷云

[复制链接]
不帅 该用户已被删除
跳转到指定楼层
楼主
发表于 2015-1-18 11:43:09 | 只看该作者 回帖奖励 |倒序浏览 |阅读模式

马上注册,结交更多好友,享用更多功能,让你轻松玩转社区。

您需要 登录 才可以下载或查看,没有帐号?立即注册

x
CoreAnimation---制作动画很强大很喜欢的框架可以用少量的代码写出漂亮的动画CQuartz2D---强大的2D绘图库COpenGL---不用介绍了超级强大的3D库CCoreImage---简介:Android是一款基于Linux内核,面向挪动终真个操纵体系。为顺应其作为挪动平台操纵体系的特别必要,谷歌对其做了出格的计划与优化,使使用程序封闭但不加入,并由操纵体系举行历程的接纳办理。本文在ApplicationFramework与Linux内核两个条理上,以历程为粒度,对Android操纵体系的历程资本接纳机制举行了分析。读者能够从本文取得对Android使用程序的保存周期的进一步了解,从而加倍公道、高效地构建使用程序。
AndroidAPP的运转情况
Android是一款基于Linux内核,面向挪动终真个操纵体系。为顺应其作为挪动平台操纵体系的特别必要,谷歌对其做了出格的计划与优化,使得其历程调剂与资本办理与其他平台的Linux有分明的区分。次要包括上面几个条理:
1.ApplicationFramework
ApplicationFramework将全部操纵体系分开成两个部分。对使用开辟者而言,一切APP都是运转在ApplicationFramework之上,而其实不必要体贴体系底层的情形。ApplicationFramework层为使用开辟者供应了丰厚的使用编程接口,如ActivityManager,ContentProvider,NotificationManager,和各类窗口Widget资本等。在ApplicationFramework层,Activity是一个APP最基础的构成部分。一样平常每一个Activity对应于屏幕上的一个视图(大概说一屏),一个APP能够有一个大概多个Activity。使用程序被打包成.apk格局的文件,由DalvikVM注释实行。
2.DalvikVM
Dalvik假造机接纳存放器架构,而不是JVM的栈布局。Java程序编译后的.class文件其实不能在Dalvik中注释实行。因而Google供应了一个dx工具,用于将.class文件转换成Dalivk可以辨认的.dex格局。详细DalvikVM的细节不是本文重点,以下不再会商。
3.Linuxkernel
由上所述,一切的APP都是由Java代码编写并在DalvikVM中失掉注释实行。在Android操纵体系中,每一个DalvikVM的每一个Instance都对应于Linux内核中的一个历程。可使用adbshell工具检察体系中确当行进程。以下图所示,Android2.3.3启动后内核中的历程列表
.Android2.3中的历程列表(部分)



<br>

Android内存接纳准绳
上面将从ApplicationFramework和Linuxkernel两个条理剖析Android操纵体系的资本办理机制。
Android之以是接纳特别的资本办理机制,缘故原由在于其计划之初就是面向挪动终端,一切可用的内存仅限于体系RAM,必需针对这类限定计划响应的优化计划。当Android使用程序加入时,其实不清算其所占用的内存,Linux内核历程也响应的持续存在,所谓“加入但不封闭”。从而使得用户挪用程序时可以在第一工夫失掉呼应。当体系内存不敷时,体系将激活内存接纳历程。为了不因内存接纳影响用户体验(如杀逝世以后的举动历程),Android基于历程中运转的组件及其形态划定了默许的五个接纳优先级:
IMPORTANCE_FOREGROUND:
IMPORTANCE_VISIBLE:
IMPORTANCE_SERVICE:
IMPORTANCE_BACKGROUND:
IMPORTANCE_EMPTY:
这几种优先级的接纳按次是Emptyprocess、Backgroundprocess、Serviceprocess、Visibleprocess、Foregroundprocess。关于分别准绳拜见http://developer.android.com/guide/topics/fundamentals/processes-and-threads.html文件中。
ActivityManagerService会合办理一切历程的内存资本分派。一切历程必要请求或开释内存之前必需挪用ActivityManagerService对象,取得其“允许”以后才干举行下一步操纵,大概ActivityManagerService将间接“代庖”。类ActivityManagerService中触及到内存接纳的几个主要的成员办法以下:trimApplications(),updateOomAdjLocked(),activityIdleInternal()。这几个成员办法次要卖力Android默许的内存接纳机制,若Linux内核中的内存接纳机制没有被禁用,则跳过默许接纳。
默许接纳历程
Android操纵体系中的内存接纳可分为两个条理,即默许内存接纳与内核级内存接纳,本章重点对默许内存接纳机制举行研讨,Linux内核条理的内存接纳机制将鄙人一张先容。本章一切代码可拜见ActivityManagerService.java。
接纳举措出口:activityIdleInternal()
Android体系中内存接纳的触发点大抵可分为三种情形。第一,用户程序挪用StartActivity(),使以后举动的Activity被掩盖;第二,用户按back键,加入以后使用程序;第三,启动一个新的使用程序。这些可以触发内存接纳的事务终极挪用的函数接口就是activityIdleInternal()。当ActivityManagerService吸收到异步动静IDLE_TIMEOUT_MSG大概IDLE_NOW_MSG时,activityIdleInternal()将会被挪用。代码以下:
清单1.IDLE_NOW_MSG的处置体例
12345caseIDLE_NOW_MSG:{IBindertoken=(Ibinder)msg.obj;activityIdle(token,null);}break;清单2.IDLE_TIMEOUT_MSG的处置体例
12345678910111213caseIDLE_TIMEOUT_MSG:{if(mDidDexOpt){mDidDexOpt=false;Messagenmsg=mHandler.obtainMessage(IDLE_TIMEOUT_MSG);nmsg.obj=msg.obj;mHandler.sendMessageDelayed(nmsg,IDLE_TIMEOUT);return;}IBindertoken=(IBinder)msg.obj;Slog.w(TAG,"Activityidletimeoutfor"+token);activityIdleInternal(token,true,null);}break;IDLE_NOW_MSG由Activity的切换和Activiy核心的改动等事务激发,IDLE_TIMEOUT_MSG在Activity启动超时的情形下激发,一样平常这个超不时间设为10s,假如10s以内一个Activity仍然没有乐成启动,那末将发送异步动静IDLE_TIMEOUT_MSG举行资本接纳。activityIdleInternal()的次要义务是改动体系中Activity的形态信息,并将其增加到分歧形态列表中。其次要事情以下:
起首,挪用scheduleAppGcsLocked()办法关照一切举行中的义务举行渣滓接纳。scheduleAppGcsLocked()将举行调剂JVM的garbagecollect,接纳一部份内存空间,这里仅仅是关照每一个历程自行历程渣滓反省并调剂接纳工夫,而非同步接纳。然后,掏出mStoppingActivities和mFinishigActivities列表中的一切内容,暂存在一时变量中。这两个列表分离存储了以后形态为stop和finishi的activity对象。关于stop列表,假如个中的activity的finish形态为true,判别是否是要当即中断,假如要当即中断则挪用destroyActivityLocked()关照方针历程挪用onDestroy()办法,不然,先挪用resumeTopActivity()运转下一个Activity。假如finish形态为false,则挪用stopActivityLocked()关照客户历程中断该Activity,这类情形一样平常产生在挪用startActivity()后。关于finish列表,间接挪用destroyActivityLocked()关照客户历程烧毁方针Activity。
这里的destroyActivityLocked等函数并没有真正意义上改动内存的利用,只是将其形态改动为“同意接纳”,真实的接纳鄙人面行将挪用的trimApplications()函数中。
接纳历程函数trimApplications()
trimApplications()函数的布局以下:
清单3.trimApplications函数
123456789101112131415privatefinalvoidtrimApplications(){synchronized(this){//Firstremoveanyunusedapplicationprocesseswhosepackage//hasbeenremoved.for(i=mRemovedProcesses.size()-1;i>=0;i--){(1)//killprocess;}if(!updateOomAdjLocked()){(2)//dosomethingdefault}//Finally,iftherearetoomanyactivitiesnowrunning,tryto//finishasmanyaswecantogetbackdowntothelimit.(3)dosomething}}清单3中的三个标序号的地位分离卖力以下事情:
(1)当程序实行到trimApplications()以后,起首反省mRemovedProcesses列表中的历程。mRemovedProcesses列表中次要包括了crash的历程、5秒内没有呼应并被用户选在强迫封闭的历程、和使用开辟这挪用killBackgroundProcess想要杀逝世的历程。挪用Process.killProcess将一切此类历程全体杀逝世。
(2)挪用updateOomAdjLocked()函数,若乐成前往,申明Linux内核撑持setOomAdj()接口,updateOomAdjLocked将修正adj的值并关照linux内核,内核依据adj值和内存利用情形静态办理历程资本(lowmemorykiller和oom_killer)。若updateOomAdjLocked()前往为假,则暗示以后体系不撑持setOomAdj()接口,将在当地举行默许的资本接纳。
(3)最初,假如以后仍然运转了过量的Activity,对过剩的Activity举行接纳。trimApplications()的年夜多半的代码都在处置Oom_killer不存在情形下的默许资本接纳,上面对其默许接纳历程(即代码清单中标志(2)的地位)举行进一步剖析。其接纳历程可大抵形貌以下。
步骤一,猎取以后一切运转的历程mLruProcesses,mLruProcesses中的排序划定规矩是按比来利用工夫。对mLruProcesses中不克不及被封闭的历程举行计数,这些不克不及被封闭的历程包含运转service的历程,运转broadcastreceiver的历程等,见以下代码。
清单4.计数不克不及被封闭的历程
1234567if(app.persistent||app.services.size()!=0||app.curReceiver!=null||app.persistentActivities>0){//Dontcountprocessesholdingservicesagainstour//maximumprocesscount.numServiceProcs++;}步骤二,设以后最年夜运转历程数curMaxProcs=curMaxProcs+numServiceProcs(即默许最猛进程数与运转Service的历程数之和),假如以后历程的数目mRemovedProcesses.size()年夜于这个值,则遍历一切以后运转的历程,杀逝世切合前提的那些历程并开释内存。清算历程见清单5(部分代码省略)。从清单5的代码中能够看出,历程被杀逝世的前提是:
●必需长短persistent历程,即非体系历程;
●必需是空历程,即历程中没有任何activity存在。假如杀逝世存在Activity的历程,有大概封闭用户正在利用的程序,大概使使用程序恢复的时延变年夜,从而影响用户体验;
●必需无broadcastreceiver。运转broadcastreceiver一样平常都在守候一个事务的产生,用户其实不但愿此类程序被体系强迫封闭;
●历程中service的数目必需为0。存在service的历程很有大概在为一个大概多个程序供应某种服务,如GPS定位服务。杀逝世此类历程将使其他历程没法一般服务。
以上前提缺一不成。
清单5.清算历程
123456789101112131415161718if(!app.persistent&&app.activities.size()==0&&app.curReceiver==null&&app.services.size()==0){if(app.pid>0&&app.pid!=MY_PID){Process.killProcess(app.pid);}else{try{app.thread.scheduleExit();}catch(Exceptione){//Ignoreexceptions.}}//todo:Fornowweassumetheapplicationisnotbuggy//orevil,andwillquitasaresultofourrequest.//Eventuallyweneedtodrivethisoffofthedeath//notification,andkilltheprocessifittakestoolong.cleanUpApplicationRecordLocked(app,false,i);i--;}步骤三,再次反省以后运转的历程,假如mRemovedProcesses.size()仍旧年夜于curMaxProcs,则放宽前提再次举行接纳。判别前提见代码清单6(部分代码省略)。上面代码中,布尔变量canQuit的值为真时,那末这个历程能够被接纳。canQuit的取值分两个步骤,起首是依据历程的属性赋值。1.必需长短persistent历程,即非体系历程;2.必需无broadcastreceiver;3.历程中service的数目必需为0;4.persistent范例的activity数目为0。与步骤二独一的分歧在第4条,这里不请求历程是空历程,只需历程中没有persistent范例的Activity就能够(Activity是不是是persistent范例在开辟阶段指定)。这些前提都满意时,再反省历程中每一个Activity的属性,当该历程中一切的Activity都还必需满意三个前提:Activity的形态已保留,以后处在不成见形态而且Activity已Stop。这时候杀失落历程只会下降下次挪用程序时的加载速率,下次启动时将恢复到封闭之前的形态,其实不会在用户体验上形成致命的影响,以是,canQuit置位为真。这类情形与步骤二的接纳体例也有所分歧,因为历程中Activity的数目不是0,下一步必要对每一个activity实行destroyActivityLocked()烧毁,最初才杀逝世历程。
清单6.实行destroyActivityLocked()烧毁
12345678910111213141516171819202122232425booleancanQuit=!app.persistent&&app.curReceiver==null&&app.services.size()==0&&app.persistentActivities==0;intNUMA=app.activities.size();for(j=0;j<NUMA&&canQuit;j++){HistoryRecordr=(HistoryRecord)app.activities.get(j);canQuit=(r.haveState||!r.stateNotNeeded)&&!r.visible&&r.stopped;}if(canQuit){//Finishalloftheactivities,andthentheappitself.for(j=0;j<NUMA;j++){HistoryRecordr=(HistoryRecord)app.activities.get(j);if(!r.finishing){destroyActivityLocked(r,false);}r.resultTo=null;}if(app.pid>0&&app.pid!=MY_PID){Process.killProcess(app.pid);}cleanUpApplicationRecordLocked(app,false,i);i--;//dump();}步骤四,下面3个历程都是针对全部process举行的资本接纳。在以上历程实行终了以后,将在更小的粒度上对Activity的资本举行接纳。与下面所述相似,列表mLRUActivities存储了以后一切运转中的Activity,排序划定规矩一样为起码会见准绳。mLRUActivities.size()前往体系中运转的Activity的数目,当其年夜于MAX_ACTIVITIES(MAX_ACTIVITIES是一个常量,一样平常值为20,代表体系中最年夜同意同时存在的Activity)时。将接纳部分满意前提的Activity以削减内存的利用。接纳前提代码清单7所示:
清单7.接纳前提代码
1234567891011121314151617181920//Finally,iftherearetoomanyactivitiesnowrunning,tryto//finishasmanyaswecantogetbackdowntothelimit.for(i=0;i<mLRUActivities.size()&&mLRUActivities.size()>curMaxActivities;i++){finalHistoryRecordr=(HistoryRecord)mLRUActivities.get(i);//Wecanfinishthisoneifwehaveitsiciclesavedand//itisnotpersistent.if((r.haveState||!r.stateNotNeeded)&&!r.visible&&r.stopped&&!r.persistent&&!r.finishing){finalintorigSize=mLRUActivities.size();destroyActivityLocked(r,true);if(origSize>mLRUActivities.size()){i--;}}}这里接纳的只是Activity的内存资本,其实不会杀逝世历程,也不会影响历程的运转。当历程必要挪用被杀失落的Activity时,能够从保留的形态中复兴,固然大概必要绝对长一点的时延。
Linux内核中的内存接纳
lowmemorykiller
下面提到,trimApplications()函数中会实行一个叫做updateOomAdjLocked()的函数,假如前往false,则实行默许接纳,若前往true则不实行默许内存接纳。updateOomAdjLocked将针对每个历程更新一个名为adj的变量,并将其告诉Linux内核,内核保护一个包括adj的数据布局(即历程表),并经由过程lowmemorykiller反省体系内存的利用情形,在内存不敷的情形下杀逝世一些历程并开释内存。上面将对这类AndroidFramework与Linux内核相共同的内存接纳机制举行研讨。
因为Android操纵体系中的一切使用程序都运转在自力的Dalvik假造机情况中,Linux内核没法获知每一个历程的运转形态,也就没法为每一个历程保护一个符合的adj值,因而,AndroidApplicationFramework中必需供应一套机制以静态的更新每一个历程的adj。这就是updateOomAdjLocked()。
updateOomAdjLocked()位于ActivityManagerService中,其次要感化是为历程选择一个符合的adj值,并关照Linux内核更新这个值。updateOomAdjLocked起首挪用computeOomAdjLocked()开端盘算adj的值,然后回到updateOomAdjLocked()对其值举行进一步修改。预算流程可拜见代码。
关于adj,其界说在task_struct->signal_struct->adj,文件/kernel/include/linux/sched.h中。本色为历程数据布局中的一个变量,用来暗示产生OutofMemory时杀逝世历程的优先级按次。lowmemorykiller使用这个变量对历程的主要水平举行判别,并在内存不敷时开释部分空间,实在如今文件/kernel/drivers/staging/android/lowmemorykiller.c中。lowmemorykiller界说了两个数组:lowmem_adj和lowmem_minfree。个中lowmen_adj界说了一系列adj键值,而lowmem_minfree的每一个元素代表一个内存阈值。以下代码中四个阈值分离是6MB,8MB,16MB和64MB,分离代表当内存小于64MB时,adj年夜于或即是12的那些历程将被杀逝世并接纳,内存小于16MB时,adj年夜于即是6的那些历程将被杀逝世并接纳,内存小于8MB时,adj年夜于即是1的那些历程将被杀逝世并接纳,内存小于6MB时,adj年夜于即是0的一切历程将被杀逝世并接纳。内核中的每一个历程都持有一个adj,取值局限-17到15,值越小代表历程的主要性越高,接纳优先级越低,个中-17代表禁用主动接纳。Android体系中,只要0-15被利用。
清单8.每一个历程都持有一个adj
1234567891011121314staticintlowmem_adj[6]={0,1,6,12,};staticintlowmem_adj_size=4;staticsize_tlowmem_minfree[6]={3*512,/*6MB*/2*1024,/*8MB*/4*1024,/*16MB*/16*1024,/*64MB*/};staticintlowmem_minfree_size=4;lowmemorykiller注册一个lowmem_shrinker,lowmem_shrinker使用了尺度Linux内核中的CacheShrinker来完成,当余暇内存页面不敷时,内核线程kswapd将用已注册的lowmem_shrinker往返收内存页面。
清单9.用已注册的lowmem_shrinker往返收内存页面
1234567891011staticstructshrinkerlowmem_shrinker={.shrink=lowmem_shrink,.seeks=DEFAULT_SEEKS*16};staticint__initlowmem_init(void){task_free_register(&task_nb);register_shrinker(&lowmem_shrinker);return0;}lowmem_shrink的代码在函数lowmem_shrink中,上面给出该函数的次要布局。lowmem_shrink依据上述划定规矩遍历一切历程,选出必要停止的历程,经由过程发送一个没法疏忽的旌旗灯号SIGKILL强迫停止这些历程
清单10.强迫停止历程
123456789101112staticintlowmem_shrink(structshrinker*s,intnr_to_scan,gfp_tgfp_mask){for_each_process(p){//Selectprocessestobeforced}if(selected){force_sig(SIGKILL,selected);rem-=selected_tasksize;}elserem=-1;returnrem;}Oom_killer.
假如上述各类办法都没法开释出充足的内存空间,那末当为新的历程分派使用程序时将产生OutofMemory非常,OOM_killer将尽最初的勉力杀失落一些历程来开释空间。Android中的OOM_killer承继自尺度Linux2.6内核,用于分派内存时OutofMemory的处置。Android并没有对实在现体例举行修正。其地位在linux/mm/oom_kill.c。oom_killer遍历历程,并盘算一切历程的badness值,选择badness最年夜的谁人历程将其杀失落。函数badness的声明以下:
unsignedlongbadness(structtask_struct*p,unsignedlonguptime)函数select_bad_process前往将要杀失落的谁人历程。
清单11.前往将要杀失落的历程
123456789101112staticstructtask_struct*select_bad_process(unsignedlong*ppoints,structmem_cgroup*mem){for_each_process(p){points=badness(p,uptime.tv_sec);if(points>*ppoints||!chosen){chosen=p;*ppoints=points;}}returnchosen;}最初,和lowmemorykiller一样,经由过程发送SIGKILL停止选中的历程。因为oom_killer与尺度Linux内核并没有分歧,这里不再具体研讨。
总结
本文研讨了Android操纵体系上的内存接纳机制。次要包含ApplicationFramework层的默许接纳和Linux内核中的lowmemorykiller、OOM_killer。一样平常来讲使用开辟者其实不必要把持大概修正体系的内存办理和接纳,可是深切了解这些体系级的办理机制仍是需要的,特别有助于加倍公道地计划使用程序,使使用程序的历程在其性命周期内高效地运转。而体系级开辟者假如想要对内存办理机制举行优化,对原无机制的了解则是必不成少的主要条件。

管理所有设备发生的事件比如屏幕旋转屏幕关闭或者一些其他的程序的控制逻辑也应该写在这里他的初始化函数是-(id)initWithNibName:(NSString*)nibNamebundle:(NSBundle*)nibBundle后面那个NibName是InterfaceBuilder里设计的界面现在IB已经集成到XCode里了
小妖女 该用户已被删除
沙发
发表于 2015-1-21 13:07:19 | 只看该作者
首先是基础,在汉昌的课程非常全面。从object—c到最后的毕业项目,基本上方方面面都涉及到了,我是一名非计算机专业的学生,起初学习还有点吃力,因为基础知识薄弱。经常像听天书
爱飞 该用户已被删除
板凳
发表于 2015-1-30 18:54:31 | 只看该作者
边吃零食边看Stanford的视频教程
变相怪杰 该用户已被删除
地板
发表于 2015-1-30 18:54:31 | 只看该作者
同很多iOS开发者一样,我也是通过培训进入到iOS开发这个行业,开始没有打算培训,只准备自己学习一些计算机编程相关的知识,毕业时找一份编程相关工作(本人是信息与计算科学这个专业,是数学系)。
若相依 该用户已被删除
5#
发表于 2015-1-30 23:02:49 | 只看该作者
开始的时候甚至想放弃,不过想想自己的未来,只能咬牙坚持,课下就不停的缠着老师。放学就补基础,这些基础的东西没有速成的,只有刻苦努力。我是后来发现的,转变自己的心态,不要读书看资料当成一种痛苦
乐观 该用户已被删除
6#
发表于 2015-2-1 10:11:05 | 只看该作者
重要的是,放眼全球也的确找不到第二个如苹果iOS平台这样健壮、完整、先进而且为开发者带来真实收益的开发平台来。
再见西城 该用户已被删除
7#
发表于 2015-2-7 02:59:45 | 只看该作者
近期由于IOS7的发布,所以应用的适配潮可谓是都搞的锣鼓喧天,甚是热闹,因此呢,因适配IOS7而产生的问题也是铺天盖地的卷来,
愤怒的大鸟 该用户已被删除
8#
发表于 2015-2-12 09:27:39 | 只看该作者
以上可以同时进行,学习过程中尽量不要纠结细节和底层,要知道ios是封闭的、OC是高级语言,我们不可能过多地去了解它的原理,至少在新手阶段没有必要。用迭代的方式更新你的知识,而不是死抠一个知识点。
不帅 该用户已被删除
9#
 楼主| 发表于 2015-2-16 21:26:09 | 只看该作者
才在自己的Windows电脑上安装配置成功了一个完美的Mac OS X Lion(10.7.4)系统,而且下载了Xcode4.5的最新版本。虽然不能实机调试,但是作为iOS开发学习已经非常完美了。
若天明 该用户已被删除
10#
发表于 2015-2-19 13:49:22 | 只看该作者
以上可以同时进行,学习过程中尽量不要纠结细节和底层,要知道ios是封闭的、OC是高级语言,我们不可能过多地去了解它的原理,至少在新手阶段没有必要。用迭代的方式更新你的知识,而不是死抠一个知识点。
海妖 该用户已被删除
11#
发表于 2015-2-27 15:13:35 | 只看该作者
重要的是,放眼全球也的确找不到第二个如苹果iOS平台这样健壮、完整、先进而且为开发者带来真实收益的开发平台来。
金色的骷髅 该用户已被删除
12#
发表于 2015-3-9 07:56:52 | 只看该作者
在百度搜索你想要了解的类名(苹果的cocoa和cocoatouch框架的类名很有特点很容易搜到,前缀都是NS or UI),看别人写的博客详解
小女巫 该用户已被删除
13#
发表于 2015-3-12 11:27:03 | 只看该作者
down下code4app网站的每个分类的代码挨着看
冷月葬花魂 该用户已被删除
14#
发表于 2015-3-14 14:02:20 | 只看该作者
在百度搜索你想要了解的类名(苹果的cocoa和cocoatouch框架的类名很有特点很容易搜到,前缀都是NS or UI),看别人写的博客详解
admin 该用户已被删除
15#
发表于 2015-3-21 09:04:14 | 只看该作者
同很多iOS开发者一样,我也是通过培训进入到iOS开发这个行业,开始没有打算培训,只准备自己学习一些计算机编程相关的知识,毕业时找一份编程相关工作(本人是信息与计算科学这个专业,是数学系)。
16#
发表于 2015-3-26 14:53:53 | 只看该作者
以上可以同时进行,学习过程中尽量不要纠结细节和底层,要知道ios是封闭的、OC是高级语言,我们不可能过多地去了解它的原理,至少在新手阶段没有必要。用迭代的方式更新你的知识,而不是死抠一个知识点。
兰色精灵 该用户已被删除
17#
发表于 2015-4-3 21:41:44 | 只看该作者
因为我们老师也是自学的,给我们讲课说的最多的就是百度,谷歌,查文档。
再现理想 该用户已被删除
18#
发表于 2015-4-6 04:08:28 | 只看该作者
重要的是,放眼全球也的确找不到第二个如苹果iOS平台这样健壮、完整、先进而且为开发者带来真实收益的开发平台来。
灵魂腐蚀 该用户已被删除
19#
发表于 2015-4-17 13:31:58 | 只看该作者
才在自己的Windows电脑上安装配置成功了一个完美的Mac OS X Lion(10.7.4)系统,而且下载了Xcode4.5的最新版本。虽然不能实机调试,但是作为iOS开发学习已经非常完美了。
分手快乐 该用户已被删除
20#
发表于 2015-5-8 09:40:38 | 只看该作者
然而,在vmware软件环境下,安装Mac OS X操作系统也是一件非常复杂的事情,而且还有可能花费了大量时间,最后却跑不起来。笔者也是经过了大量的实践,
您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

QQ|Archiver|手机版|仓酷云 鄂ICP备14007578号-2

GMT+8, 2024-12-23 11:21

Powered by Discuz! X3.2

© 2001-2013 Comsenz Inc.

快速回复 返回顶部 返回列表