Pushwoosh Apple Push Notification Service is only partial working.

excellent developers:

I have developed corona sdk project to implement push notification for android and iPhone.

In my corona sdk project, I used GCM server for android version, and pushwoosh server for iPhone version.

Android version works well now. But iPhone version can receive push notification on a not running application. iPhone version can not receive push notification from pushwoosh on running application. Also application only loads message if user taps the push notification message alert on the iOS screen. 

I am searching solution of this issue for over 2 days, but I can not find any solutions in corona sdk project.

I shared registering/receiving push notification code from main.lua.

-------------------- main.lua --------------------

if ( system.getInfo("platformName") == "iPhone OS" ) then notifications.registerForPushNotifications() end local function notificationListener( event ) if ( event.type == "remoteRegistration" ) then local deviceToken = myData.tokenID local deviceType = 1 --default to iOS if ( system.getInfo("platformName") == "Android" ) then deviceType = 3 end if(deviceType == 1) then local PW\_APPLICATION = "XXXXX-XXXXX" --use your app ID in PushWoosh local PW\_URL = "https://cp.pushwoosh.com/json/1.3/registerDevice" local function networkListener( event ) if ( event.isError ) then --error occurred native.showAlert( "Notification Registration Failed", "An Error Contacting the Server has Occurred.", { "OK" } ) else --registration successful! print( "-----------------------------PushWoosh registration successful", system.getInfo("deviceID") ) end end local commands\_json = { ["request"] = { ["application"] = PW\_APPLICATION, ["push\_token"] = deviceToken, ["language"] = "en", --OR: system.getPreference( "ui", "language" ), ["hwid"] = system.getInfo("deviceID"), ["timezone"] = 3600, --offset in seconds ["device\_type"] = deviceType } } local post\_body = json.encode( commands\_json ) local headers = {} headers["Content-Type"] = "application/json" headers["Accept-Language"] = "en-US" local params = {} params.headers = headers params.body = post\_body network.request ( PW\_URL, "POST", networkListener, params ) end elseif ( event.type == "remote" ) then if ( system.getInfo("platformName") == "Android" ) then native.showAlert(event.alert.."on android") else native.showAlert(event.alert.."on iphone") end print("////////////////////////////////////// I got push notification("..event.alert.."). ////////////////////////////") end end local launchArgs = ... if ( launchArgs and launchArgs.notification ) then notificationListener( launchArgs.notification ) end Runtime:addEventListener( "notification", notificationListener ) 

Look forward to hearing good solutions to receive push notifications on running application in iPhone.

How can I implement corona sdk code or project settings?

Regards. Lion

Your code is really hard to read. It needs to be formatted with indentions. In the future please click the blue <> button and paste your formatted code in there. It will make everyone’s life easier. Can you edit your post and update the code?

Thanks

Rob

Dear Rob,

  I have the a problem with the push notification in iOS. (Android is OK)

  here is the code in main.lua

--================================================ notifications = require( "plugin.notifications" ) if system.getInfo("platformName") == "iPhone OS" then notifications.registerForPushNotifications() end local function notificationListener( event ) print( "Notification EVENT" ) if event.type == "remoteRegistration" then print("Push Notification Token:",event.token) -- I will try to send the token back to my own server here elseif event.type == "remote" then elseif event.type == "local" then end end Runtime:addEventListener( "notification", notificationListener ) --================================================

  I try same code on Android and iOS

  on Android, both 

        print( “Notification EVENT” )

        print(“Push Notification Token:”,event.token)

  worked!

  but on iOS, both

        print( “Notification EVENT” )

        print(“Push Notification Token:”,event.token)

  can not be reached! not working.

  I did setup config.lua and build.settings correctly

  Can you help me to solve the problem?

BOB

@wanderingsloth, on iOS if you touch the app’s icon on the screen to start it, you will never receive the push notification in your app. If you interact with the push notification from the notifications screen, your app is being cold started, you will get a launch notification via “LaunchArgs”. If your app is backgrounded, it will be foregrounded and you will get a notification event “remote”. If the app is active on screen you will also get a “remote” notification event. Please see:

https://docs.coronalabs.com/guide/events/appNotification/index.html#app-interaction

@bob can you post your build.settings?

Dear Rob,

here is my build.settings

