猴子原创,欢迎转载。转载请注明: 转载自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,并移除该表项。