Skip to content


关于 ios开发 oauth 安全性问题

关于oauth得详解在这里就不多说了.关于ios开发中得oauth认证过程为了方便用户使用一半会考虑用uiwebview来实现这个授权过程.然而在这个过程中..可以通过webview得委托方法抓到用户得帐号密码.简单分析以下post信息也包含在httpbody里,通过相应的委托方法 可以获得到当前页面的httpbody然后分析出post信息.
- (BOOL) webView: (UIWebView *) webView shouldStartLoadWithRequest: (NSURLRequest *) request navigationType: (UIWebViewNavigationType) navigationType {
NSData *data = [request HTTPBody];
NSLog(@”===%@”,[[[NSString alloc] initWithData:data encoding: NSUTF8StringEncoding] autorelease]);
return YES;
}

2011-08-23 17:00:41.154 weiQo[16073:b303] ===action=submit&regCallback=http%253A%252F%252Fapi.t.sina.com.cn%252Foauth%252Fmobilehtml5%253Foauth_token%253Dbaf7b00e4390d85ae38df5f57c185960%2526oauth_callback%253Doauth%253A%252F%252Fweiqo.com%2526from%253D&oauth_token=baf7b00e4390d85ae38df5f57c185960&display=null&oauth_callback=oauth%3A%2F%2Fweiqo.com&from=&userId=Netseye%40gmail.com&passwd=********(密码省略);
不过我认为这个问题会有很多有人心人在意

Posted in Life.


FPS 游戏主循环—-转载

原文地址

http://dewitters.koonsolo.com/gameloop.html

游戏主循环
引言
游戏主循环是每个游戏的心跳,输送着整个游戏需要的养分。不幸的是没有任何一篇好的文章来指导一个菜鸟游戏程序员如何为自己的程序供养。不过不用担心,因为你刚好不小心看到了这篇,也是唯一一篇给予这个话题足够重视的文章。
由于我身为游戏程序员,我见过许许多多的手机小游戏的代码。这些代码给我展示了五彩缤纷的游戏主循环实现方法。你可能要问:“这么简单的一个小玩意还能做到千奇百怪?” 事实就是这样,我就会在此文中讨论一些主流实现的优缺点,并且给你介绍在我看来最好的输送养分的解决方案。

游戏主循环

每一个游戏都是由获得用户输入,更新游戏状态,处理AI,播放音乐和音效,还有画面显示这些行为组成。游戏主循环就是用来处理这个行为序列。如我在引言中所说,游戏主循环是每一个游戏的心跳。在此文中我不会深入讲解上面提到的任何一个行为,而只详细介绍游戏主循环。所以我把这些行为简化为了两个函数:
update_game(); //更新游戏状态 (后文可能翻译为逻辑帧)
display_game(); //更新显示 (显示帧)
下面是最简单的游戏主循环:
bool game_is_running = true;
while( game_is_running ) {
update_game();
display_game();
}