-- Supported values for orientation: -- portrait, portraitUpsideDown, landscapeLeft, landscapeRight settings = { orientation = { default = "landscapeLeft", supported = { "landscapeLeft", "landscapeRight" } }, iphone = { plist = { CoronaWindowMovesWhenKeyboardAppears = true, -- 2015-09-30 暫時解決 ATS 問題 NSAppTransportSecurity = { NSExceptionDomains = { -- 允許自家 API 連結 ["qland.qll.co"] = { NSIncludesSubdomains = true, NSThirdPartyExceptionAllowsInsecureHTTPLoads = true, }, ["cdn.qll.co"] = { NSIncludesSubdomains = true, NSThirdPartyExceptionAllowsInsecureHTTPLoads = true, }, ["pubsub.pubnub.com"] = { NSIncludesSubdomains = true, NSThirdPartyExceptionAllowsInsecureHTTPLoads = true, }, ["s3.amazonaws.com"] = { NSIncludesSubdomains = true, NSThirdPartyExceptionAllowsInsecureHTTPLoads = true, -- 測試用 NSExceptionRequiresForwardSecrecy = false, }, -- s3 可能不到 TLS v1.2 https:// -- HTTPS must be used to connect to the server -- The server must support TLS v1.2 or higher -- 以下是 FB connect 用到的 ["fbcdn.net"] = { NSIncludesSubdomains = true, NSExceptionRequiresForwardSecrecy = false, }, ["facebook.com"] = { NSIncludesSubdomains = true, NSExceptionRequiresForwardSecrecy = false, }, ["akamaihd.net"] = { NSIncludesSubdomains = true, NSExceptionRequiresForwardSecrecy = false, }, }, }, LSApplicationQueriesSchemes = { "fb", "fbapi", "fbapi20130214", "fbapi20130410", "fbapi20130702", "fbapi20131010", "fbapi20131219", "fbapi20140410", "fbapi20140116", "fbapi20150313", "fbapi20150629", "fbauth", "fbauth2", "fb-messenger-api20140430", }, -- For all version of iOS. CFBundleIconFiles = { "Icon.png", "Icon@2x.png", "Icon-60.png", "Icon-60@2x.png", "Icon-60@3x.png", "Icon-72.png", "Icon-72@2x.png", "Icon-76.png", "Icon-76@2x.png", "Icon-167.png", "Icon-Small-40.png", "Icon-Small-40@2x.png", "Icon-Small-40@3x.png", "Icon-Small-50.png", "Icon-Small-50@2x.png", "Icon-Small.png", "Icon-Small@2x.png", "Icon-Small@3x.png" }, UIAppFonts = { }, CoronaDelegates = { "CoronaFacebookDelegate" }, CFBundleShortVersionString = "1.0", UIStatusBarHidden = false, UIPrerenderedIcon = true, -- set to false for "shine" overlay UIApplicationExitsOnSuspend = false, -- uncomment to quit app on suspend --FacebookAppID = "800806170027750", FacebookAppID = "810971139011253", --test -- iOS app URL schemes: CFBundleURLTypes = { { CFBundleURLSchemes = { --"fb800806170027750", -- scheme for facebook "fb810971139011253", -- scheme for facebook "geptlite", -- our scheme } } }, } }, android = { versionCode = "1", --usesExpansionFile = true, largeHeap = true, --facebookAppId = "800806170027750", facebookAppId = "810971139011253", coronaWindowMovesWhenKeyboardAppears = true, intentFilters = { { label = "QLand, where happy ending begins.", actions = { "android.intent.action.VIEW" }, categories = { "android.intent.category.DEFAULT", "android.intent.category.BROWSABLE", }, data = { scheme = "geptlite" } }, -- You can add more intent filters here. }, icon = { "icon-xxxhdpi.png", "icon-xxhdpi.png", "icon-xhdpi.png", "icon-hdpi.png", "icon-mdpi.png", "icon-ldpi.png", }, permissions = { { name = ".permission.C2D\_MESSAGE", protectionLevel = "signature", }, }, usesPermissions = { "android.permission.INTERNET", "android.permission.GET\_ACCOUNTS", "android.permission.ACCESS\_NETWORK\_STATE", "android.permission.ACCESS\_COARSE\_LOCATION", "android.permission.WRITE\_EXTERNAL\_STORAGE", "android.permission.WAKE\_LOCK", "android.permission.RECEIVE\_BOOT\_COMPLETED", "com.google.android.c2dm.permission.RECEIVE", ".permission.C2D\_MESSAGE", }, }, plugins = { ["plugin.notifications"] = { publisherId = "com.coronalabs" }, ["plugin.zip"] = { publisherId = "com.coronalabs", }, ["plugin.facebook.v4"] = { publisherId = "com.coronalabs", }, -- 為了廣告而新增的 ["plugin.fbAudienceNetwork"] = { publisherId = "com.coronalabs", supportedPlatforms = { iphone=true, ["iphone-sim"]=true, android=true } }, ["plugin.google.play.services"] = { publisherId = "com.coronalabs", supportedPlatforms = { iphone=true, ["iphone-sim"]=true, android=true } }, }, window = { defaultMode = "fullscreen", enableCloseButton = true, minViewWidth = 1280, minViewHeight = 960, titleText = { default = "全民英檢初級保證班", ["en"] = "Where happy ending begins.", }, }, osx = { plist = { CFBundleURLTypes = { { CFBundleURLName = "全民英檢初級保證班", CFBundleURLSchemes = { "geptlite", }, }, }, }, }, }

Thanks for the quick response.

Bob

Hi, Rob:

by the way, I found I can not get the simulator build after 2016.2866 run in my mac. But before 2016.2865 it is OK.

Is there anything changed?

Bob

These were the two additions in 2866:

  • OS X: fix issue with displaying errors correctly from build.settings. No casenum.
  • Simulators: add Runtime:setCheckGlobals() and system global checking. No casenum.

So an error in build.settings could be tripping you up or you’re trying to overwrite a system global. But in either case, there should be errors in the console windows that we show when you start the simulator.

What do you mean by you can’t get it to run? Can you provide a better description of what’s happening? Can you run and build our Hello World sample app?

Rob

Dear Rob,

  What do I mean by I can’t get it to run?

  Everytime I select the “RUN Project” from sublime>corona editor menu.

  I got the following output from terminal:

Copyright (C) 2009-2016 C o r o n a L a b s I n c . Version: 3.0.0 Build: 2016.2875 Platform: iPad / x86\_64 / 10.11.4 / Intel(R) Iris(TM) Graphics 6100 / 2.1 INTEL-10.14.58 / 2016.2875 / zh-Hant | TW | zh\_TW | zh Loading project from: ~/Documents/Corona Project/GEPT\_lite Project sandbox folder: ~/Library/Application Support/Corona Simulator/GEPT\_lite-A0ED2D88A036845FD08AEE5BA8097E39 PluginSync: plugin com.coronalabs/plugin.facebook.v4 needs to be updated for platform mac-sim to build number: 2669 PluginSync: downloading plugin: http://plugins.coronasphere.com/com.coronalabs/plugin.facebook.v4/2015.2669/mac-sim/plugin.facebook.v4.zip PluginSync: plugin com.coronalabs/plugin.notifications needs to be updated for platform mac-sim to build number: 2542 PluginSync: downloading plugin: http://plugins.coronasphere.com/com.coronalabs/plugin.notifications/2015.2542/mac-sim/plugin.notifications.zip PluginSync: plugin com.coronalabs/plugin.zip needs to be updated for platform mac-sim to build number: 2584 PluginSync: downloading plugin: http://plugins.coronasphere.com/com.coronalabs/plugin.zip/2013.2584/mac-sim/plugin.zip.zip PluginSync: plugin com.coronalabs/plugin.fuse needs to be updated for platform mac-sim to build number: 2828 PluginSync: downloading plugin: http://plugins.coronasphere.com/com.coronalabs/plugin.fuse/2016.2828/mac-sim/plugin.fuse.zip PluginSync: plugin com.coronalabs/plugin.fbAudienceNetwork needs to be updated for platform mac-sim to build number: 2731 PluginSync: downloading plugin: http://plugins.coronasphere.com/com.coronalabs/plugin.fbAudienceNetwork/2015.2731/mac-sim/plugin.fbAudienceNetwork.zip Archive: /Users/mac/Library/Application Support/Corona/Simulator/Plugins/plugin.facebook.v4.zip inflating: /Users/mac/Library/Application Support/Corona/Simulator/Plugins/plugin\_facebook\_v4.lua Archive: /Users/mac/Library/Application Support/Corona/Simulator/Plugins/plugin.fbAudienceNetwork.zip inflating: /Users/mac/Library/Application Support/Corona/Simulator/Plugins/plugin\_fbAudienceNetwork.lua Archive: /Users/mac/Library/Application Support/Corona/Simulator/Plugins/plugin.fuse.zip inflating: /Users/mac/Library/Application Support/Corona/Simulator/Plugins/plugin\_fuse.lua Archive: /Users/mac/Library/Application Support/Corona/Simulator/Plugins/plugin.notifications.zip inflating: /Users/mac/Library/Application Support/Corona/Simulator/Plugins/plugin\_notifications.lua Archive: /Users/mac/Library/Application Support/Corona/Simulator/Plugins/plugin.zip.zip inflating: /Users/mac/Library/Application Support/Corona/Simulator/Plugins/plugin\_zip.dylib WARNING: The 'plugin.facebook.v4' library is not available in the Corona Simulator. [Finished in 3.3s with exit code 1] [cmd: ['/Applications/CoronaSDK-2875/Corona Simulator.app/Contents/MacOS/Corona Simulator', '-singleton', '1', '-no-console', '1', '/Users/mac/Documents/Corona Project/GEPT\_lite/main.lua']] [dir: /Users/mac/Documents/Corona Project/GEPT\_lite] [path: /usr/bin:/bin:/usr/sbin:/sbin]

Then the Corona Simulator do not appear.

My college have the same problem too.

BOB

Dear Rob,

 Looks like we have an OOP.lua module is not compatible with the new 2016.2866 build

Could you give us a hint of how can we solve the problem. (How we should modify the code)

Here is a part of the code sample:

 

--## package path --[[packagePath("lib.OOP")]]-- --## package requirements --## class:OOP --OOP是一個物件導向類別。協助lua語言更接近物件導向。提供多種方法實現類別、封裝、繼承與多態,允許函數限定參數型別,增加程式撰寫的嚴謹程度,提升程式碼重用性,使程式更容易被擴充與除錯。 --此類別無實例,所以沒有new()方法(建構函數)。此類別不會參與繼承,本身也無法被存取(只會傳回nil)。 -- --\<公開屬性與方法\>:此類別的所有公開方法與屬性都被設定成全域方法,可在任何地方直接呼叫,但無法透過此類別呼叫。OOP類別會在它被require()引入時立即進行佈署,所有透過OOP設置的全域方法與屬性都將立即可用。 --由OOP所建立的方法與屬性等同於關鍵字,他們的名稱將成為保留字元,他們無法被修改或更動。必須注意的是,大部分作為關鍵字的方法與屬性,只能被用在類別或物件的定義區塊當中。 -- --\<定義類別與物件\>:透過呼叫class()來開始一個類別定義區塊,呼叫endClass()則可結束類別定義區塊。在區塊當中可使用 set, get, func, override, const 等關鍵字來定義函數和屬性。 --通常一個類別會有一個new()方法。用來建立這個類別的實例。在new()方法中,必須使用object() 和 endObject()方法所圍成的區塊來定義物件。在這範圍中同樣可使用上述的關鍵字來定義函數與屬性。 -- --\<虛擬類別\>:coronaSDK 的官方API 並非物件導向架構。換而言之透過官方API所產生的物件(例如:display.newImage())並沒有所屬的類別。這表示我們將無類別可繼承,而當這些物件被當作參數傳入時,我們也很難確認他的型別。 --透過虛擬類別的機制,我們將可以解決這個問題。透過 virtualClass()與virtualObject()方法,我們將官方API用來產生物件的函數(例如:display.newImage()),轉換成虛擬類別來使用。必要時虛擬類別也可以繼承類別,但僅限繼承抽象類別。 -- --●警告:這個文件當中的程式碼,具有相當複雜的邏輯。並且會影響整個 library的正常運作。即使除錯時錯誤訊息指向此處,卻經常是由於其他文件中,有語法以不正確的方式呼叫此文件中的方法,或傳入不正確的參數。除非「非常確定」問題發生於此文件,否則請盡可能的不要改動此文件的內容。 -- 如果要改動此文件的內容,請確保你的頭腦是清楚的,而且你知道你在做什麼。否則請詢問作者 Chris (cocoychris@gmail.com)。 --[[local OOP=abstract(class("OOP")) local OOP=object(OOP) return endObject()]]-- local OOP=nil--不會傳回任何值 --## static private property local gProtectProps={}--這個table用來儲存全域屬性當中透過proxy轉址而訪問的受保護屬性(和方法),它們是唯讀的。由於是全域屬性,所以可以在任何地方讀取或呼叫。 local newObj--正在被文件定義屬性或方法的物件 local newClass--正在被文件定義屬性或方法的類別 local specialPropAccessKey={}--以這個鑰匙當作索引來存取物件特殊屬性,特殊屬性由OOP物件設置,並只能由OOP物件讀取。 local newObjList={}--此清單用來儲存巢狀object()endObject()結構中,當前階層以下階層的newObj參考。 local newFuncName=""--透過func關鍵字建立新函數時,函數名稱會被暫存於於此處。 local virtualClassDict={}--所有透過requireVirtual()建立的虛擬類別,都會被集中於此。 local oldNativeFuncDict={}--所有被複寫的全域官方API原始方法都會被放在這裡 --## static public property ----全域關鍵字---- gProtectProps.override={}--複寫方法的關鍵字。用以在物件的定義中,複寫物件所繼承的方法。 gProtectProps.set={}--建立setter的關鍵字。在物件或類別定義中,透過此關鍵字指定的方法,將被當作一個可被 寫入 的屬性使用。指定的方法必須要設定一個參數,以便接收傳入的值。 gProtectProps.get={}--建立getter的關鍵字。在物件或類別定義中,透過此關鍵字指定的方法,將被當作一個可被 讀取 的屬性使用。若僅指定getter而沒有setter,那麼該屬性便是唯讀的。指定的方法必須有一個傳回值。 gProtectProps.const={}--建立constant的關鍵字。在物件或類別定義中,透過此關鍵字指定的屬性將成為唯讀屬性,其值將無法更改。 gProtectProps.func={}--建立function的關鍵字。不同於預設的function 關鍵字,透過此關鍵字宣告的函數將支援資料型別檢查,呼叫該函數時若參數或傳回值型別錯誤,將會出現錯誤訊息。 ----全域類別---- --gProtectProps.Class={}--虛擬類別「Class」。所有類別作為物件,都屬於「Class」這個類別的實例。 --gProtectProps.Object={}--虛擬類別「Object」。所有類別都繼承自這個類別。 gProtectProps.Class=require("lib.Class")--虛擬類別「Class」。所有類別作為物件,都屬於「Class」這個類別的實例。 gProtectProps.Object=require("lib.Object")--虛擬類別「Object」。所有類別都繼承自這個類別。 ----全域資料型別---- gProtectProps.Number="number"--參數限定型別 Number gProtectProps.String="string"--參數限定型別 String gProtectProps.Boolean="boolean"--參數限定型別 Boolean gProtectProps.Nil="nil"--不建議使用,請改用Any表示任意型別 gProtectProps.Function="function"--參數限定型別 Function gProtectProps.Table="table"--參數限定型別 Table gProtectProps.Thread="thread"--參數限定型別 Thread gProtectProps.UserData="userdata"--參數限定型別 UserData gProtectProps.Any="any"--特殊值,表示可接受任何型別 gProtectProps.Void="void"--特殊值,表示不該有任何參數傳回,此型別尚未被實作,請勿使用。 --## static private method --設置虛擬類別-- local function createVirtualClass() gProtectProps.Class:init(gProtectProps,specialPropAccessKey,oldNativeFuncDict) gProtectProps.Object:init(gProtectProps,specialPropAccessKey,oldNativeFuncDict) end --取得或設置目前的物件特殊屬性 local function getSpecProps(object) local result if(object) then result=object[specialPropAccessKey] end return result end --函數參數檢驗器 local function funcArgTester(proxyTable,table,...) local argList={...} local typeList=proxyTable.typeList local argValid=true if(#argList\<proxyTable.requireArgCount) then error("ERROR: Incorrect argument count to function '"..proxyTable.funcName.."' (expect: "..proxyTable.requireArgCount..", got: "..#argList..") on "..proxyTable.objNameText.." .") argValid=false elseif(argValid) then for i = 1, #typeList do local expectType=typeList[i] local arg=argList[i] local argType=type(arg) --邏輯:如果 此參數應檢查,且以下兩種狀況之一發生:「1.在必要參數範圍之內 2.參數不為Nil。」且參數與指定類型不相符,則擲回錯誤。 if(expectType~=Nil and expectType~=Any and (i\<=proxyTable.requireArgCount or arg~=nil) and argType~=expectType and not isClass(arg,expectType)) then --轉為類別名稱 expectType=getClassName(expectType) or expectType or "(undefined)" argType=getObjClassName(arg) or argType or "(undefined)" error("ERROR: Bad argument #"..i.." to function '"..proxyTable.funcName.."' (expect: "..expectType..", got: "..argType..") on "..proxyTable.objNameText.." .") argValid=false end end end local returnValue if(argValid) then returnValue=proxyTable.func(...) local returnType=proxyTable.returnType if(returnType) then local gotRetType=type(returnValue) if(gotRetType~=returnType and not isClass(returnValue,returnType) and gotRetType~=Nil) then if(isClass(returnType,Class))then returnTypeName=getClassName ( returnType ) else returnTypeName=tostring(returnType) end error("ERROR: Bad return from function '"..proxyTable.funcName.."' (expect: "..returnTypeName..", got: "..tostring(gotRetType)..").") end end end return returnValue end --比較override新版本的型別限定是否相容於舊的限定。 local function compareTypeList(specProps,proxyTable) local valid=true local oldProxyTable=specProps.proxyTableDict[newFuncName] local newTypeList=proxyTable.typeList if(oldProxyTable) then local oldTypeList=oldProxyTable.typeList valid=#oldTypeList\<=#newTypeList--新的參數限定數量可以比舊的多 if(valid) then valid=proxyTable.requireArgCount\<=oldProxyTable.requireArgCount--新的必要參數數量可以比舊的少 end if(valid) then for i = 1, oldProxyTable.requireArgCount do valid=oldTypeList[i]==newTypeList[i]--型別完全相等 if(not valid) then break end end valid=(oldProxyTable.returnValue==nil or oldProxyTable.returnValue==Nil)or oldProxyTable.returnValue==proxyTable.returnValue end assert(valid,"ERROR: Overriding function '"..newFuncName.."' failed. Argument type specification cannot be different from super function.") end return valid end --建立轉址table,將函數與資訊儲存, --每一個透過func 關鍵字建立的函數都有一個ProxyTable。用來記錄相關資訊。 --funcData 是一個陣列,前面的值是函數的參數型態限定,最後一個值是函數的參照。 local function createProxyTable(funcData) local specProps=getSpecProps(newObj) if(specProps) then local proxyTable={} local lastData=table.remove(funcData) if(type(lastData)=="function") then--返回值限定被省略 proxyTable.returnType=nil proxyTable.func=lastData or error("ERROR: Creating function '"..newFuncName.."' on "..newObj.toString().." failed. Got one or more nil value in bracket '{}' of function definition.") else proxyTable.returnType=lastData--寫入返回值限定 lastData=table.remove(funcData) if(lastData and type(lastData)=="function") then proxyTable.func=lastData else error("ERROR: Creating function '"..newFuncName.."' on "..newObj.toString().." failed. Got one or more nil value in bracket '{}' of function definition.") end end if(type(funcData[#funcData])=="number") then--寫入必要參數數量 proxyTable.requireArgCount=table.remove(funcData)--有指定 else proxyTable.requireArgCount=#funcData--無指定,自動以長度作為預設 end proxyTable.typeList=funcData proxyTable.funcName=newFuncName proxyTable.\_\_call=funcArgTester--轉址到參數檢驗器 proxyTable.objNameText=newObj.toString() local valid=compareTypeList(specProps,proxyTable) if(valid) then setmetatable(proxyTable, proxyTable) specProps.proxyTableDict[newFuncName]=proxyTable --建立替代原始函數的介面函數 local function interfaceFunc(...) return proxyTable(...) end --將介面函數寫入物件 -- print("Adding function with keyword 'func'.",newFuncName) specProps.funcDict[newFuncName]=interfaceFunc end end end ---------------------------覆寫原始方法------------------------------ local function redefineFunctions() --覆寫 tostring() oldNativeFuncDict.tostring=\_G.tostring \_G.tostring=function(e) if(getObjClass(e)) then return e:toString() else return oldNativeFuncDict.tostring(e) end end end ---------------------------設置關鍵字 Proxy-------------------------- --\<設置函數複寫proxy\> --一般複寫 local function overrideHandeler(table,key,value) local specProps=getSpecProps(newObj) if(specProps and type(value)=="function") then if(specProps.funcDict[key]) then if(specProps.proxyTableDict[key]==nil) then if(specProps.superProps)then specProps.superProps[key]=specProps.funcDict[key] end specProps.funcDict[key]=value else error("ERROR: Overriding method '"..key.."' failed. Argument type specification required.") end else error("ERROR: Function '"..key.."' does not exist, therefore cannot be overridden.") end else error("ERROR: Overriding method '"..key.."' failed. Illegal 'override' usage.") end end --進階複寫。此種複寫使用方式等同於func關鍵字,允許在複寫時指定資料型別。 local function advancedOverrideHandeler(table,key) local specProps=getSpecProps(newObj) local result=function ()end newFuncName=key if(specProps)then if(specProps.proxyTableDict[newFuncName]~=nil) then result=createProxyTable if(specProps.superProps)then specProps.superProps[newFuncName]=specProps.proxyTableDict[newFuncName] end else error("ERROR: Function '"..newFuncName.."' does not exist, therefore cannot be overridden.") end else error("ERROR: Overriding function '"..newFuncName.."' failed. Keyword 'override' can only be used in class or object definition.") end return result end local function setOverrideProxy() local overrideProxy=gProtectProps.override local metaTable={\_\_index=advancedOverrideHandeler,\_\_newindex=overrideHandeler} setmetatable(overrideProxy, metaTable) end --\<設置setter的proxy\> local function setterHandeler(table,key,value) local specProps=getSpecProps(newObj) if(specProps and type(value)=="function") then--允許寫入 specProps.setFuncDict[key]=value else--寫入失敗 error("ERROR: Adding setter '"..key.."' failed.") end end local function setSetterProxy() local setterProxy=gProtectProps.set local metaTable={\_\_newindex=setterHandeler} setmetatable(setterProxy, metaTable) end --\<設置getter的proxy\> local function getterHandeler(table,key,value) local specProps=getSpecProps(newObj) if(specProps and type(value)=="function") then specProps.getFuncDict[key]=value else error("ERROR: Adding getter '"..key.."' failed.") end end local function setGetterProxy() local getterProxy=gProtectProps.get local metaTable={\_\_newindex=getterHandeler} setmetatable(getterProxy, metaTable) end --\<設置constant的proxy\> local function constantHandeler(table,key,value) local specProps=getSpecProps(newObj) if(specProps) then if(specProps.constDict[key]==nil) then specProps.constDict[key]=value -- print("constant", key,value) else print("WARNING: Attempt to change value of constant '"..key.."'.") end else error("ERROR: Adding constant '"..key.."' failed. Keyword 'const' can only be used in class or object definition.") end end local function setConstantProxy() local constantProxy=gProtectProps.const local metaTable={\_\_newindex=constantHandeler} setmetatable(constantProxy, metaTable) end --設置func的proxy --當func關鍵字被index,傳回函數createProxyTable,以便承接函數定義資料。 local function funcHandeler(table,key) local specProps=getSpecProps(newObj) local result=function ()end newFuncName=key if(specProps)then if(specProps.proxyTableDict[newFuncName]==nil) then result=createProxyTable else error("ERROR: overriding object method '"..newFuncName.."' through key word 'func' failed. Keyword 'override' required.") end else error("ERROR: Adding function '"..newFuncName.."' failed. Keyword 'func' can only be used in class or object definition.") end return result end local function setFuncProxy() local funcProxy=gProtectProps.func local metaTable={\_\_index=funcHandeler,\_\_newindex=function ()end} setmetatable(funcProxy, metaTable) end ----------------設置物件 Proxy----------------- --設置super的proxy,使其屬性成為唯讀 -- local function setSuperProxy(specProps) -- local super={} -- setmetatable(super,{\_\_index=specProps.superFuncDict}) -- return super -- end --為proxy準備 local function prepareObjProxy(obj) obj=obj or newObj specProps=getSpecProps(obj) --將所有setter與getter函數存在這兩處 specProps.funcDict={}--物件的方法將被置於此處保護起來 specProps.setFuncDict={}--物件的setter方法 specProps.getFuncDict={}--物件的getter方法 -- specProps.superFuncDict={}--物件的繼承方法,被複寫(override)時會被轉移到此處保存 --specProps.superSetFuncDict={}--未來可以加入 --specProps.superGetFuncDict={}--未來可以加入 -- specProps.constDict={super=setSuperProxy(specProps)}--物件的常數,包含一個特殊常數super,透過此常數可存取已被複寫的方法。 specProps.constDict={}--物件的常數 specProps.proxyTableDict={}--使用func關鍵字建立的函數,都會有一個proxyTable包含該函數的相關資訊,這些proxyTable被集中在此。 specProps.propExistDict={}--將物件上存在的固定屬性指向True,確保屬性不會因為被設為Nil而被移除。 --將obj上的 toString方法 轉移到特殊屬性當中保護起來。 if(obj.toString) then specProps.funcDict.toString=obj.toString obj.toString=nil--移除原本的toString,以免干擾。 end end --為原廠API物件建立函數呼叫轉址。手動將呼叫物件,設成該物件。 local function createFactoryFuncProxy(funcName,specProps,factoryFunc) --這裡的table是發出呼叫的物件,此物件是我們建立的虛擬物件,為避免將虛擬物件傳遞給原廠物件,我們必須將它手動改成原廠物件。 local function proxyFunc(table,...) return factoryFunc(specProps.factoryObject,...) end specProps.factoryFuncProxyDict[funcName]=proxyFunc --加入清單 return proxyFunc end --啟動物件的proxy local function applyObjProxy() local specProps=getSpecProps(newObj) --轉向到key對應的get方法 local function objIndex(table,key) local value -- print("Index",table,getObjClassName(table),key) if(specProps.getFuncDict[key]) then--getter方法 value=specProps.getFuncDict[key]() elseif(specProps.constDict[key]) then--constant(常數) value=specProps.constDict[key] elseif(specProps.funcDict[key]) then--一般函數 -- print("call func",key,table) value=specProps.funcDict[key] elseif(specProps.factoryObject and specProps.factoryObject[key]) then--在 factoryObject 上找到屬性() value=specProps.factoryObject[key] if(type(value)=="function") then--若為函數,就必須置換成proxy函數。避免將此虛擬物件被當作self參數傳入原廠物件。 value=specProps.factoryFuncProxyDict[key] or createFactoryFuncProxy(key,specProps,value) end -- else--物件上的屬性 -- value=rawget(table, key) end return value end --轉向到key對應的set方法 local function objNewIndex(table,key,value) -- print("NewIndex",table,getObjClassName(table),key,value) local isDefining=table==newObj--物件是在定義過程中被附加屬性 if(specProps.setFuncDict[key]) then--找到setter,轉交給setter函數處理 -- print("found setter.") specProps.setFuncDict[key](nil,value) elseif(specProps.getFuncDict[key] or specProps.constDict[key]) then--未找到setter,但找到getter或constant,此為受保護的屬性! print("WARNING: Attempt to set property '"..key.."' (A constant or a read-only variable).") elseif(specProps.funcDict[key]) then--已找到對應的函數,不允許複寫 print("WARNING: Attempt to override object method '"..key.."' failed. Keyword 'override' required.") elseif(specProps.factoryObject and specProps.factoryObject[key]~=nil) then--在factoryObject找到屬性 if(type(specProps.factoryObject[key])=="function") then--若為函數,則阻止寫入。 print("WARNING: Attempt to override object method '"..key.."' failed. This method is protected and cannot be overridden.(a factory object method.)") else specProps.factoryObject[key]=value--將值寫入屬性(而非在物件上新增屬性) -- print("write value "..value.." to factory object property '"..key.."'.") end elseif(specProps.isDynamic or isDefining or specProps.propExistDict[key]) then--沒有setter和getter,若允許動態新增 或 物件正在被定義 或 在固定屬性清單中找到屬性,則加入新的屬性。 --附加屬性或方法 if(type(value)~="function") then -- print("dynamic property adding.",key,value) specProps.propExistDict[key]=true rawset(table, key, value) else -- print("dynamic function adding.",key) specProps.funcDict[key]=value end else print("WARNING: Adding property '"..key.."' to "..tostring(table).." failed! Dynamic property adding is not supported to this object.") end end --設置 MetaTable if(not getmetatable(newObj)) then local objMetaTable={\_\_index=objIndex,\_\_newindex=objNewIndex} setmetatable(newObj, objMetaTable) end end -----------------------設置全域(\_G) Proxy-------------------- --設置\_G的屬性Proxy --讀取 local function gIndex(table,key) local value=gProtectProps[key] return value or rawget(\_G, key) end --寫入 local function gNewIndex(table,key,value) if(gProtectProps[key])then--防止複寫 print("WARNING: Global property '"..key.."' is read-only.") else rawset(\_G, key, value) end end --設置\_G(全域)的MetaTable以重新導向 local function setGMeta() local gMetaTable={} gMetaTable.\_\_index=gIndex gMetaTable.\_\_newindex=gNewIndex setmetatable(\_G, gMetaTable) end --初始化 local function init() createVirtualClass() setOverrideProxy() setSetterProxy() setGetterProxy() setConstantProxy() setFuncProxy() setGMeta() gProtectProps.packagePath("lib.OOP") redefineFunctions() end --## static public method ----------------------類別定義關鍵字(方法)------------------ --[[--建立新的類別,並開始類別定義區塊 --name:類別名稱 --extendsClass:繼承的類別 func.class{String,Class,1, function(name,extendsClass) end ,Class}]]-- function gProtectProps.class(name,extendsClass) if(newClass==nil)then newClass=object(Class)--類別作為物件時屬於類別class的實例 classSpecProps=getSpecProps(newClass) classSpecProps.name=name classSpecProps.isAbstract=false extendsClass=extendsClass or Object if(extendsClass and isClass(extendsClass,Class)) then --繼承類別 local extInheritDict=getSpecProps(extendsClass).inheritDict classSpecProps.inheritDict={[newClass]=true} for key, value in pairs(extInheritDict) do classSpecProps.inheritDict[key]=value end classSpecProps.superClass=extendsClass -- print("CLASS:",name,"extends",getClassName(extendsClass)) end else error("ERROR: Creating class '"..name.."' failed. Class definitions are nested or 'endClass()' function call is missing!") end return newClass end --[[--建立新物件,並開始物件定義區塊 --class:物件的類別 --superConstructorCall:呼叫所繼承類別的new()方法(建構函數)並取得傳回的實例。抽象類別免填。 func.object{Class,Object,1, function(class, superConstructorCall) end ,Object}]]-- function gProtectProps.object( class, superConstructorCall) --處理抽象類別,superConstructorCall 由抽象類別自行呼叫並填入。 local superClass=getSpecProps(class).superClass if(superClass and superClass~=Object) then local superSpecProp=getSpecProps(superClass) if(superSpecProp.isAbstract and superSpecProp.abstractNew) then superConstructorCall=superSpecProp:abstractNew()--呼叫抽象類別的 superConstructorCall end end --儲存尚未以endObject()完結的newObj if(newObj) then table.insert(newObjList,newObj) end --建立新物件並且設置 繼承 與 無繼承 時的屬性 if(superConstructorCall) then--已經繼承 newObj=superConstructorCall if(superClass~=getObjClass(superConstructorCall)) then error("ERROR: Defining object of class '"..(getClassName(class)or "unknown").."' failed. Argument 'superConstructorCall' of 'object()' should be the constructor function call of class '"..(getClassName(superClass)or "unknown").."'.") end else--無繼承 newObj=Object:new() prepareObjProxy() end local specProps=getSpecProps(newObj) -- print("OBJ:",getClassName(class),newObj) --設置 物件特殊屬性 specProps.class=class specProps.asClass=class--類別備份,在使用asClass()轉換之後此值會變更,增加類別相容度 specProps.isDynamic=false applyObjProxy() return newObj end --[[--將一個類別宣告為抽象類別(抽象類別可以沒有new()方法,有也無法被外部呼叫。因此無法建立實例,只有被繼承之後才可建立實例。) --classFunctionCall:對class()函數的呼叫 --使用範例: local MyObj=abstract(class("MyObj")) func.abstract{Class, function(classFunctionCall) end ,Class}]]-- function gProtectProps.abstract(classFunctionCall) local specProps=getSpecProps(classFunctionCall) if(classFunctionCall and specProps) then local superSpecProps=getSpecProps(specProps.superClass) if(superSpecProps.isAbstract or specProps.superClass==Object) then specProps.isAbstract=true else error("ERROR: Setting class '"..getClassName(classFunctionCall).."' as abstract failed. (Super class should also be an abstract class.)") end else error("ERROR: Require 'class()' function call as argument to 'abstract()'.") end return classFunctionCall end --[[--將一個物件宣告為允許動態附加屬性 --請小心使用 dynamic,因為一旦設置成 dynamic 所有繼承此類別的物件也都將強制繼承 dynamic 的設置。 --objectFunctionCall:對object()函數的呼叫 --使用範例: local myObj=dynamic(object(MyObj)) func.dynamic{Object, function(objectFunctionCall) end ,Object}]]-- function gProtectProps.dynamic(objectFunctionCall) local specProps=getSpecProps(objectFunctionCall) if(objectFunctionCall and specProps) then specProps.isDynamic=true specProps.requireDynamic=true else error("ERROR: Require 'object()' function call as argument to 'dynamic()'.") end return objectFunctionCall end --[[--宣告一個虛擬類別並開始類別定義區塊 --虛擬類別定義區塊同樣使用endClass結尾。 --建立虛擬類別的目的,是把Corona官方API(例如:display.newGroup(),display.newImage()等)所產生的物件整合到類別機制當中。 --原本透過這些API所產生的物件並不屬於任何的類別,這使得這些物件無法被繼承,也無法對他們進行參數型別檢查。而虛擬類別解決了這個問題。 --●重要訊息:盡可能多多實驗虛擬類別的使用。factoryObject與虛擬類別的關係也要搞清楚。等到很有把握,才可以修改現有的虛擬類別。有問題請詢問 Chris (cocoychris@gmail.com)。 --className:虛擬類別名稱 --constructorFunc:作為建構式的函數。例如display.newGroup(),display.newImage()等。 --extendsAbstract:繼承的抽象類別(可為虛擬抽象類別,但不可為一般類別或虛擬類別。) func.virtualClass{String,Function,Class,2, function(className,constructorFunc,extendsAbstract) end ,Class}]]-- function gProtectProps.virtualClass(className,constructorFunc,extendsAbstract) local existClass=virtualClassDict[constructorFunc] local existClass2=virtualClassDict[className] if(existClass) then--已存在 local existClassName=getClassName(existClass) if(className~=existClassName) then print("WARNING: In function 'requireVirtual()'. Virtual class '"..className.."' has already being named as '"..existClassName.."'.") end return existClass elseif(existClass2) then--檢查名稱重複,則傳回相同的類別 return existClass2 else--同樣的虛擬類別不存在,允許建立新的 --檢驗繼承的抽象類別 if(extendsAbstract) then local extSpecProps=getSpecProps(extendsAbstract) if(not extSpecProps.isAbstract) then error("ERROR: Virtual class '"..className.."' cannot extend class '"..getClassName(extendsAbstract).."'(not an abstract class). ") extendsAbstract=nil end end --登記虛擬類別 if(constructorFunc) then virtualClassDict[constructorFunc]=vClass else virtualClassDict[className]=vClass end --建立虛擬類別 local vClass=class(className,extendsAbstract) getSpecProps(vClass).constructorFunc=constructorFunc--將建構函數加入特殊屬性中 return vClass end end --[[--宣告一個虛擬物件並開始物件定義區塊 --虛擬物件定義區塊同樣使用endObject結尾 --vClass:物件的虛擬類別 --constructorFuncCall:對建構函數的呼叫(例如:display.newGroup()),將建立的物件傳入。 func.virtualObject{Class,Table, function(vClass,constructorFuncCall) end ,Object}]]-- function gProtectProps.virtualObject(vClass,constructorFuncCall) local vObject=object(vClass)--建立虛擬物件 if(constructorFuncCall) then local specProps=getSpecProps(newObj) specProps.factoryObject=constructorFuncCall local factoryObject=specProps.factoryObject --此屬性只存在於虛擬類別的實例中。虛擬物件是從原廠物件擴充而成,透過這個屬性可以直接存取到虛擬物件當中的原廠物件(即透過 Corona API所建立的物件) function get:factoryObject() return factoryObject end rawset(vObject,"\_proxy", specProps.factoryObject.\_proxy)--這是原廠物件具有的重要屬性,虛擬物件上必須有同樣的屬性,並指向同一個參照 rawset(vObject,"\_class", specProps.factoryObject.\_class)--這是原廠物件具有的重要屬性,虛擬物件上必須有同樣的屬性,並指向同一個參照 specProps.factoryFuncProxyDict={}--針對原廠物件函數的轉址函數會集中在這。 else error("ERROR: Creating virtual object of class '"..getClassName(vClass).."' failed. Argument #2 ('constructorFuncCall') required.") end return vObject end --[[--結束類別定義區塊,並傳回類別的參考 --建議直接放在return 關鍵字的後方。 --使用範例:return endClass() func.endClass{ function() end ,Class}]]-- function gProtectProps.endClass() local endedClass=newClass if(newClass) then local classSpecProp=getSpecProps(newClass) if(classSpecProp.isAbstract) then if(newClass.new) then--將抽象類別的new方法轉移到保護區 classSpecProp.abstractNew=newClass.new local className=getClassName(newClass) classSpecProp.funcDict.new=function ()error("ERROR: Attempt to create object(call method 'new()') of abstract class '"..className.."'.") end end elseif(not newClass.new) then--確保類別有建構函數 error("ERROR: Class '"..getClassName(newClass).."' don't have a constructor ! Method 'new()' or keyword 'abstract()' is Required ! ") end endObject() newClass=nil else error("ERROR: Unexpected function call 'endClass()'.") end return endedClass end --[[--結束物件定義區塊,並傳回物件的參考 --建議直接放在return 關鍵字的後方。 --使用範例:return endObject() func.endObject{ function() end ,Object}]]-- function gProtectProps.endObject() local specProps=getSpecProps(newObj) if(specProps)then specProps.superProps=nil; if(specProps.requireDynamic and not specProps.isDynamic) then error("ERROR: function 'dynamic()' should be called while defining object "..getObjClassName(newObj)..". Because this object is inherited from a dynamic object.") end end local endedObj=newObj if(newObj) then newObj=table.remove(newObjList) -- print("OBJ END",endedObj,newObj or "ok") else error("ERROR: Unexpected function call 'endObject()'.") end return endedObj end -------------一般方法--------------- --[[--判定是否為指定類別或繼承指定類別。 --object:任意物件 --class:任意類別 func.isClass{Table,Table, function(object, class) end ,Boolean}]]-- function gProtectProps.isClass(object, class) local result=false if(type(object)=="table") then local specProps=object[specialPropAccessKey] if(specProps) then local classSpecProps=specProps.class[specialPropAccessKey] result=classSpecProps.inheritDict[class] or false end end return result end --將某類別作為其繼承樹當中的其他父級類別使用。藉此實現多態。 -- function gProtectProps.asClass( object, class) -- local specProps=object[specialPropAccessKey] -- if(specProps) then -- local classSpecProps=specProps.class[specialPropAccessKey] -- local valid=classSpecProps.inheritDict[class] -- if(valid) then -- specProps.asClass=class -- else -- print("WARNING: Cannot convert object form class "..getObjClassName(object).." to class "..getClassName(class)..".") -- end -- else -- print("WARNING: Calling function 'asClass()' failed. Unknown input object class.") -- end -- return object -- end --[[--取得物件的類別參照 --obj:任意物件 func.getObjClass{Object, function(obj) end ,Class}]]-- function gProtectProps.getObjClass(obj) if(type(obj)=="table" and obj[specialPropAccessKey]) then return obj[specialPropAccessKey].class end end --[[--取得類別名稱 --class:任意類別 func.getClassName{Class, function(class) end ,String}]]-- function gProtectProps.getClassName(class) if(class and class[specialPropAccessKey]) then return class[specialPropAccessKey].name end end --[[--取得物件類別名稱 --class:任意類別 func.getObjClassName{Object, function(obj) end ,String}]]-- function gProtectProps.getObjClassName(obj) local classRef=getObjClass(obj) if(classRef) then return classRef[specialPropAccessKey].name end end --[[--取得 Setter方法的參照 --此函數只能用於物件或類別宣告區塊之中。在覆寫繼承 Setter前,可透過此函數保留繼承的 Setter參照。 --name:Setter名稱 func.getSetter{String, function(name) end ,Function}]]-- function gProtectProps.getSetter(name) local specProps=getSpecProps(newObj); if(specProps)then return specProps.setFuncDict[name]; else error("Calling function 'getSetter' failed. This function can only be used in class or object definition.") end end --[[--取得 Getter方法的參照 --此函數只能用於物件或類別宣告區塊之中。在覆寫繼承 Getter前,可透過此函數保留繼承的 Getter參照。 --name:Getter名稱 func.getGetter{String, function(name) end ,Function}]]-- function gProtectProps.getGetter(name) local specProps=getSpecProps(newObj); if(specProps)then return specProps.getFuncDict[name]; else error("Calling function 'getGetter' failed. This function can only be used in class or object definition.") end end --[[--取得一個 儲存了被覆寫的繼承方法(備份)的 Table。 --只要呼叫過此函數,後續被使用 override關鍵字覆寫的方法,都會被備份在傳回的 table當中。 --注意,此 Table不會備份屬性或 Getter、Setter。如要在覆寫前備份 Getter、Setter,請使用 getGetter()和 getSetter()方法。 --此函數只能用於物件或類別宣告區塊之中。 func.getSuperProps{ function() end ,Table}]]-- function gProtectProps.getSuperProps() local specProps=getSpecProps(newObj); if(specProps)then specProps.superProps=specProps.superProps or {}; return specProps.superProps; else error("Calling function 'getSuperProps' failed. This function can only be used in class or object definition.") end end --[[--宣告類別自身的套件路徑,並在被載入時檢查套件路徑的拼字與大小寫是否正確。 --path:套件路徑,結尾必須包含類別名稱。(與使用 require()的格式相同) func.packagePath{String, function(path) end }]]-- function gProtectProps.packagePath(packagePath) if(package[packagePath] or not package.loaded[packagePath]) then--套件已經被載入此時卻被重複載入,或已載入套件清單當中找不到對應的套件名稱 error("ERROR: Found inconsistent package path while requiring class '"..packagePath.."'. Please check spelling and character cases.") end package[packagePath]=true end --[[--發出錯誤訊息 --obj:錯誤來源物件(接受 nil值) --funcName:錯誤來源方法名稱(接受 nil值) --msg:錯誤訊息 func.err{Object,String,String,1, function(obj,funcName,msg) end }]]-- function gProtectProps.err(obj,funcName,msg) local objString="" if(obj) then objString=tostring(obj) end msg = msg or "" if(obj and funcName) then error("ERROR: Calling '"..funcName.."()' on "..objString.." failed. "..msg) elseif(obj)then error("ERROR: On "..objString.." - "..msg) else error("ERROR: "..msg) end end --[[--發出警告訊息 --obj:錯誤來源物件(接受 nil值) --funcName:錯誤來源方法名稱(接受 nil值) --msg:警告訊息 func.warn{Object,String,String,1, function(obj,funcName,msg) end }]]-- function gProtectProps.warn(obj,funcName,msg) local objString="" if(obj) then objString=tostring(obj) end if(obj and funcName) then print("WARNING: Calling '"..funcName.."()' on "..objString.." failed. "..msg) elseif(obj)then print("WARNING: On "..objString.." - "..msg) else print("WARNING: "..msg) end end --傳回類別 init() return OOP --[[return endClass()]]-- --## sample code -- --\< class定義區塊範例 \> -- local Dog=class("Dog")--定義區塊開始 -- --此處為class定義區塊 -- --通常此處會定義一個new()方法,以便用來產生物件(見下方「object定義區塊範例」) -- --如果不打算在這個區塊內定義new()方法,則應使用abstract()關鍵字。否則會導致錯誤。 -- return endClass()--定義區塊結束 -- -- --\< object定義區塊範例 \> -- 通常會把object定義區塊放在class定義區塊的new()方法中 -- 這樣當類別上的new()被呼叫的時候,就會傳回新的物件。 -- local Dog=class("Dog") -- --此處為class定義區塊 -- --接著使用func關鍵字定義new()方法 -- func.new{ -- function () -- local dog=object(Dog)--object定義區塊開始 -- --此處為object定義區塊 -- return endObject()--object定義區塊結束 -- end -- ,Dog} -- --又回到class定義區塊 -- return endClass() -- -- --------------------- -- --注意:以下範例,請在class或object定義區塊中使用,否則會導致錯誤! -- --------------------- -- --\< getter,setter範例 \> -- local \_testNumber -- --設置getter之後,會使物件增加一個可讀取的屬性 testVar,嘗試讀取此屬性時,就會觸發這個 getter函數而得到傳回的值 \_testNumber。 -- function get:testVar() -- return \_testNumber -- print("被讀取囉~") -- end -- --設置setter之後,會使物件增加一個可寫入的屬性 testVar,嘗試寫入此屬性時,就會觸發這個 setter函數而將數值寫入 \_testNumber。 -- function set:testVar(arg) -- \_testNumber=arg -- print("被寫入囉~") -- end -- -- --\< func關鍵字範例 \> -- --以下使用func關鍵字在物件上建立方法 myFunc,限定參數 a,b皆為Number類型。 -- func.myFunc{Number,Number, -- function (a,b) -- return a+b--傳回a,b相加的結果 -- end -- ,Number}--限定傳回值為Number。

Thanks

Bob

PS: How about the notification problem?

First of all, dumping that much code makes helping you quite difficult. But anyway, I spotted the likely culprit:

_G.tostring = function()

Starting with build 2866, we are explicitly blocking attempts to overwrite system globals. There should have been an error in your console log stating this. We are backing the change out in tomorrow’s daily build until we can figure out how to implement this a different way.

Had you been using Corona SDK standalone with it’s console, it would have been obvious since we color code error messages. Glider is a good tool to use, but it does limit some things since you don’t want extra console windows around.

As for the push issue, I can’t tell if your build.settings has errors or not. I would recommend getting out of Glider and just running the simulator and look for errors and warnings in the console window we provide.

Rob

Your code is really hard to read. It needs to be formatted with indentions. In the future please click the blue <> button and paste your formatted code in there. It will make everyone’s life easier. Can you edit your post and update the code?

Thanks

Rob

Dear Rob,

  I have the a problem with the push notification in iOS. (Android is OK)

  here is the code in main.lua

--================================================ notifications = require( "plugin.notifications" ) if system.getInfo("platformName") == "iPhone OS" then notifications.registerForPushNotifications() end local function notificationListener( event ) print( "Notification EVENT" ) if event.type == "remoteRegistration" then print("Push Notification Token:",event.token) -- I will try to send the token back to my own server here elseif event.type == "remote" then elseif event.type == "local" then end end Runtime:addEventListener( "notification", notificationListener ) --================================================

  I try same code on Android and iOS

  on Android, both 

        print( “Notification EVENT” )

        print(“Push Notification Token:”,event.token)

  worked!

  but on iOS, both

        print( “Notification EVENT” )

        print(“Push Notification Token:”,event.token)

  can not be reached! not working.

  I did setup config.lua and build.settings correctly

  Can you help me to solve the problem?

BOB

@wanderingsloth, on iOS if you touch the app’s icon on the screen to start it, you will never receive the push notification in your app. If you interact with the push notification from the notifications screen, your app is being cold started, you will get a launch notification via “LaunchArgs”. If your app is backgrounded, it will be foregrounded and you will get a notification event “remote”. If the app is active on screen you will also get a “remote” notification event. Please see:

https://docs.coronalabs.com/guide/events/appNotification/index.html#app-interaction

@bob can you post your build.settings?

Dear Rob,

here is my build.settings

-- Supported values for orientation: -- portrait, portraitUpsideDown, landscapeLeft, landscapeRight settings = { orientation = { default = "landscapeLeft", supported = { "landscapeLeft", "landscapeRight" } }, iphone = { plist = { CoronaWindowMovesWhenKeyboardAppears = true, -- 2015-09-30 暫時解決 ATS 問題 NSAppTransportSecurity = { NSExceptionDomains = { -- 允許自家 API 連結 ["qland.qll.co"] = { NSIncludesSubdomains = true, NSThirdPartyExceptionAllowsInsecureHTTPLoads = true, }, ["cdn.qll.co"] = { NSIncludesSubdomains = true, NSThirdPartyExceptionAllowsInsecureHTTPLoads = true, }, ["pubsub.pubnub.com"] = { NSIncludesSubdomains = true, NSThirdPartyExceptionAllowsInsecureHTTPLoads = true, }, ["s3.amazonaws.com"] = { NSIncludesSubdomains = true, NSThirdPartyExceptionAllowsInsecureHTTPLoads = true, -- 測試用 NSExceptionRequiresForwardSecrecy = false, }, -- s3 可能不到 TLS v1.2 https:// -- HTTPS must be used to connect to the server -- The server must support TLS v1.2 or higher -- 以下是 FB connect 用到的 ["fbcdn.net"] = { NSIncludesSubdomains = true, NSExceptionRequiresForwardSecrecy = false, }, ["facebook.com"] = { NSIncludesSubdomains = true, NSExceptionRequiresForwardSecrecy = false, }, ["akamaihd.net"] = { NSIncludesSubdomains = true, NSExceptionRequiresForwardSecrecy = false, }, }, }, LSApplicationQueriesSchemes = { "fb", "fbapi", "fbapi20130214", "fbapi20130410", "fbapi20130702", "fbapi20131010", "fbapi20131219", "fbapi20140410", "fbapi20140116", "fbapi20150313", "fbapi20150629", "fbauth", "fbauth2", "fb-messenger-api20140430", }, -- For all version of iOS. CFBundleIconFiles = { "Icon.png", "Icon@2x.png", "Icon-60.png", "Icon-60@2x.png", "Icon-60@3x.png", "Icon-72.png", "Icon-72@2x.png", "Icon-76.png", "Icon-76@2x.png", "Icon-167.png", "Icon-Small-40.png", "Icon-Small-40@2x.png", "Icon-Small-40@3x.png", "Icon-Small-50.png", "Icon-Small-50@2x.png", "Icon-Small.png", "Icon-Small@2x.png", "Icon-Small@3x.png" }, UIAppFonts = { }, CoronaDelegates = { "CoronaFacebookDelegate" }, CFBundleShortVersionString = "1.0", UIStatusBarHidden = false, UIPrerenderedIcon = true, -- set to false for "shine" overlay UIApplicationExitsOnSuspend = false, -- uncomment to quit app on suspend --FacebookAppID = "800806170027750", FacebookAppID = "810971139011253", --test -- iOS app URL schemes: CFBundleURLTypes = { { CFBundleURLSchemes = { --"fb800806170027750", -- scheme for facebook "fb810971139011253", -- scheme for facebook "geptlite", -- our scheme } } }, } }, android = { versionCode = "1", --usesExpansionFile = true, largeHeap = true, --facebookAppId = "800806170027750", facebookAppId = "810971139011253", coronaWindowMovesWhenKeyboardAppears = true, intentFilters = { { label = "QLand, where happy ending begins.", actions = { "android.intent.action.VIEW" }, categories = { "android.intent.category.DEFAULT", "android.intent.category.BROWSABLE", }, data = { scheme = "geptlite" } }, -- You can add more intent filters here. }, icon = { "icon-xxxhdpi.png", "icon-xxhdpi.png", "icon-xhdpi.png", "icon-hdpi.png", "icon-mdpi.png", "icon-ldpi.png", }, permissions = { { name = ".permission.C2D\_MESSAGE", protectionLevel = "signature", }, }, usesPermissions = { "android.permission.INTERNET", "android.permission.GET\_ACCOUNTS", "android.permission.ACCESS\_NETWORK\_STATE", "android.permission.ACCESS\_COARSE\_LOCATION", "android.permission.WRITE\_EXTERNAL\_STORAGE", "android.permission.WAKE\_LOCK", "android.permission.RECEIVE\_BOOT\_COMPLETED", "com.google.android.c2dm.permission.RECEIVE", ".permission.C2D\_MESSAGE", }, }, plugins = { ["plugin.notifications"] = { publisherId = "com.coronalabs" }, ["plugin.zip"] = { publisherId = "com.coronalabs", }, ["plugin.facebook.v4"] = { publisherId = "com.coronalabs", }, -- 為了廣告而新增的 ["plugin.fbAudienceNetwork"] = { publisherId = "com.coronalabs", supportedPlatforms = { iphone=true, ["iphone-sim"]=true, android=true } }, ["plugin.google.play.services"] = { publisherId = "com.coronalabs", supportedPlatforms = { iphone=true, ["iphone-sim"]=true, android=true } }, }, window = { defaultMode = "fullscreen", enableCloseButton = true, minViewWidth = 1280, minViewHeight = 960, titleText = { default = "全民英檢初級保證班", ["en"] = "Where happy ending begins.", }, }, osx = { plist = { CFBundleURLTypes = { { CFBundleURLName = "全民英檢初級保證班", CFBundleURLSchemes = { "geptlite", }, }, }, }, }, }

Thanks for the quick response.

Bob

Hi, Rob:

by the way, I found I can not get the simulator build after 2016.2866 run in my mac. But before 2016.2865 it is OK.

Is there anything changed?

Bob

These were the two additions in 2866:

  • OS X: fix issue with displaying errors correctly from build.settings. No casenum.
  • Simulators: add Runtime:setCheckGlobals() and system global checking. No casenum.

So an error in build.settings could be tripping you up or you’re trying to overwrite a system global. But in either case, there should be errors in the console windows that we show when you start the simulator.

What do you mean by you can’t get it to run? Can you provide a better description of what’s happening? Can you run and build our Hello World sample app?

Rob

Dear Rob,

  What do I mean by I can’t get it to run?

  Everytime I select the “RUN Project” from sublime>corona editor menu.

  I got the following output from terminal:

Copyright (C) 2009-2016 C o r o n a L a b s I n c . Version: 3.0.0 Build: 2016.2875 Platform: iPad / x86\_64 / 10.11.4 / Intel(R) Iris(TM) Graphics 6100 / 2.1 INTEL-10.14.58 / 2016.2875 / zh-Hant | TW | zh\_TW | zh Loading project from: ~/Documents/Corona Project/GEPT\_lite Project sandbox folder: ~/Library/Application Support/Corona Simulator/GEPT\_lite-A0ED2D88A036845FD08AEE5BA8097E39 PluginSync: plugin com.coronalabs/plugin.facebook.v4 needs to be updated for platform mac-sim to build number: 2669 PluginSync: downloading plugin: http://plugins.coronasphere.com/com.coronalabs/plugin.facebook.v4/2015.2669/mac-sim/plugin.facebook.v4.zip PluginSync: plugin com.coronalabs/plugin.notifications needs to be updated for platform mac-sim to build number: 2542 PluginSync: downloading plugin: http://plugins.coronasphere.com/com.coronalabs/plugin.notifications/2015.2542/mac-sim/plugin.notifications.zip PluginSync: plugin com.coronalabs/plugin.zip needs to be updated for platform mac-sim to build number: 2584 PluginSync: downloading plugin: http://plugins.coronasphere.com/com.coronalabs/plugin.zip/2013.2584/mac-sim/plugin.zip.zip PluginSync: plugin com.coronalabs/plugin.fuse needs to be updated for platform mac-sim to build number: 2828 PluginSync: downloading plugin: http://plugins.coronasphere.com/com.coronalabs/plugin.fuse/2016.2828/mac-sim/plugin.fuse.zip PluginSync: plugin com.coronalabs/plugin.fbAudienceNetwork needs to be updated for platform mac-sim to build number: 2731 PluginSync: downloading plugin: http://plugins.coronasphere.com/com.coronalabs/plugin.fbAudienceNetwork/2015.2731/mac-sim/plugin.fbAudienceNetwork.zip Archive: /Users/mac/Library/Application Support/Corona/Simulator/Plugins/plugin.facebook.v4.zip inflating: /Users/mac/Library/Application Support/Corona/Simulator/Plugins/plugin\_facebook\_v4.lua Archive: /Users/mac/Library/Application Support/Corona/Simulator/Plugins/plugin.fbAudienceNetwork.zip inflating: /Users/mac/Library/Application Support/Corona/Simulator/Plugins/plugin\_fbAudienceNetwork.lua Archive: /Users/mac/Library/Application Support/Corona/Simulator/Plugins/plugin.fuse.zip inflating: /Users/mac/Library/Application Support/Corona/Simulator/Plugins/plugin\_fuse.lua Archive: /Users/mac/Library/Application Support/Corona/Simulator/Plugins/plugin.notifications.zip inflating: /Users/mac/Library/Application Support/Corona/Simulator/Plugins/plugin\_notifications.lua Archive: /Users/mac/Library/Application Support/Corona/Simulator/Plugins/plugin.zip.zip inflating: /Users/mac/Library/Application Support/Corona/Simulator/Plugins/plugin\_zip.dylib WARNING: The 'plugin.facebook.v4' library is not available in the Corona Simulator. [Finished in 3.3s with exit code 1] [cmd: ['/Applications/CoronaSDK-2875/Corona Simulator.app/Contents/MacOS/Corona Simulator', '-singleton', '1', '-no-console', '1', '/Users/mac/Documents/Corona Project/GEPT\_lite/main.lua']] [dir: /Users/mac/Documents/Corona Project/GEPT\_lite] [path: /usr/bin:/bin:/usr/sbin:/sbin]

Then the Corona Simulator do not appear.

My college have the same problem too.

BOB

Dear Rob,

 Looks like we have an OOP.lua module is not compatible with the new 2016.2866 build

Could you give us a hint of how can we solve the problem. (How we should modify the code)

Here is a part of the code sample:

 

--## package path --[[packagePath("lib.OOP")]]-- --## package requirements --## class:OOP --OOP是一個物件導向類別。協助lua語言更接近物件導向。提供多種方法實現類別、封裝、繼承與多態,允許函數限定參數型別,增加程式撰寫的嚴謹程度,提升程式碼重用性,使程式更容易被擴充與除錯。 --此類別無實例,所以沒有new()方法(建構函數)。此類別不會參與繼承,本身也無法被存取(只會傳回nil)。 -- --\<公開屬性與方法\>:此類別的所有公開方法與屬性都被設定成全域方法,可在任何地方直接呼叫,但無法透過此類別呼叫。OOP類別會在它被require()引入時立即進行佈署,所有透過OOP設置的全域方法與屬性都將立即可用。 --由OOP所建立的方法與屬性等同於關鍵字,他們的名稱將成為保留字元,他們無法被修改或更動。必須注意的是,大部分作為關鍵字的方法與屬性,只能被用在類別或物件的定義區塊當中。 -- --\<定義類別與物件\>:透過呼叫class()來開始一個類別定義區塊,呼叫endClass()則可結束類別定義區塊。在區塊當中可使用 set, get, func, override, const 等關鍵字來定義函數和屬性。 --通常一個類別會有一個new()方法。用來建立這個類別的實例。在new()方法中,必須使用object() 和 endObject()方法所圍成的區塊來定義物件。在這範圍中同樣可使用上述的關鍵字來定義函數與屬性。 -- --\<虛擬類別\>:coronaSDK 的官方API 並非物件導向架構。換而言之透過官方API所產生的物件(例如:display.newImage())並沒有所屬的類別。這表示我們將無類別可繼承,而當這些物件被當作參數傳入時,我們也很難確認他的型別。 --透過虛擬類別的機制,我們將可以解決這個問題。透過 virtualClass()與virtualObject()方法,我們將官方API用來產生物件的函數(例如:display.newImage()),轉換成虛擬類別來使用。必要時虛擬類別也可以繼承類別,但僅限繼承抽象類別。 -- --●警告:這個文件當中的程式碼,具有相當複雜的邏輯。並且會影響整個 library的正常運作。即使除錯時錯誤訊息指向此處,卻經常是由於其他文件中,有語法以不正確的方式呼叫此文件中的方法,或傳入不正確的參數。除非「非常確定」問題發生於此文件,否則請盡可能的不要改動此文件的內容。 -- 如果要改動此文件的內容,請確保你的頭腦是清楚的,而且你知道你在做什麼。否則請詢問作者 Chris (cocoychris@gmail.com)。 --[[local OOP=abstract(class("OOP")) local OOP=object(OOP) return endObject()]]-- local OOP=nil--不會傳回任何值 --## static private property local gProtectProps={}--這個table用來儲存全域屬性當中透過proxy轉址而訪問的受保護屬性(和方法),它們是唯讀的。由於是全域屬性,所以可以在任何地方讀取或呼叫。 local newObj--正在被文件定義屬性或方法的物件 local newClass--正在被文件定義屬性或方法的類別 local specialPropAccessKey={}--以這個鑰匙當作索引來存取物件特殊屬性,特殊屬性由OOP物件設置,並只能由OOP物件讀取。 local newObjList={}--此清單用來儲存巢狀object()endObject()結構中,當前階層以下階層的newObj參考。 local newFuncName=""--透過func關鍵字建立新函數時,函數名稱會被暫存於於此處。 local virtualClassDict={}--所有透過requireVirtual()建立的虛擬類別,都會被集中於此。 local oldNativeFuncDict={}--所有被複寫的全域官方API原始方法都會被放在這裡 --## static public property ----全域關鍵字---- gProtectProps.override={}--複寫方法的關鍵字。用以在物件的定義中,複寫物件所繼承的方法。 gProtectProps.set={}--建立setter的關鍵字。在物件或類別定義中,透過此關鍵字指定的方法,將被當作一個可被 寫入 的屬性使用。指定的方法必須要設定一個參數,以便接收傳入的值。 gProtectProps.get={}--建立getter的關鍵字。在物件或類別定義中,透過此關鍵字指定的方法,將被當作一個可被 讀取 的屬性使用。若僅指定getter而沒有setter,那麼該屬性便是唯讀的。指定的方法必須有一個傳回值。 gProtectProps.const={}--建立constant的關鍵字。在物件或類別定義中,透過此關鍵字指定的屬性將成為唯讀屬性,其值將無法更改。 gProtectProps.func={}--建立function的關鍵字。不同於預設的function 關鍵字,透過此關鍵字宣告的函數將支援資料型別檢查,呼叫該函數時若參數或傳回值型別錯誤,將會出現錯誤訊息。 ----全域類別---- --gProtectProps.Class={}--虛擬類別「Class」。所有類別作為物件,都屬於「Class」這個類別的實例。 --gProtectProps.Object={}--虛擬類別「Object」。所有類別都繼承自這個類別。 gProtectProps.Class=require("lib.Class")--虛擬類別「Class」。所有類別作為物件,都屬於「Class」這個類別的實例。 gProtectProps.Object=require("lib.Object")--虛擬類別「Object」。所有類別都繼承自這個類別。 ----全域資料型別---- gProtectProps.Number="number"--參數限定型別 Number gProtectProps.String="string"--參數限定型別 String gProtectProps.Boolean="boolean"--參數限定型別 Boolean gProtectProps.Nil="nil"--不建議使用,請改用Any表示任意型別 gProtectProps.Function="function"--參數限定型別 Function gProtectProps.Table="table"--參數限定型別 Table gProtectProps.Thread="thread"--參數限定型別 Thread gProtectProps.UserData="userdata"--參數限定型別 UserData gProtectProps.Any="any"--特殊值,表示可接受任何型別 gProtectProps.Void="void"--特殊值,表示不該有任何參數傳回,此型別尚未被實作,請勿使用。 --## static private method --設置虛擬類別-- local function createVirtualClass() gProtectProps.Class:init(gProtectProps,specialPropAccessKey,oldNativeFuncDict) gProtectProps.Object:init(gProtectProps,specialPropAccessKey,oldNativeFuncDict) end --取得或設置目前的物件特殊屬性 local function getSpecProps(object) local result if(object) then result=object[specialPropAccessKey] end return result end --函數參數檢驗器 local function funcArgTester(proxyTable,table,...) local argList={...} local typeList=proxyTable.typeList local argValid=true if(#argList\<proxyTable.requireArgCount) then error("ERROR: Incorrect argument count to function '"..proxyTable.funcName.."' (expect: "..proxyTable.requireArgCount..", got: "..#argList..") on "..proxyTable.objNameText.." .") argValid=false elseif(argValid) then for i = 1, #typeList do local expectType=typeList[i] local arg=argList[i] local argType=type(arg) --邏輯:如果 此參數應檢查,且以下兩種狀況之一發生:「1.在必要參數範圍之內 2.參數不為Nil。」且參數與指定類型不相符,則擲回錯誤。 if(expectType~=Nil and expectType~=Any and (i\<=proxyTable.requireArgCount or arg~=nil) and argType~=expectType and not isClass(arg,expectType)) then --轉為類別名稱 expectType=getClassName(expectType) or expectType or "(undefined)" argType=getObjClassName(arg) or argType or "(undefined)" error("ERROR: Bad argument #"..i.." to function '"..proxyTable.funcName.."' (expect: "..expectType..", got: "..argType..") on "..proxyTable.objNameText.." .") argValid=false end end end local returnValue if(argValid) then returnValue=proxyTable.func(...) local returnType=proxyTable.returnType if(returnType) then local gotRetType=type(returnValue) if(gotRetType~=returnType and not isClass(returnValue,returnType) and gotRetType~=Nil) then if(isClass(returnType,Class))then returnTypeName=getClassName ( returnType ) else returnTypeName=tostring(returnType) end error("ERROR: Bad return from function '"..proxyTable.funcName.."' (expect: "..returnTypeName..", got: "..tostring(gotRetType)..").") end end end return returnValue end --比較override新版本的型別限定是否相容於舊的限定。 local function compareTypeList(specProps,proxyTable) local valid=true local oldProxyTable=specProps.proxyTableDict[newFuncName] local newTypeList=proxyTable.typeList if(oldProxyTable) then local oldTypeList=oldProxyTable.typeList valid=#oldTypeList\<=#newTypeList--新的參數限定數量可以比舊的多 if(valid) then valid=proxyTable.requireArgCount\<=oldProxyTable.requireArgCount--新的必要參數數量可以比舊的少 end if(valid) then for i = 1, oldProxyTable.requireArgCount do valid=oldTypeList[i]==newTypeList[i]--型別完全相等 if(not valid) then break end end valid=(oldProxyTable.returnValue==nil or oldProxyTable.returnValue==Nil)or oldProxyTable.returnValue==proxyTable.returnValue end assert(valid,"ERROR: Overriding function '"..newFuncName.."' failed. Argument type specification cannot be different from super function.") end return valid end --建立轉址table,將函數與資訊儲存, --每一個透過func 關鍵字建立的函數都有一個ProxyTable。用來記錄相關資訊。 --funcData 是一個陣列,前面的值是函數的參數型態限定,最後一個值是函數的參照。 local function createProxyTable(funcData) local specProps=getSpecProps(newObj) if(specProps) then local proxyTable={} local lastData=table.remove(funcData) if(type(lastData)=="function") then--返回值限定被省略 proxyTable.returnType=nil proxyTable.func=lastData or error("ERROR: Creating function '"..newFuncName.."' on "..newObj.toString().." failed. Got one or more nil value in bracket '{}' of function definition.") else proxyTable.returnType=lastData--寫入返回值限定 lastData=table.remove(funcData) if(lastData and type(lastData)=="function") then proxyTable.func=lastData else error("ERROR: Creating function '"..newFuncName.."' on "..newObj.toString().." failed. Got one or more nil value in bracket '{}' of function definition.") end end if(type(funcData[#funcData])=="number") then--寫入必要參數數量 proxyTable.requireArgCount=table.remove(funcData)--有指定 else proxyTable.requireArgCount=#funcData--無指定,自動以長度作為預設 end proxyTable.typeList=funcData proxyTable.funcName=newFuncName proxyTable.\_\_call=funcArgTester--轉址到參數檢驗器 proxyTable.objNameText=newObj.toString() local valid=compareTypeList(specProps,proxyTable) if(valid) then setmetatable(proxyTable, proxyTable) specProps.proxyTableDict[newFuncName]=proxyTable --建立替代原始函數的介面函數 local function interfaceFunc(...) return proxyTable(...) end --將介面函數寫入物件 -- print("Adding function with keyword 'func'.",newFuncName) specProps.funcDict[newFuncName]=interfaceFunc end end end ---------------------------覆寫原始方法------------------------------ local function redefineFunctions() --覆寫 tostring() oldNativeFuncDict.tostring=\_G.tostring \_G.tostring=function(e) if(getObjClass(e)) then return e:toString() else return oldNativeFuncDict.tostring(e) end end end ---------------------------設置關鍵字 Proxy-------------------------- --\<設置函數複寫proxy\> --一般複寫 local function overrideHandeler(table,key,value) local specProps=getSpecProps(newObj) if(specProps and type(value)=="function") then if(specProps.funcDict[key]) then if(specProps.proxyTableDict[key]==nil) then if(specProps.superProps)then specProps.superProps[key]=specProps.funcDict[key] end specProps.funcDict[key]=value else error("ERROR: Overriding method '"..key.."' failed. Argument type specification required.") end else error("ERROR: Function '"..key.."' does not exist, therefore cannot be overridden.") end else error("ERROR: Overriding method '"..key.."' failed. Illegal 'override' usage.") end end --進階複寫。此種複寫使用方式等同於func關鍵字,允許在複寫時指定資料型別。 local function advancedOverrideHandeler(table,key) local specProps=getSpecProps(newObj) local result=function ()end newFuncName=key if(specProps)then if(specProps.proxyTableDict[newFuncName]~=nil) then result=createProxyTable if(specProps.superProps)then specProps.superProps[newFuncName]=specProps.proxyTableDict[newFuncName] end else error("ERROR: Function '"..newFuncName.."' does not exist, therefore cannot be overridden.") end else error("ERROR: Overriding function '"..newFuncName.."' failed. Keyword 'override' can only be used in class or object definition.") end return result end local function setOverrideProxy() local overrideProxy=gProtectProps.override local metaTable={\_\_index=advancedOverrideHandeler,\_\_newindex=overrideHandeler} setmetatable(overrideProxy, metaTable) end --\<設置setter的proxy\> local function setterHandeler(table,key,value) local specProps=getSpecProps(newObj) if(specProps and type(value)=="function") then--允許寫入 specProps.setFuncDict[key]=value else--寫入失敗 error("ERROR: Adding setter '"..key.."' failed.") end end local function setSetterProxy() local setterProxy=gProtectProps.set local metaTable={\_\_newindex=setterHandeler} setmetatable(setterProxy, metaTable) end --\<設置getter的proxy\> local function getterHandeler(table,key,value) local specProps=getSpecProps(newObj) if(specProps and type(value)=="function") then specProps.getFuncDict[key]=value else error("ERROR: Adding getter '"..key.."' failed.") end end local function setGetterProxy() local getterProxy=gProtectProps.get local metaTable={\_\_newindex=getterHandeler} setmetatable(getterProxy, metaTable) end --\<設置constant的proxy\> local function constantHandeler(table,key,value) local specProps=getSpecProps(newObj) if(specProps) then if(specProps.constDict[key]==nil) then specProps.constDict[key]=value -- print("constant", key,value) else print("WARNING: Attempt to change value of constant '"..key.."'.") end else error("ERROR: Adding constant '"..key.."' failed. Keyword 'const' can only be used in class or object definition.") end end local function setConstantProxy() local constantProxy=gProtectProps.const local metaTable={\_\_newindex=constantHandeler} setmetatable(constantProxy, metaTable) end --設置func的proxy --當func關鍵字被index,傳回函數createProxyTable,以便承接函數定義資料。 local function funcHandeler(table,key) local specProps=getSpecProps(newObj) local result=function ()end newFuncName=key if(specProps)then if(specProps.proxyTableDict[newFuncName]==nil) then result=createProxyTable else error("ERROR: overriding object method '"..newFuncName.."' through key word 'func' failed. Keyword 'override' required.") end else error("ERROR: Adding function '"..newFuncName.."' failed. Keyword 'func' can only be used in class or object definition.") end return result end local function setFuncProxy() local funcProxy=gProtectProps.func local metaTable={\_\_index=funcHandeler,\_\_newindex=function ()end} setmetatable(funcProxy, metaTable) end ----------------設置物件 Proxy----------------- --設置super的proxy,使其屬性成為唯讀 -- local function setSuperProxy(specProps) -- local super={} -- setmetatable(super,{\_\_index=specProps.superFuncDict}) -- return super -- end --為proxy準備 local function prepareObjProxy(obj) obj=obj or newObj specProps=getSpecProps(obj) --將所有setter與getter函數存在這兩處 specProps.funcDict={}--物件的方法將被置於此處保護起來 specProps.setFuncDict={}--物件的setter方法 specProps.getFuncDict={}--物件的getter方法 -- specProps.superFuncDict={}--物件的繼承方法,被複寫(override)時會被轉移到此處保存 --specProps.superSetFuncDict={}--未來可以加入 --specProps.superGetFuncDict={}--未來可以加入 -- specProps.constDict={super=setSuperProxy(specProps)}--物件的常數,包含一個特殊常數super,透過此常數可存取已被複寫的方法。 specProps.constDict={}--物件的常數 specProps.proxyTableDict={}--使用func關鍵字建立的函數,都會有一個proxyTable包含該函數的相關資訊,這些proxyTable被集中在此。 specProps.propExistDict={}--將物件上存在的固定屬性指向True,確保屬性不會因為被設為Nil而被移除。 --將obj上的 toString方法 轉移到特殊屬性當中保護起來。 if(obj.toString) then specProps.funcDict.toString=obj.toString obj.toString=nil--移除原本的toString,以免干擾。 end end --為原廠API物件建立函數呼叫轉址。手動將呼叫物件,設成該物件。 local function createFactoryFuncProxy(funcName,specProps,factoryFunc) --這裡的table是發出呼叫的物件,此物件是我們建立的虛擬物件,為避免將虛擬物件傳遞給原廠物件,我們必須將它手動改成原廠物件。 local function proxyFunc(table,...) return factoryFunc(specProps.factoryObject,...) end specProps.factoryFuncProxyDict[funcName]=proxyFunc --加入清單 return proxyFunc end --啟動物件的proxy local function applyObjProxy() local specProps=getSpecProps(newObj) --轉向到key對應的get方法 local function objIndex(table,key) local value -- print("Index",table,getObjClassName(table),key) if(specProps.getFuncDict[key]) then--getter方法 value=specProps.getFuncDict[key]() elseif(specProps.constDict[key]) then--constant(常數) value=specProps.constDict[key] elseif(specProps.funcDict[key]) then--一般函數 -- print("call func",key,table) value=specProps.funcDict[key] elseif(specProps.factoryObject and specProps.factoryObject[key]) then--在 factoryObject 上找到屬性() value=specProps.factoryObject[key] if(type(value)=="function") then--若為函數,就必須置換成proxy函數。避免將此虛擬物件被當作self參數傳入原廠物件。 value=specProps.factoryFuncProxyDict[key] or createFactoryFuncProxy(key,specProps,value) end -- else--物件上的屬性 -- value=rawget(table, key) end return value end --轉向到key對應的set方法 local function objNewIndex(table,key,value) -- print("NewIndex",table,getObjClassName(table),key,value) local isDefining=table==newObj--物件是在定義過程中被附加屬性 if(specProps.setFuncDict[key]) then--找到setter,轉交給setter函數處理 -- print("found setter.") specProps.setFuncDict[key](nil,value) elseif(specProps.getFuncDict[key] or specProps.constDict[key]) then--未找到setter,但找到getter或constant,此為受保護的屬性! print("WARNING: Attempt to set property '"..key.."' (A constant or a read-only variable).") elseif(specProps.funcDict[key]) then--已找到對應的函數,不允許複寫 print("WARNING: Attempt to override object method '"..key.."' failed. Keyword 'override' required.") elseif(specProps.factoryObject and specProps.factoryObject[key]~=nil) then--在factoryObject找到屬性 if(type(specProps.factoryObject[key])=="function") then--若為函數,則阻止寫入。 print("WARNING: Attempt to override object method '"..key.."' failed. This method is protected and cannot be overridden.(a factory object method.)") else specProps.factoryObject[key]=value--將值寫入屬性(而非在物件上新增屬性) -- print("write value "..value.." to factory object property '"..key.."'.") end elseif(specProps.isDynamic or isDefining or specProps.propExistDict[key]) then--沒有setter和getter,若允許動態新增 或 物件正在被定義 或 在固定屬性清單中找到屬性,則加入新的屬性。 --附加屬性或方法 if(type(value)~="function") then -- print("dynamic property adding.",key,value) specProps.propExistDict[key]=true rawset(table, key, value) else -- print("dynamic function adding.",key) specProps.funcDict[key]=value end else print("WARNING: Adding property '"..key.."' to "..tostring(table).." failed! Dynamic property adding is not supported to this object.") end end --設置 MetaTable if(not getmetatable(newObj)) then local objMetaTable={\_\_index=objIndex,\_\_newindex=objNewIndex} setmetatable(newObj, objMetaTable) end end -----------------------設置全域(\_G) Proxy-------------------- --設置\_G的屬性Proxy --讀取 local function gIndex(table,key) local value=gProtectProps[key] return value or rawget(\_G, key) end --寫入 local function gNewIndex(table,key,value) if(gProtectProps[key])then--防止複寫 print("WARNING: Global property '"..key.."' is read-only.") else rawset(\_G, key, value) end end --設置\_G(全域)的MetaTable以重新導向 local function setGMeta() local gMetaTable={} gMetaTable.\_\_index=gIndex gMetaTable.\_\_newindex=gNewIndex setmetatable(\_G, gMetaTable) end --初始化 local function init() createVirtualClass() setOverrideProxy() setSetterProxy() setGetterProxy() setConstantProxy() setFuncProxy() setGMeta() gProtectProps.packagePath("lib.OOP") redefineFunctions() end --## static public method ----------------------類別定義關鍵字(方法)------------------ --[[--建立新的類別,並開始類別定義區塊 --name:類別名稱 --extendsClass:繼承的類別 func.class{String,Class,1, function(name,extendsClass) end ,Class}]]-- function gProtectProps.class(name,extendsClass) if(newClass==nil)then newClass=object(Class)--類別作為物件時屬於類別class的實例 classSpecProps=getSpecProps(newClass) classSpecProps.name=name classSpecProps.isAbstract=false extendsClass=extendsClass or Object if(extendsClass and isClass(extendsClass,Class)) then --繼承類別 local extInheritDict=getSpecProps(extendsClass).inheritDict classSpecProps.inheritDict={[newClass]=true} for key, value in pairs(extInheritDict) do classSpecProps.inheritDict[key]=value end classSpecProps.superClass=extendsClass -- print("CLASS:",name,"extends",getClassName(extendsClass)) end else error("ERROR: Creating class '"..name.."' failed. Class definitions are nested or 'endClass()' function call is missing!") end return newClass end --[[--建立新物件,並開始物件定義區塊 --class:物件的類別 --superConstructorCall:呼叫所繼承類別的new()方法(建構函數)並取得傳回的實例。抽象類別免填。 func.object{Class,Object,1, function(class, superConstructorCall) end ,Object}]]-- function gProtectProps.object( class, superConstructorCall) --處理抽象類別,superConstructorCall 由抽象類別自行呼叫並填入。 local superClass=getSpecProps(class).superClass if(superClass and superClass~=Object) then local superSpecProp=getSpecProps(superClass) if(superSpecProp.isAbstract and superSpecProp.abstractNew) then superConstructorCall=superSpecProp:abstractNew()--呼叫抽象類別的 superConstructorCall end end --儲存尚未以endObject()完結的newObj if(newObj) then table.insert(newObjList,newObj) end --建立新物件並且設置 繼承 與 無繼承 時的屬性 if(superConstructorCall) then--已經繼承 newObj=superConstructorCall if(superClass~=getObjClass(superConstructorCall)) then error("ERROR: Defining object of class '"..(getClassName(class)or "unknown").."' failed. Argument 'superConstructorCall' of 'object()' should be the constructor function call of class '"..(getClassName(superClass)or "unknown").."'.") end else--無繼承 newObj=Object:new() prepareObjProxy() end local specProps=getSpecProps(newObj) -- print("OBJ:",getClassName(class),newObj) --設置 物件特殊屬性 specProps.class=class specProps.asClass=class--類別備份,在使用asClass()轉換之後此值會變更,增加類別相容度 specProps.isDynamic=false applyObjProxy() return newObj end --[[--將一個類別宣告為抽象類別(抽象類別可以沒有new()方法,有也無法被外部呼叫。因此無法建立實例,只有被繼承之後才可建立實例。) --classFunctionCall:對class()函數的呼叫 --使用範例: local MyObj=abstract(class("MyObj")) func.abstract{Class, function(classFunctionCall) end ,Class}]]-- function gProtectProps.abstract(classFunctionCall) local specProps=getSpecProps(classFunctionCall) if(classFunctionCall and specProps) then local superSpecProps=getSpecProps(specProps.superClass) if(superSpecProps.isAbstract or specProps.superClass==Object) then specProps.isAbstract=true else error("ERROR: Setting class '"..getClassName(classFunctionCall).."' as abstract failed. (Super class should also be an abstract class.)") end else error("ERROR: Require 'class()' function call as argument to 'abstract()'.") end return classFunctionCall end --[[--將一個物件宣告為允許動態附加屬性 --請小心使用 dynamic,因為一旦設置成 dynamic 所有繼承此類別的物件也都將強制繼承 dynamic 的設置。 --objectFunctionCall:對object()函數的呼叫 --使用範例: local myObj=dynamic(object(MyObj)) func.dynamic{Object, function(objectFunctionCall) end ,Object}]]-- function gProtectProps.dynamic(objectFunctionCall) local specProps=getSpecProps(objectFunctionCall) if(objectFunctionCall and specProps) then specProps.isDynamic=true specProps.requireDynamic=true else error("ERROR: Require 'object()' function call as argument to 'dynamic()'.") end return objectFunctionCall end --[[--宣告一個虛擬類別並開始類別定義區塊 --虛擬類別定義區塊同樣使用endClass結尾。 --建立虛擬類別的目的,是把Corona官方API(例如:display.newGroup(),display.newImage()等)所產生的物件整合到類別機制當中。 --原本透過這些API所產生的物件並不屬於任何的類別,這使得這些物件無法被繼承,也無法對他們進行參數型別檢查。而虛擬類別解決了這個問題。 --●重要訊息:盡可能多多實驗虛擬類別的使用。factoryObject與虛擬類別的關係也要搞清楚。等到很有把握,才可以修改現有的虛擬類別。有問題請詢問 Chris (cocoychris@gmail.com)。 --className:虛擬類別名稱 --constructorFunc:作為建構式的函數。例如display.newGroup(),display.newImage()等。 --extendsAbstract:繼承的抽象類別(可為虛擬抽象類別,但不可為一般類別或虛擬類別。) func.virtualClass{String,Function,Class,2, function(className,constructorFunc,extendsAbstract) end ,Class}]]-- function gProtectProps.virtualClass(className,constructorFunc,extendsAbstract) local existClass=virtualClassDict[constructorFunc] local existClass2=virtualClassDict[className] if(existClass) then--已存在 local existClassName=getClassName(existClass) if(className~=existClassName) then print("WARNING: In function 'requireVirtual()'. Virtual class '"..className.."' has already being named as '"..existClassName.."'.") end return existClass elseif(existClass2) then--檢查名稱重複,則傳回相同的類別 return existClass2 else--同樣的虛擬類別不存在,允許建立新的 --檢驗繼承的抽象類別 if(extendsAbstract) then local extSpecProps=getSpecProps(extendsAbstract) if(not extSpecProps.isAbstract) then error("ERROR: Virtual class '"..className.."' cannot extend class '"..getClassName(extendsAbstract).."'(not an abstract class). ") extendsAbstract=nil end end --登記虛擬類別 if(constructorFunc) then virtualClassDict[constructorFunc]=vClass else virtualClassDict[className]=vClass end --建立虛擬類別 local vClass=class(className,extendsAbstract) getSpecProps(vClass).constructorFunc=constructorFunc--將建構函數加入特殊屬性中 return vClass end end --[[--宣告一個虛擬物件並開始物件定義區塊 --虛擬物件定義區塊同樣使用endObject結尾 --vClass:物件的虛擬類別 --constructorFuncCall:對建構函數的呼叫(例如:display.newGroup()),將建立的物件傳入。 func.virtualObject{Class,Table, function(vClass,constructorFuncCall) end ,Object}]]-- function gProtectProps.virtualObject(vClass,constructorFuncCall) local vObject=object(vClass)--建立虛擬物件 if(constructorFuncCall) then local specProps=getSpecProps(newObj) specProps.factoryObject=constructorFuncCall local factoryObject=specProps.factoryObject --此屬性只存在於虛擬類別的實例中。虛擬物件是從原廠物件擴充而成,透過這個屬性可以直接存取到虛擬物件當中的原廠物件(即透過 Corona API所建立的物件) function get:factoryObject() return factoryObject end rawset(vObject,"\_proxy", specProps.factoryObject.\_proxy)--這是原廠物件具有的重要屬性,虛擬物件上必須有同樣的屬性,並指向同一個參照 rawset(vObject,"\_class", specProps.factoryObject.\_class)--這是原廠物件具有的重要屬性,虛擬物件上必須有同樣的屬性,並指向同一個參照 specProps.factoryFuncProxyDict={}--針對原廠物件函數的轉址函數會集中在這。 else error("ERROR: Creating virtual object of class '"..getClassName(vClass).."' failed. Argument #2 ('constructorFuncCall') required.") end return vObject end --[[--結束類別定義區塊,並傳回類別的參考 --建議直接放在return 關鍵字的後方。 --使用範例:return endClass() func.endClass{ function() end ,Class}]]-- function gProtectProps.endClass() local endedClass=newClass if(newClass) then local classSpecProp=getSpecProps(newClass) if(classSpecProp.isAbstract) then if(newClass.new) then--將抽象類別的new方法轉移到保護區 classSpecProp.abstractNew=newClass.new local className=getClassName(newClass) classSpecProp.funcDict.new=function ()error("ERROR: Attempt to create object(call method 'new()') of abstract class '"..className.."'.") end end elseif(not newClass.new) then--確保類別有建構函數 error("ERROR: Class '"..getClassName(newClass).."' don't have a constructor ! Method 'new()' or keyword 'abstract()' is Required ! ") end endObject() newClass=nil else error("ERROR: Unexpected function call 'endClass()'.") end return endedClass end --[[--結束物件定義區塊,並傳回物件的參考 --建議直接放在return 關鍵字的後方。 --使用範例:return endObject() func.endObject{ function() end ,Object}]]-- function gProtectProps.endObject() local specProps=getSpecProps(newObj) if(specProps)then specProps.superProps=nil; if(specProps.requireDynamic and not specProps.isDynamic) then error("ERROR: function 'dynamic()' should be called while defining object "..getObjClassName(newObj)..". Because this object is inherited from a dynamic object.") end end local endedObj=newObj if(newObj) then newObj=table.remove(newObjList) -- print("OBJ END",endedObj,newObj or "ok") else error("ERROR: Unexpected function call 'endObject()'.") end return endedObj end -------------一般方法--------------- --[[--判定是否為指定類別或繼承指定類別。 --object:任意物件 --class:任意類別 func.isClass{Table,Table, function(object, class) end ,Boolean}]]-- function gProtectProps.isClass(object, class) local result=false if(type(object)=="table") then local specProps=object[specialPropAccessKey] if(specProps) then local classSpecProps=specProps.class[specialPropAccessKey] result=classSpecProps.inheritDict[class] or false end end return result end --將某類別作為其繼承樹當中的其他父級類別使用。藉此實現多態。 -- function gProtectProps.asClass( object, class) -- local specProps=object[specialPropAccessKey] -- if(specProps) then -- local classSpecProps=specProps.class[specialPropAccessKey] -- local valid=classSpecProps.inheritDict[class] -- if(valid) then -- specProps.asClass=class -- else -- print("WARNING: Cannot convert object form class "..getObjClassName(object).." to class "..getClassName(class)..".") -- end -- else -- print("WARNING: Calling function 'asClass()' failed. Unknown input object class.") -- end -- return object -- end --[[--取得物件的類別參照 --obj:任意物件 func.getObjClass{Object, function(obj) end ,Class}]]-- function gProtectProps.getObjClass(obj) if(type(obj)=="table" and obj[specialPropAccessKey]) then return obj[specialPropAccessKey].class end end --[[--取得類別名稱 --class:任意類別 func.getClassName{Class, function(class) end ,String}]]-- function gProtectProps.getClassName(class) if(class and class[specialPropAccessKey]) then return class[specialPropAccessKey].name end end --[[--取得物件類別名稱 --class:任意類別 func.getObjClassName{Object, function(obj) end ,String}]]-- function gProtectProps.getObjClassName(obj) local classRef=getObjClass(obj) if(classRef) then return classRef[specialPropAccessKey].name end end --[[--取得 Setter方法的參照 --此函數只能用於物件或類別宣告區塊之中。在覆寫繼承 Setter前,可透過此函數保留繼承的 Setter參照。 --name:Setter名稱 func.getSetter{String, function(name) end ,Function}]]-- function gProtectProps.getSetter(name) local specProps=getSpecProps(newObj); if(specProps)then return specProps.setFuncDict[name]; else error("Calling function 'getSetter' failed. This function can only be used in class or object definition.") end end --[[--取得 Getter方法的參照 --此函數只能用於物件或類別宣告區塊之中。在覆寫繼承 Getter前,可透過此函數保留繼承的 Getter參照。 --name:Getter名稱 func.getGetter{String, function(name) end ,Function}]]-- function gProtectProps.getGetter(name) local specProps=getSpecProps(newObj); if(specProps)then return specProps.getFuncDict[name]; else error("Calling function 'getGetter' failed. This function can only be used in class or object definition.") end end --[[--取得一個 儲存了被覆寫的繼承方法(備份)的 Table。 --只要呼叫過此函數,後續被使用 override關鍵字覆寫的方法,都會被備份在傳回的 table當中。 --注意,此 Table不會備份屬性或 Getter、Setter。如要在覆寫前備份 Getter、Setter,請使用 getGetter()和 getSetter()方法。 --此函數只能用於物件或類別宣告區塊之中。 func.getSuperProps{ function() end ,Table}]]-- function gProtectProps.getSuperProps() local specProps=getSpecProps(newObj); if(specProps)then specProps.superProps=specProps.superProps or {}; return specProps.superProps; else error("Calling function 'getSuperProps' failed. This function can only be used in class or object definition.") end end --[[--宣告類別自身的套件路徑,並在被載入時檢查套件路徑的拼字與大小寫是否正確。 --path:套件路徑,結尾必須包含類別名稱。(與使用 require()的格式相同) func.packagePath{String, function(path) end }]]-- function gProtectProps.packagePath(packagePath) if(package[packagePath] or not package.loaded[packagePath]) then--套件已經被載入此時卻被重複載入,或已載入套件清單當中找不到對應的套件名稱 error("ERROR: Found inconsistent package path while requiring class '"..packagePath.."'. Please check spelling and character cases.") end package[packagePath]=true end --[[--發出錯誤訊息 --obj:錯誤來源物件(接受 nil值) --funcName:錯誤來源方法名稱(接受 nil值) --msg:錯誤訊息 func.err{Object,String,String,1, function(obj,funcName,msg) end }]]-- function gProtectProps.err(obj,funcName,msg) local objString="" if(obj) then objString=tostring(obj) end msg = msg or "" if(obj and funcName) then error("ERROR: Calling '"..funcName.."()' on "..objString.." failed. "..msg) elseif(obj)then error("ERROR: On "..objString.." - "..msg) else error("ERROR: "..msg) end end --[[--發出警告訊息 --obj:錯誤來源物件(接受 nil值) --funcName:錯誤來源方法名稱(接受 nil值) --msg:警告訊息 func.warn{Object,String,String,1, function(obj,funcName,msg) end }]]-- function gProtectProps.warn(obj,funcName,msg) local objString="" if(obj) then objString=tostring(obj) end if(obj and funcName) then print("WARNING: Calling '"..funcName.."()' on "..objString.." failed. "..msg) elseif(obj)then print("WARNING: On "..objString.." - "..msg) else print("WARNING: "..msg) end end --傳回類別 init() return OOP --[[return endClass()]]-- --## sample code -- --\< class定義區塊範例 \> -- local Dog=class("Dog")--定義區塊開始 -- --此處為class定義區塊 -- --通常此處會定義一個new()方法,以便用來產生物件(見下方「object定義區塊範例」) -- --如果不打算在這個區塊內定義new()方法,則應使用abstract()關鍵字。否則會導致錯誤。 -- return endClass()--定義區塊結束 -- -- --\< object定義區塊範例 \> -- 通常會把object定義區塊放在class定義區塊的new()方法中 -- 這樣當類別上的new()被呼叫的時候,就會傳回新的物件。 -- local Dog=class("Dog") -- --此處為class定義區塊 -- --接著使用func關鍵字定義new()方法 -- func.new{ -- function () -- local dog=object(Dog)--object定義區塊開始 -- --此處為object定義區塊 -- return endObject()--object定義區塊結束 -- end -- ,Dog} -- --又回到class定義區塊 -- return endClass() -- -- --------------------- -- --注意:以下範例,請在class或object定義區塊中使用,否則會導致錯誤! -- --------------------- -- --\< getter,setter範例 \> -- local \_testNumber -- --設置getter之後,會使物件增加一個可讀取的屬性 testVar,嘗試讀取此屬性時,就會觸發這個 getter函數而得到傳回的值 \_testNumber。 -- function get:testVar() -- return \_testNumber -- print("被讀取囉~") -- end -- --設置setter之後,會使物件增加一個可寫入的屬性 testVar,嘗試寫入此屬性時,就會觸發這個 setter函數而將數值寫入 \_testNumber。 -- function set:testVar(arg) -- \_testNumber=arg -- print("被寫入囉~") -- end -- -- --\< func關鍵字範例 \> -- --以下使用func關鍵字在物件上建立方法 myFunc,限定參數 a,b皆為Number類型。 -- func.myFunc{Number,Number, -- function (a,b) -- return a+b--傳回a,b相加的結果 -- end -- ,Number}--限定傳回值為Number。

Thanks

Bob

PS: How about the notification problem?

First of all, dumping that much code makes helping you quite difficult. But anyway, I spotted the likely culprit:

_G.tostring = function()

Starting with build 2866, we are explicitly blocking attempts to overwrite system globals. There should have been an error in your console log stating this. We are backing the change out in tomorrow’s daily build until we can figure out how to implement this a different way.

Had you been using Corona SDK standalone with it’s console, it would have been obvious since we color code error messages. Glider is a good tool to use, but it does limit some things since you don’t want extra console windows around.

As for the push issue, I can’t tell if your build.settings has errors or not. I would recommend getting out of Glider and just running the simulator and look for errors and warnings in the console window we provide.

Rob