iOS直接连接MacOS的MySQL服务器
Posted by Jimney Lee at 2012-02-12 with tags mysql, xcode, macos, iphone
一、在MacOS上搭建MySQL Server,本文采用Mac10.6.x,支持64位
1、根据系统版本和支持位数,下载MySQL Server的dmg文件
2、dmg包里面有三个文件
-
a、安装
mysql-5.1.61-osx10.6-x86_64.pkg
-
b、安装
MYSQL.prefPane
,可以在系统偏好设置(system preference)的其他(Other)栏显示。可以启动和停止Server。 -
c、(可选)安装
MySQLStartupItem.pkg
支持开机启动
3、将安装目录下support-files
目录中的my-small.cnf
拷贝到ect中,并更名为my.cnf
,MySQL启动时加载的配置文件
$ cd /usr/local/mysql-5.1.61-osx10.6-x86_64
$ sudo cp support-files/my-small.cnf /etc/
$ mv /ect/my-small.cnf /ect/my.cnf
4、初始root密码为空,所以需要设置root密码
$ mysqladmin -u root -p password [password]
5、登录测试,在系统设置偏好(system reference)中MySQL开启运行Server,并做些sql指令操作(本文省去)
$ mysql -h localhost -u root -p
6、关于字节编码需要做一些简单配置修改,初始默认支持latin
,所以数据库存入中文utf8
后,查询显示为乱码
- a、显示字符集编码
$ mysql> show variables like'character%';
- b、修改配置文件,完成后在登录mysql,运行上面的a操作,看字节编码是否已经全部为utf8,参考此处sql编码
$ sudo vi /ect/my.cnf
[client] default-character-set = utf8 [mysqld] default-storage-engine = INNODB character-set-server = utf8 collation-server = utf8_general_ci default-character-set = utf8
至此,完成了MySQL Server的安装与测试。过程跟linux基本一样,都是类unix系统。
二、iOS系统上访问MySQL Server
在iPhone/iPad上访问MySQL Server,是通过官方MySQL Client库来实现。iOS完全兼容C语言,所以通过官方的Client源码生成静态库来调用。
我们将制作这个静态库libmysqlclient.a
,支持Simulator和iOS设备。
1、下载client的源码,当前版本(mysql-connector-c-6.0.2.tar.gz)
2、源码采用cmake进行编译,所以请下载最新的cmake编译工具
3、通过终端进入到mysql-connector-c-6.0.2
目录下,运行下面命令,生成XCode工程
$ cmake -G Xcode 在这里推荐一个MacOS开源插件[cdto](https://github.com/jbtule/cdto)。类似ubuntu系统下,在可视化的目录窗口,终端进入直接对应的目录路径,挺方便的。
4、修改一些文件,不然编译会出错
- a、在
my_net.h
文件中,注销掉47和49行
//#include <netinet/in_systm.h>
#include <netinet/in.h>
//#include <netinet/ip.h>
- b、在
my_global.h
,129行编译宏添加arm编译选择
#if defined(__i386__) || defined(__ppc__) || defined(__arm__)
- c、在
my_config.h
,注销掉63行
5、通过xcodebuild
命令为simulator和ios设备,生成对应的编译库,然后通过lipo
生成一个统一包。首先在终端(terminal)cd到源码的libmysql
路径下,运行如下命令:
- a、生成simulator静态包,注意sdk选择与mac系统中一致
$ xcodebuild -project ../libmysql.xcodeproj -target mysqlclient -configuration Release -sdk iphonesimulator4.3 ONLY_ACTIVE_ARCH=NO ARCHS=i386 PRODUCT_NAME=mysqlclient_simulator
- b、生成ios设备静态包
$ xcodebuild -project ../libmysql.xcodeproj -target mysqlclient -configuration Release -sdk iphoneos4.3 ONLY_ACTIVE_ARCH=NO ARCHS="armv6 armv7" PRODUCT_NAME=mysqlclient_device
- c、使用lipo打在一起,保存在Release-iphoneos路径下
$ lipo ./Release-iphonesimulator/libmysqlclient_simulator.a ./Release-iphoneos/libmysqlclient_device.a -create -output ./Release-iphoneos/libmysqlclient.a
至此所用的静态包已经打好,库头文件为源码目录中的include目录下所有的头文件。
注意:今天测试发现,在lion系统下打的SDK 5.0的libmysqlclient.a
到Snow Leaporad编译出错,无法使用,所以在Snow Leaporad下重新编译这个库,可以正常使用,可能跟上面编译选项有关,如果有路过的兄弟知道原因,请不吝赐教。
三、访问测试,使用库测试参考MYSQL C API
通过XCode建立一个视图工程,在视图viewDidLoad中添加如下代码:
#import "mysql.h"
MYSQL *mysql = mysql_init(nil);
if(mysql_real_connect(mysql, localhost, username, passwd, dbname, 3306, NULL, 0)) {
//set encode utf8
mysql_set_character_set(mysql, "utf8");
NSLog(@"Connection Successful!");
//TODO:CRUD
}
else {
NSLog(@"Connection Failed");
}
后记
今天发现这些东西梳理出来写成博文的过程,跟code有一样的体验。理清思路,把知道的东西简洁表达而不被误解,还是要有一些思考和推敲。好长时间没写东西,纲领概要啥都无知,今后也要多写写。软件开发忌讳重复劳动,这意味额外的代价。人生短暂,伤不起也损不起。如果这篇博文能为您节省生命,乃我大幸大乐也!
解决three20在sdk5.0编译时tableView出现Empty section header
Posted by Jimney Lee at 2011-12-29 with tags three20, iphone
如题,three20的tableview在最新的sdk5.0中,即使未设置section的title,tableview头部仍然会显示一个空的sectionview,在网上瞎搜索几天,今天终于找到解决方法,e文不好,贴在这里也给其他遇到同样问题的朋友参考下。
参考git上320讨论组:
https://github.com/facebook/three20/issues/694
解决方法,简译一下:
为TTTableViewDelegate
方法创建一个category
(类别),实现
- (CGFloat)tableView:(UITableView *)tableView
heightForHeaderInSection:(NSInteger)section
获取section头高度的代理方法
TTTableViewDelegate+EmptySectionHeader.m
如下:
#import "TTTableViewDelegate+EmptySectionHeader.h"
@implementation TTTableViewDelegate (EmptySectionHeader)
- (CGFloat)tableView:(UITableView *)tableView heightForHeaderInSection:(NSInteger)section
{
id<TTTableViewDataSource> dataSource = (id<TTTableViewDataSource>)tableView.dataSource;
if ([dataSource isKindOfClass:[TTListDataSource class]])
return 0.;
return 20.;
}
@end
update 2011-12-29:
原来的方法使用比较受限制,对于自定义的datesource无法处理,而且比较麻烦。但是我们可以修改一下这个代理方法的实现过程,参考three20中TTTableViewGroupedVarHeightDelegate
中这个代理方法的实现,修改如下:
#import "TTTableViewDelegate+EmptySectionHeader.h"
static const CGFloat kEmptyHeaderHeight = 0;
static const CGFloat kSectionHeaderHeight = 18;
static const CGFloat kGroupEmptyHeaderHeight = 5;
static const CGFloat kGroupSectionHeaderHeight = 35;
@implementation TTTableViewDelegate (EmptySectionHeader)
- (CGFloat)tableView:(UITableView *)tableView heightForHeaderInSection:(NSInteger)section
{
if (tableView.style == UITableViewStylePlain) {
if ([tableView.dataSource respondsToSelector:@selector(tableView:titleForHeaderInSection:)]) {
NSString* title = [tableView.dataSource tableView:tableView titleForHeaderInSection:section];
if (!title.length) {
return kEmptyHeaderHeight;
} else {
return kSectionHeaderHeight;
}
}
else {
return kEmptyHeaderHeight;
}
}
else {
if ([tableView.dataSource respondsToSelector:@selector(tableView:titleForHeaderInSection:)]) {
NSString* title = [tableView.dataSource tableView:tableView titleForHeaderInSection:section];
if (!title.length) {
return kGroupEmptyHeaderHeight;
}
else {
return kGroupSectionHeaderHeight;
}
}
else {
return kGroupEmptyHeaderHeight;
}
}
}
@end
UIImage的一个category:缩放图片限制横宽区域内
Posted by Jimney Lee at 2011-05-20 with tags iphone
/*
* 缩放图片限制横宽区域内
*/
- (UIImage*)scaleWithSize:(CGSize)size {
CGFloat imageWidth = self.size.width;
CGFloat imageHeight = self.size.height;
CGFloat wScale = imageWidth / size.width;
CGFloat hScale = imageHeight / size.height;
NSLog(@"ws = %f, hs = %f", wScale, hScale);
CGSize newSize = self.size;
BOOL needRedraw;
if (wScale > hScale && wScale > 1) {
newSize.width = size.width;
newSize.height = imageHeight / wScale;
needRedraw = YES;
}
else if (hScale > wScale && hScale > 1) {
newSize.width = imageWidth / hScale;
newSize.height = size.height;
needRedraw = YES;
}
NSLog(@"w = %f, h = %f", newSize.width, newSize.height);
if (needRedraw) {
UIGraphicsBeginImageContext(newSize);
[self drawInRect:CGRectMake(0, 0, newSize.width, newSize.height)];
self = UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsEndImageContext();
}
return self;
}
赞一个开源库:720全景显示
Posted by Jimney Lee at 2011-01-05 with tags iphone, panorama
展示的效果跟香港720一样,感谢老外的开源精神。
google代码首页:
http://code.google.com/p/panoramagl/
库使用详细介绍:
http://www.codeproject.com/KB/iPhone/panoramagl.aspx
功能介绍:
- 1、支持720全景展示
- 2、支持重力加速度感应控制
遇到主要问题及解决方法:
1、报错如: >No architectures to compile for (ARCHS=armv6 armv7, VALID_ARCHS=i386 ppc ppc64 ppc7400 ppc970 x86_64).
需要添加armv6和armv7,如下图:
2、glu.h
链接文件出错,需要include "glues.h"
#ifndef __glu_h__
#define __glu_h__
#include "glues.h"
#endif
3、报错如:
Undefined symbols: “OBJC_CLASS$_PLTexture”, referenced from: objc-class-ref-to-PLTexture in HelloPanoramaViewController.o ld: symbol(s) not found
一般这个问题由于添加PanoramaGL.xcodeproj
到HelloPanorama
项目中时,忘了勾选库的右上角的圆框,如下图:
也就是stackoverflow上这句话的意思,但是因为没图,其他人看了很不理解,包括我 :( >I had the same problem, and noticed that although I included PanoramaGL xcode project, its lib (libPanoramaGL.a) was not selected to build with the target (that >little checkbox in the top right list of >XCode).
其实这句话意思是:这个.a静态库没有被选中,导致不能被编译到目标程序中。
右键.a文件,get info -->Targets
,多targets项目,可以指定一个静态库为那几个targets所有,同样的道理使用于程序源文件和资源文件,看了下面这幅图就更加理解了
记录一个低级错误
Posted by Jimney Lee at 2010-11-19 with tags iphone, debug
程序中需要在视图上重复添加新的子层CATiledLayer
类对象来显示不同的pdf文件,因为层对象采用[CATiledLayer layer]
静态方法来获取内存,心里想当然认为子层对象的生命周期不需要我再管了,每次都采用此静态方法产生新的子层对象,然后添加到当前视图的根层上。但是下班前突然发现打开七八张pdf文件后,机子就罢工,显示内存警告然后翘辫子了。
第一时间我就想到leak
工具,但是悲剧的是,并没有内存泄露提示。接着google了半夜,看了千层帖,终无果!浑天暗地之际,突然醒悟,addSublayer
到父层,会对子层retain一次,所以每次新开辟前,需要[tiledLayer removeFromSuperlayer]
来释放前一次的层对象。
总结:
- 1、写程序是穿线的活,心细不能想当然。
- 2、leak工具不是万能,关键时还需冷静分析。
- 3、调试不是自己写的程序,很费神。
- 4、头昏时就休息,不然效率低下,而且伤身体。
PS:不是因为紧急,偶是不会熬通宵,这是偶上班来的处女夜,竟然竟为此差错,不谈也罢,只为纪念!欢迎拍砖!
iPhone 锁屏后事件的处理
Posted by Jimney Lee at 2010-09-14 with tags iphone
今天测试程序发现,在一代机上,按了最上面的屏保键,然后回到程序界面,机子就死掉了,不过ipad每这个问题。 请教: 这个问题是一代机子本身问题,还是我的程序问题? 如果是机子问题,我在程序中怎么避免,在哪边可以获得外界屏保设置的消息?不甚感谢!
PS(2010-09-16):=============================================
之前的标题:iphone一代机,屏保后,程序死掉,机也完蛋 问题描述: 可能是我之前的描述有问题吧,其实我遇到的问题就是当人为按了最上面的锁屏键,解锁后回到程序时,一操作就会发生死机,郁闷一天! 后来经过分析调试,是主界面背景音乐的循环播放造成的,机器锁屏后,程序依旧运行,只是声音被系统暂停。 但是在我程序中,循环播放的背景音乐,因为锁屏、解锁,系统暂停再开启就会导致系统挂掉。 解决思路: 既然是因为锁屏,系统暂停我程序音乐播放导致的,而我程序内部对声音的暂停和恢复是没问题的。 所以我最初的思路就是不让系统暂停我的背景声音,通过获得锁屏后的事件,程序自己来暂停声音,然后锁屏后再恢复播放。沿着这个思路,找到这个触发的事件是王道。
未完待续,肚子催我回去了,明天补充!
PS (2010-09-17):================================================
从cc这篇文章中我了解到(PS:感谢文章作者,同时也非常感谢版主们的辛苦整理),程序的主代理UIApplicationDelegate
接口提供给生命周期函数来处理应用程序以及应用程序的系统事件,也就是说UIApplicationDelegate
代理能够处理系统发给应用程序的消息,那么我要找的锁屏后系统触发的事件,应该在这个主代理所能处理的生命周期函数中处理。
通过log输出发现,下面几个函数中能够输出信息:
- (void)applicationWillBecomeActive::(UIApplication *)application;
- (void)applicationWillResignActive:(UIApplication *)application;
- (void)applicationDidBecomeActive:(UIApplication *)application;
- (void)applicationDidResignActive:(UIApplication *)application; 从官方的SDK中得知,系统发送改变应用程序活动状态的事件,可以通过这两个函数处理。可以是一个来电或者SMS事件,当然还有我遇到的这个系统锁屏事件。 譬如:游戏正在运行,有来电了,你可以先暂停游戏,等通话结束,再恢复游戏。那我遇到的这个声音恢复导致死机问题,就可以自己来暂停和恢复背景声音。 问题是,我要在程序的主代理中`UIApplicationDelegate`处理这个事件很不方便,我的播放背景声音是在主Controller的子Controller中定义的,如果能在子Controller中处理这个事件,问题就容易多了。如何实现呢?
SDK文档这句话给了我启示:
applicationWillResignActive: Sent by the default notification center immediately before the application is deactivated. A notification named NSApplicationWillResignActiveNotification. Calling the object method of this notification returns the NSApplication object itself.
NSApplicationWillResignActiveNotification
是系统变成不活动(inactive)状态的消息,通过默认的notification center能够处理这个消息。对这个概念的理解,还得感谢CC的babylon_0049
朋友乐于分享他自己的一些理解和分析,这在帮助我解决这个问题,启到了很大的提示。
cc地址
同时关于UIApplicationDidBecomeActiveNotification
,文档里有这句话:
when the device is locked, and gains focus when the device is unlocked
这使我解决问题的方向更加明确具体。
呵呵,到这里,解决方法就不言而喻了! 在自控制器或者视图的初始化内添加如下语句:
//把self添加到NSNotificationCenter的观察者中
[[NSNotificationCenter defaultCenter] addObserver:self
selector:@selector(becomeActive)
name:UIApplicationDidBecomeActiveNotification
object:nil];
[[NSNotificationCenter defaultCenter] addObserver:self
selector:@selector(resignActive)
name:UIApplicationWillResignActiveNotification
object:nil];
响应的动作函数:
/*
*程序活动的动作
*/
- (void)becomeActive
{
//创建新的定时器
//继续播放声音,stop后currentTime值不变,直接使用play
//TODO:
}
/*
*程序不活动的动作
*/
- (void)resignActive
{
//关闭定时器
//停止背景播放声音,个人测试发现最好使用stop方法
//TODO:
} 与大家一起分享,希望能给没有接触这部分知识的朋友有一点帮助! cc地址:[http://www.cocoachina.com/bbs/read.php?tid=32245](http://www.cocoachina.com/bbs/read.php?tid=32245)