这个简单循环的主要问题是它忽略了时间,游戏会尽情的飞奔。在小霸王机器上运行会使玩家有极强的挫败感,在牛逼的机器上运行则会要求玩家有超人的判断力和APM(原意为慢的机器上运行慢,快的机器上运行快%26#8230;%26#8230;)。在远古时代,硬件的速度已知的情况下,这不算什么,但是目前有如此多的硬件平台使得我们不得不去处理时间这个重要因素。对于时间的处理有很多的方法,接下来我会一一奉上。
首先我会解释两个贯穿全文的术语:
每秒帧数(后简称FPS)
FPS是Frames Per Second的缩写。在此文的上下文中它意味着display_game()每秒被调用的次数。
游戏速度
游戏速度是每秒更新游戏状态的速度,换言之,即update_game()每秒被调用的次数。
FPS依赖于恒定的游戏速度
实现
一个让游戏每秒稳定运行在25帧的解决方案如下:
const int FRAMES_PER_SECOND = 25;
const int SKIP_TICKS = 1000 / FRAMES_PER_SECOND;
DWORD next_game_tick = GetTickCount();
// GetTickCount() returns the current number of milliseconds
// that have elapsed since the system was started
int sleep_time = 0;
bool game_is_running = true;
while( game_is_running ) {
update_game();
display_game();
next_game_tick += SKIP_TICKS;
sleep_time = next_game_tick – GetTickCount();
if( sleep_time >= 0 ) {
Sleep( sleep_time );
}
else {
// Shit, we are running behind!
}
}

这个方案有一个非常大的优点:简单!因为你知道update_game()每秒被调用25次,那么你的游戏的逻辑部分代码编写将非常直白。比如说在这种主循环实现的游戏中实现一个 重放 函数将非常简单(译者注:因为每帧的间隔时间已知,只需要记录每一帧游戏的状态,回放时按照恒定的速度播放即可。就像电影胶片一样)。如果在游戏中没有受到随机值的影响,只需要记录玩家的输入就可以实现重放。
在你实现这个循环的硬件上你可以按需要调整FRAMES_PER_SECOND到一个理想的值,但是这个游戏主循环实现会在各种硬件上表现得怎么样呢?
小霸王机
如果硬件可以应付指定的FPS,那么不会有什么事情发生。但是小霸王通常是应付不了的,游戏就会卡。在极端情况下就会卡得掉渣,或者一步十卡、一卡十步(原意为某些情况下游戏速度很慢,有一些情况下又比较正常)。这样的问题会毁掉你的游戏,使得玩家及其挫败。
牛逼的机器
在牛逼的机器上似乎不会有任何问题,但是这样的游戏主循环浪费大量的时钟循环!牛逼的机器运行这个游戏可以轻松的跑到300帧,却每秒只运行了25或者30帧~ 那么这个主循环实现会让拥有牛逼硬件的玩家无法尽情发挥其硬件效果产生极大的挫败感(原意为这样的实现会让你的视觉效果受到影响,尤其是高速移动物体)。
从另外一个角度来说,在移动设备上,这一点可能会是一个优点。游戏持续的高速运行会很快地消耗电池%26#8230;%26#8230;
结论
基于恒定游戏速度的FPS的主循环实现方案简单易学。但是存在一些问题,比如定义的FPS太高会使得老爷机不堪重负,定义的FPS太低则会使得高端硬件损失太多视觉效果。
基于可变FPS的游戏速度
实现
另外一种游戏实现可以让游戏尽可能的飞奔,并且让依据FPS来决定游戏速度。游戏状态会根据每一显示帧消耗的时间来进行更新。
DWORD prev_frame_tick;
DWORD curr_frame_tick = GetTickCount();
bool game_is_running = true;
while( game_is_running ) {
prev_frame_tick = curr_frame_tick;
curr_frame_tick = GetTickCount();
update_game( curr_frame_tick – prev_frame_tick );
display_game();
}

这个游戏主循环的代码比起之前稍微复杂一些,因为我们必须去考虑两次update_game()调用之间的时间差。不过,好在这并不算复杂。
初窥这个实现的代码好像是一个理想的实现方案。我已经见过许多聪明的游戏程序员用这种方式来书写游戏主循环。但是我会给你展示这个实现方案在小霸王和牛逼的机器上的严重问题!是的,包括非常职业非常娴熟非常牛逼的玩家的机器。
小霸王
小霸王会在某些运算复杂的地方出现卡的情况,尤其在3D游戏中的复杂场景更是如此。帧率的降低会影响游戏输入的响应,同时降低玩家的反应速度。游戏状态更新也会因此突然受到影响。这样的情况会使得玩家和AI的反应速度减慢,造成玩家挫败感加剧。比如一个在正常帧率下可以轻松越过的障碍会在低帧率下无法逾越。更严重的问题是在小霸王上会经常发生一些违反物理规律的怪事,如果这些运算涉及到物理模拟的话。
牛逼的机器
你可能会好奇,为什么刚才的游戏循环在飞快的机器上会出现问题。不幸的是,这个方案的确如此,首先,让我给你介绍一些计算机数学知识。
浮点数类型占用内存大小是有限的,那么有一些数值就无法被呈现。比如0.1就不能用2进制表示,所以会被近似的存储在一个浮点数中。我用python给你们展示一下。
>>> 0.1
0.10000000000000001
这个问题本身并不怎么具有戏剧性,但是这样的后果却截然相反。比方说你的赛车的速度是0.001个单元每微秒。那么正确的结果是在10秒后你的赛车会移动10个单位,那么我们这样来实现一下:
>>> def get_distance( fps ):
… skip_ticks = 1000 / fps
… total_ticks = 0
… distance = 0.0
… speed_per_tick = 0.001
… while total_ticks < 10000:
... distance += speed_per_tick * skip_ticks
... total_ticks += skip_ticks
... return distance

现在我们来得到40帧每秒时运行10秒后的结果
>>> get_distance( 40 )
10.000000000000075
等等~怎么不是10呢?发生了什么?嗯,400次加法后的误差就有这么大,每秒运行100次加法后又会是怎么一个样子呢?
>>> get_distance( 100 )
9.9999999999998312
误差越来越大了!那么,40帧每秒的结果和100帧每秒之间误差差距是多大呢?
>>> get_distance( 40 ) – get_distance( 100 )
2.4336088699783431e-13
你可能会想这样的误差可以忽略。但是真正的问题出现在你使用这些错误的值去进行更多的运算。小的误差会被扩大为致命的错误!然后这些错误会在游戏飞奔的同时毁掉它!这些问题发生的几率绝对大到足够引起你的注意。我有见过因为这个原因在高帧率出现问题得游戏。之后那个游戏程序员发现这些问题出现在游戏的核心部分,只有重写大部分代码才能修复它。
结论
这样的游戏主循环看上起不错,但是并不怎么样。不管运行它的硬件怎样,都可能出现严重的问题。另外,游戏实现的代码相对于固定游戏速度的主循环而言更加复杂,那你还有什么使用它的理由呢?
最大FPS和恒定游戏速度
实现
我们的第一个实现中,FPS依赖于恒定的游戏速度,在低端的机器上会出现问题。游戏速度和游戏显示都会出现掉帧。一个可行的解决方案是牺牲显示帧率的来保持恒定的游戏速度。下面就实现了这种方案:

const int TICKS_PER_SECOND = 50;
const int SKIP_TICKS = 1000 / TICKS_PER_SECOND;
const int MAX_FRAMESKIP = 10;

DWORD next_game_tick = GetTickCount();
int loops;

bool game_is_running = true;
while( game_is_running ) {
loops = 0;
while( GetTickCount() > next_game_tick %26amp;%26amp; loops < MAX_FRAMESKIP) {
update_game();

next_game_tick += SKIP_TICKS;
loops++;
}

display_game();
}
游戏会以稳定的50(逻辑)帧每秒的速度更新,渲染速度也尽可能的快。需要注意的是,如果渲染速度超过了50帧每秒的话,有一些帧的画面将会是完全相同的。所以显示帧率实际上也等同于最快每秒50帧。在小霸王上运行的话,显示帧率会在更新游戏状态循环达到MAX_FRAMESKIP时下降。从上面这个例子来说就是当渲染帧率下降到5(FRAMES_PER_SECOND / MAX_FRAMESKIP)以下时,游戏速度会变慢。
小霸王
在小霸王上运行这样的游戏循环会出现掉帧,但是游戏速度不受到影响。如果硬件还是没有办法处理这样的循环,那么游戏速度和游戏帧率都会受到影响。
牛逼的机器
在牛逼的机器上这个游戏循环不会出现问题,但是如同第一个解决方案一样,还是浪费了太多的时钟周期。找到一个快速更新并且依然能够在小霸王上运行游戏的平衡点是至关重要的!
结论
使用上面的这个方案可以使游戏的实现代码比较简单。但是仍然有一些问题:如果定义了一个过高的FPS会让小霸王吃不消,如果过低则会让牛逼的机器难以发挥性能。

独立的可变显示帧率和恒定的游戏速度
实现
有没有可能对之前的那种方案进行优化使得它在各种平台上都有足够好的表现呢?当然是有的!游戏状态本身并不需要每秒更新60次。玩家输入,AI信息等都不需要如此高的帧率来进行更新,大约每秒25次就足够了。所以,我们可以试着让update_game()每秒不多不少的被调用25次。渲染则放任不管,让其飞奔。但是不能让小霸王的低渲染帧率影响到游戏状态更新的速度。下面就是这个方案的实现:
const int TICKS_PER_SECOND = 25;
const int SKIP_TICKS = 1000 / TICKS_PER_SECOND;
const int MAX_FRAMESKIP = 5;
DWORD next_game_tick = GetTickCount();
int loops;
float interpolation;
bool game_is_running = true;
while( game_is_running ) {
loops = 0;
while( GetTickCount() > next_game_tick %26amp;%26amp; loops < MAX_FRAMESKIP) {
update_game();
next_game_tick += SKIP_TICKS;
loops++;
}
interpolation = float( GetTickCount() + SKIP_TICKS – next_game_tick )
/ float( SKIP_TICKS );
display_game( interpolation );
}
使用这种方案的update_game()实现会比较简单,相对而言,display_game()则会变得稍许复杂。你需要实现一个接收插值参数的预言函数,这并不是什么难事,只是需要做一些额外的工作。我会接着解释这个预言函数是如何工作的,不过首先让我告诉你为什么需要这样的一个函数。
游戏状态每秒被更新25次,如果你渲染的时候不使用插值计算,渲染帧率就会被限定在25帧。需要注意的是,25帧并没有人们想象中的糟糕,电影画面在每秒24帧的情况下依然流畅。所以25帧可以很好的展示游戏画面,不过对于高速移动的物体,更高的帧率会带来更好的效果。所以我们要做的是,在显示帧之间让高速移动的物体平滑过度。这就是我们需要一个插值和预言函数的原因。
插值和预言函数
如我之前所说,游戏状态更新在一个恒定的帧率下运行着,当你渲染画面的时刻,很有可能就在两个逻辑帧之间。假设你已经第10次更新了你的游戏状态,现在你需要渲染你的场景。这次渲染就会出现在第10次和第11次逻辑帧之间。很有可能出现在第10.3帧的位置。那么插值的值就是0.3。举个例子说,我的一辆赛车以下面的方式计算位置。
position = position + speed;
如果第10次逻辑帧后赛车的位置是500,速度是100,那么第11帧的位置就会是600. 那么在10.3帧的时候你会在什么位置渲染你的赛车呢?显而易见,应该像下面这样:
view_position = position + (speed * interpolation)
现在,赛车将会被正确地渲染在530这个位置。
基本上,插值的值就是渲染发生在前一帧和后一帧中的位置。你需要做的就是写出预言函数来预计你的赛车/摄像机或者其他物件在渲染时刻的正确位置。你可以根据物件的速度来计算预计的位置。这些并不复杂。对于某些预计后的帧中出现的错误现象,如某个物体被渲染到了某个物体之中的情况的确会出现。由于游戏速度恒定在每秒更新25次状态,那么这种错误停留在画面上的时间极短,难以发现,并无大碍。
小霸王
大多数情况下,update_game()执行需要的时间比display_game()少得多。实际上,我们可以假设在小霸王上update_game()每秒还是能运行25次。所以游戏的逻辑状态不会受到太大的影响,即使FPS非常低。
牛逼的机器
在牛逼的硬件上,游戏速度会保持每秒25次,屏幕更新却可以非常快。插值的方案可以让游戏在高帧率中有更好的画面表现。但实质上游戏的状态每秒只更新了25次。
结论
使游戏状态的更新独立于FPS的解决方案似乎是最好的游戏主循环实现。不过,你必须实现一个插值计算函数。
整体总结
游戏主循环对游戏的影响远远超乎你的想象。我们讨论了4个可能的实现方法,其中有一个方案是要坚决避免的,那就是可变帧率来决定游戏速度的方案。
一个恒定的帧率对移动设备而言可能是一个很好的实现,如果你想展示你的硬件全部的实力,那么最好使用FPS独立于游戏速度的实现方案。
如果你不想麻烦的实现一个预言函数,那么可以使用最大帧率的实现方案,只是要找到一个帧率大小的平衡点。
现在,你可以着手编写你梦寐以求的游戏了!
Koen Witters

Posted in Game.

Tagged with , .


计算NSString字节长度,中文如何按2字节计算

这个问题得回归倒基础上面。字符串的长度跟编码有关系。utf8是变长,gbk是双字节
所以看如下代码
NSString *test = [NSString stringWithString:@"这是一个中文test1"];
NSStringEncoding enc = CFStringConvertEncodingToNSStringEncoding(kCFStringEncodingGB_18030_2000);
NSLog(@”%@ length is: %d”,test,[test lengthOfBytesUsingEncoding:enc]);

Posted in Life.


自动登陆ssh脚本

由于每天都要向服务器更新文件.每次都要来回的ssh输入用户名,输入密码,输入目录.为了省去这个繁琐的工作写了一个自动登陆脚本.

?Download download.txt
1
2
3
4
5
6
7
8
9
10
11
12
13
#!/usr/bin/expect
set timeout 1
spawn /usr/bin/ssh -oStrictHostKeyChecking=no netseye@192.168.1.2
expect "*password:"
send "123456\r"
expect "*]$"
send "ssh root@server9\r"
expect "*]#"
send "cd /www/ftp/netseye.com/web/svn\r"
expect "*]#"
send "su www\r"
interact
#expect eof

Posted in Life.


为了调试方便写了一个简单的脚本

?Download download.txt
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
#!/bin/bash
echo "___________________________________"
path=`ps aux|awk '/fpm/{print $14}'|head -n 1`;
echo "文件路径:"$path;
echo "开始替换......."
if grep -l "<value name=\"display_errors\">0</value>" $path;
then
sed -i "s/<value name=\"display_errors\">0<\/value>/<value name=\"display_errors\">1<\/value>/" $path;
echo "已经开启调试";
else
echo "已经关闭调试" ;
sed -i "s/<value name=\"display_errors\">1<\/value>/<value name=\"display_errors\">0<\/value>/" $path;
fi
echo "替换完成,重启服务";
#reload自己写在此处
echo "重启完成";

没啥技术含量.做下记录

Posted in PHP.


cocoa urlencode

由于制作脑内的iphone app初期是用采集来做的..这个里面url部分为中文这个其实转成utf8编码的就应该可以通过nsurl获取的

不过还是找到了一个cocoa的urlencode的方法

    NSString * encodedString = (NSString *)CFURLCreateStringByAddingPercentEscapes(
    NULL,
    (CFStringRef)unencodedString,
    NULL,
    (CFStringRef)@"!*'();:@&=+$,/?%#[]",
    kCFStringEncodingUTF8 );

做个笔记以后用.

Posted in Life.


Mac/iPhone Category & Protocol

http://blog.codingmylife.com/?p=41

Categories
#import “ClassName.h”
@interface ClassName ( CategoryName )
// method declarations
@end
#import “ClassName+CategoryName.h”
@implementation ClassName ( CategoryName )
// method definitions
@end

For example
@interface MyObject : NSObject{
NSNumber *number;
}
- (NSNumber *)number;
@end
@interface MyObject (Setter)
- (void)setNumber:(NSNumber *)newNumber;
@end
@implementation MyObject
- (NSNumber *)number{
return number;
}
- (void)setNumber(NSNumber *)newNumber{
number = newNumber;
}
@end

Protocol
@protocol MyProtocol
- (void)requiredMethod;
@optional
- (void)anOptionalMethod;
- (void)anotherOptionalMethod;
@required
- (void)anotherRequiredMethod;
@end

For example

  1. Inherit multiple protocol. @interface Formatter : NSObject <Formatting, Prettifying>
  2. Instance. Protocol *myXMLSupportProtocol = @protocol(MyXMLSupport);
  3. Check if methods are declared
  4. if(![receiver conformsToProtocol:@protocol(MyXMLSupport)]){
    //Object does not conform to MyXMLSupport protocol
    //If you are expecting receiver to implement methods declared in the
    //MyXMLSupport protocol, this is probably an error
    }

  5. -(NSString*)formattingService:(id<myXMLSupport>)anObject;

———————————————————————

http://icocoa.cn/ocsection/oc-tips/60-category-vs-protocol

Category

Category提供了一种比继承(inheritance)更为简洁的方法来对class进行扩展,我们可以为任何已经存在的class添加方法(不包括数据成员)却不需要访问该class的实现文件。

新添加的方法和原有的方法具有同等的地位,可以访问class的数据成员,并且完全植入到class的继承体系之中,子类同样会继承新添加的方法。 利用category对类进行扩展可以避免使类的继承体系过于臃肿,复杂,降低了维护成本。另外,新添加的方法如果和已经存在的方法具有相同的 prototype,那么新添加的方法将会覆盖已经存在的方法,也就是category使得使得在没有源文件时修改已存在class的 functionality或者清除bug成为可能,所有该class的对象的行为都将发生变化,这一点是继承无法达到的。

Category的缺点:

  1. 如果一个已经存在的方法被新方法覆盖,那么将没有任何途径可以对旧方法进行调用。这一点和继承是不同的,子类对父类成员函数重写(override),但我们依然可以对父类中的被重写的成员函数进行调用。
  2. 如果有多个category对同一个class的成员函数进行重写,那么具体的行为是未定义。

Category的应用:

  1. 对类进行扩展或patch。
  2. 使用category或(anonymous category)来模拟实现private method。
  3. 使用category(informal protocol)来实现delegation,在cocoa framework中就大量使用了category来声明delegate methods。
  4. 利用category把一个庞大的class划分为小块儿来分别进行开发,从而将functionality更好的进行划分,利于维护。

Protocol

根据字面意思,protocol就是一个协议,一个contract。一个class如果采用了某个protocol,那么它就必须要遵守这个协 议。从另外一个角度,只要我遵守了某个protocol或者标准,那我就可以和其它的类进行交互,而且其它类并不需要知道我的内部是如何实现的,例如 一套组合音响,只要其中的dvd,录音机等设备采用的是标准接口,那么它就可以很轻易的被放入这个音响系统中,发挥自己的功能。回到objective c,在cocoa touch framework中利用protocol声明delegate methods是同样的道理,cocoa touch framework,你们想要提供自定义功能或者响应某个事件吗?很简单,只要你们的功能遵守我提供的protocol。(注意,这里之所以采 用protocol来声明delegate methods,而不是cocoa framework中使用informal protocol(category),是因为objective c 2.0 引入了@optional methods,iphone又是在objective c 2.0之后出来的,所以cocoa touch framework中就用protocol替代informal protocol来声明delegate methods。)其实plugin,回调函数都是和protocol相同的思路。

另一方面,objective c只支持单一继承,protocol提供了一个途径来达到多重继承的目的。

Posted in Cocoa.

Tagged with , , , , .


iphone多线程汇总

1,NSThread

- (void)updateImageForCellAtIndexPath:(NSIndexPath *)indexPath
{
NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
UIImage *image = [self getImageForCellAtIndexPath:indexPath];
UITableViewCell *cell = [self.tableView cellForRowAtIndexPath:indexPath];
[cell.imageView performSelectorOnMainThread:@selector(setImage:) withObject:image waitUntilDone:NO];
[pool release];
}

- (void)tableView:(UITableView *)tableView willDisplayCell:(UITableViewCell *)cell forRowAtIndexPath:(NSIndexPath *)indexPath
{
[NSThread detachNewThreadSelector:@selector(updateImageForCellAtIndexPath:) toTarget:self withObject:indexPath];
}

2NSOperation

首先是建立NSOperationQueue和NSOperations。NSOperationQueue会建立一个线程,每个加入到线程operation会有序的执行。

NSOperationQueue *queue = [NSOperationQueue new];
NSInvocationOperation *operation = [[NSInvocationOperation alloc];
initWithTarget:self
selector:@selector(doWork:)
object:someObject];
[queue addObject:operation];
[operation release];

下面是使用NSOperationQueue的过程:

  1. 建立一个NSOperationQueue的对象
  2. 建立一个NSOperation的对象
  3. 将operation加入到NSOperationQueue中
  4. release掉operation

使用NSOperation有几种,现在介绍最简单的一种NSInvocationOperation,NSInvocationOperation是NSOperation的子类,允许运行在operation中的targer和selector

3NSURLConnection

如果你是网络应用使用NSURLConnection的异步代理无意是最简便的解决方案

Posted in Cocoa.

Tagged with , , , , , .


IPhone View层次结构说明

View层次结构说明:

1.容器类,加强其他视图的功能,或提供额外的显示效果。

如:UIScrollView是用来显示那些内容太多,不能在一屏里显示的视图的;

UITableView是UIScrollView的子类,用来展现列表形式的内容的,因为表中的行是可选的,

UITableView也被用于层次结构的导航。

UIToolBar是用来显示一个或多个与按钮相似的子项,通常UIToolBar显示在屏幕底部,用来

显示一组经常使用的命令按钮。UIToolBar可以一直显示着,也可以在需要的时候才显示。

Continued…

Posted in Cocoa.

Tagged with .


dedecms又让我纠结一次

由于工作需要,修改了下dedecms的showMsg()方法支持了外调模板.
却怎么也不能同步了.重新设置ucenter也无计于是.由于对ucenter不是很熟悉.所以只能调试.
经过半天的折腾才发现我漏掉了一个变量$ucsynlogin, uc_user_synlogin()的返回值.原来这
个返回的js是需要加载一下的.
原showMsg()里是这么写的isset($GLOBALS['ucsynlogin']) ? $GLOBALS['ucsynlogin'] : ”很
隐蔽的藏在一个字串里……真很纠结…

Posted in Life.