猴子原创,欢迎转载。转载请注明: 转载自Cocos2D开发网--Cocos2Dev.com,谢谢!
原文地址: http://www.cocos2dev.com/?p=285
今天去CGDC听了刘钢先生的Unity在移动端的优化处理,记录刘钢先生的优化建议。
一、关于性能优化的几大误区
误区1:性能优化只是程序员的责任,与美术和策划无关。
-技术美术和关卡设计师对于游戏性能承担着非常重要的责任
-程序员往往无法补救由于滥用美术资源而造成的性能问题
误区2:在制定了严格的美术规范之后,美术师就应该对一切美术问题负责,程序员不再与此有关
-程序员应该为美术师实现完整的美术资源合法性检查工具
-Tools speak louder than rules!
误区3:对于程序而言,性能优化应该从GPU/Shader的执行效率入手
-针对CPU端/游戏逻辑的性能优化往往能够取得更大的作用
-GPU/Shader的性能优化应该放在最后进行
未完.......待续.....
由于项目的需要,要给用户显示一列表单项,让用户选择,之前使用LinerLayout加Button实现,费时费力,而且表现的内容都已经是写好的内容,对于内容可变的情况下,毫无招架之力。因此在新的项目当中使用了向ListView加入Spinner的方式来表现表单。开发过程中遇到些问题。
一、 遇到的问题:1、对于Spinner状态的保持。
2、对Spinner做了相应事件之后,对于ListView的OnItemClick事件的相应的处理。
二、 问题的解决:第一个问题在于,ListView侧重于对数据的表现,而对数据的交互方面是比较差的,因为随着ListView的上下滚动,其会对内部的数据进行重绘操作,当数据划出屏幕之后将数据移除,同时将划入的数据进行重绘。还有一个问题就是当你点击Spinner的时候,ListView并不知道你点击了哪个Spinner。以上两个原因就造成了,你点击的Spinner所选择的项目在屏幕滑来滑去的时候会改变本身的值,同时你无法获取所选的Spinner的值。
对于这个问题只要加一个Map将Spinner的状态保持住,让后在Listview重绘的时候,从这个Map当中取出ListView的值进行赋值操作即可。同时注意,因为ListView并不知道你点击了哪个Spinner,因此无法通过position作为键,这时候,可以首先将能够以为标识所点击Item的一个字段作为Spinner的Prompt,当你选中之后,Spinner的Prompt就是你点击的那个Item,获取到Prompt作为Map的键即可。
对于第二个问题,见我的另一篇博客listView当中有嵌套了有onClickListener的控件时ListView自身的onItemClick无响应的
三、 具体见主要代码:问题一的主要代码(ListView的适配器):
package com.yang.adapter; import java.util.HashMap; import java.util.Map; import ouc.sei.R; import android.content.Context; import android.util.Log; import android.view.View; import android.view.ViewGroup; import android.widget.AdapterView; import android.widget.AdapterView.OnItemSelectedListener; import android.widget.BaseAdapter; import android.widget.Spinner; import android.widget.TextView; public class CheckListAdapter extends BaseAdapter { private Context mContext; private String[] checkListName; // 存储以名值对。存放Spinner的Prompt和用户选中的值 private Map<String, Integer> allValues; public CheckListAdapter(Context mContext, String[] checkListName) { this.mContext = mContext; this.checkListName = checkListName; allValues = new HashMap<String, Integer>(); putAllValues(); } private void putAllValues() { for (String str : checkListName) { allValues.put(str, 0); } } public void setAllValues(Map<String, Integer> allValues){ this.allValues = allValues; } @Override public int getCount() { return checkListName.length; } @Override public Object getItem(int position) { return checkListName[position]; } @Override public long getItemId(int position) { return 0; } private class ViewHolder { TextView checkinfo_item_name; Spinner checkinfo_item_value; } @Override public View getView(int position, View convertView, ViewGroup parent) { ViewHolder holder = null; if (convertView == null) { convertView = View.inflate(mContext, R.layout.checkinfo_list_item, null); holder = new ViewHolder(); holder.checkinfo_item_name = (TextView) convertView .findViewById(R.id.checkinfo_item_name); holder.checkinfo_item_value = (Spinner) convertView .findViewById(R.id.checkinfo_item_value); // 设置其adapter SpinnerAdapter adapter = new SpinnerAdapter(mContext); holder.checkinfo_item_value.setAdapter(adapter); holder.checkinfo_item_value .setOnItemSelectedListener(new ItemClickSelectListener( holder.checkinfo_item_value)); convertView.setTag(holder); } else { holder = (ViewHolder) convertView.getTag(); } String checkedName = checkListName[position]; holder.checkinfo_item_name.setText(checkedName); //关键代码,配合下面的相应事件使用。 holder.checkinfo_item_value.setPrompt(checkedName); int spinnerOptionPosition = allValues.get(checkedName); Log.d("CheckList", checkedName + " = = " + spinnerOptionPosition); holder.checkinfo_item_value.setSelection(spinnerOptionPosition); return convertView; } private class ItemClickSelectListener implements OnItemSelectedListener { Spinner checkinfo_item_value ; public ItemClickSelectListener(Spinner checkinfo_item_value) { this.checkinfo_item_value = checkinfo_item_value; } @Override public void onItemSelected(AdapterView<?> parent, View view, int position, long id) { //关键代码 allValues.put(checkinfo_item_value.getPrompt().toString(), position); } @Override public void onNothingSelected(AdapterView<?> arg0) { } } //返回用于选中的所有值 public Map<String,Integer> getSelectValues() { return allValues; } }
问题二的主要代码:
<?xml version="1.0" encoding="utf-8"?> <RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="fill_parent" android:layout_height="fill_parent" android:descendantFocusability="blocksDescendants" android:orientation="horizontal" > <TextView android:id="@+id/checkinfo_item_name" android:layout_width="wrap_content" android:layout_height="fill_parent" android:layout_alignParentLeft="true" android:layout_gravity="left" android:textColor="@android:color/black" android:textSize="18sp" /> <Spinner android:id="@+id/checkinfo_item_value" android:layout_width="125dip" android:layout_height="fill_parent" android:layout_alignParentRight="true" /> </RelativeLayout>
四、项目图片
1. Ownership Qualifiers
- __strong -- 不使用任何修饰符的情况下,默认是__strong。在ARC环境下,编译器会自动为__strong修饰的对象指针生成恰当的release代码,比如出了对象所属作用域,或者发生指针赋值时。
- __weak -- 使用__weak修饰符,编译器(准确说是Runtime System)会记录被修饰的指针,当指向对象被释放时,将用__weak修饰的指针全部置为nil。如果在非ARC环境下,假设a.delegate = b,通常delegate属性为assign,如果b被释放了,a继续用delegate指针访问,有可能导致程序崩溃。这时候可以模拟__weak特性,让b记录下a.delegate指针,在dealloc时将其置为nil。使用__weak指针,也可以消除循环引用。
- __unsafe__unretained -- 与__weak的差别就在于对象被释放后,不会将指针置为nil,所以最好是使用__weak,比较安全。通常为了支持iOS 4或者OS X 雪豹才使用__unsafe__unretained。
- __autoreleasing -- 顾名思义,该修饰符是将对象指针向自动释放池注册。在ARC环境下,可以使用@autoreleasepool {} 快速建立自动释放池,并且编译器会关注函数返回值,在需要时为其进行注册。比如 + (id)array { return [[NSMutableArray alloc] init]; },按理说出了作用域,对象会被释放,但这里编译器会将该对象注册到自动释放池中。比如为了安全使用__weak指针,编译器也会自动将对象注册到自动释放池中。有一点不同的是,无修饰符的对象指针默认是__strong,而无修饰符的id指针(或者是对象指针的指针)则默认为__autoreleasing。
- 忘掉retain、release、retainCount和autorelease。以前我们在需要安全使用一个对象时,往往会将该对象retain住,在不需要的时候进行release。在ARC环境下这么做会有编译错误,所以记住上面那四个修饰符就好。
- 忘掉NSAllocateObject和NSDeallocateObject。我个人压根没用过这两个函数,具体可以查看这里、这里、这里以及这里。
- 创建对象的方法需要遵循一定的命名规则。以alloc、new、copy、mutableCopy开头的函数表示调用者对被创建的对象拥有所有权,以init开头的函数表示对对象进行初始化。这些函数需要是实例方法(而非类方法),并且返回一个对象。
- 不要显示调用dealloc。比如在dealloc函数中不要加[super dealloc],否则会有错误“ARC forbids explicit message send of 'dealloc' ”。
- 使用@autoreleasepool代替NSAutoreleasePool。
- 忘掉NSZone。关于NSZone,这里有一份详细文档。
- 对象类型变量不能作为C语言中结构体(struct)或共用体(union)的成员。
- id和void *必须显示转换。这里涉及到三个关键字:__bridge、__bridge_retained和__bridge_transfer,比如void *p = (__bridge void *)obj。三个关键字分别表示没有所有权转移的类型转换、前后都拥有所有权的类型转换以及所有权转移(交接)的类型转换。
- __strong -- 编译器会帮我们在变量出作用域时添加release动作,如果该变量是函数返回值,增添加到自动释放池中。
- __weak -- 编译器和Runtime System维护一张weak表,在给一个__weak指针赋值时,会将指针地址和指向对象作为key-value注册到表中。当指向对象被释放时,搜索表将指针置为nil,并移除该表项。