NSStatusBar にあるアイコンをクリックした際に NSPopover を表示し、フォーカスが外れたら自動で閉じる
import Cocoa @NSApplicationMain class AppDelegate: NSObject, NSApplicationDelegate { let statusItem = NSStatusBar.systemStatusBar().statusItemWithLength(-2) let popover = NSPopover() func applicationDidFinishLaunching(aNotification: NSNotification) { // Insert code here to initialize your application if let button = statusItem.button { button.image = NSImage(named: "...") button.action = Selector("showPopover:") button.target = self } let vc = // create ViewController popover.contentViewController = vc popover.behavior = .Transient } func showPopover(sender: AnyObject?) { if popover.shown { popover.performClose(sender) } else { if let button = statusItem.button { popover.showRelativeToRect(button.bounds, ofView: button, preferredEdge: NSMinYEdge) NSApplication.sharedApplication().activateIgnoringOtherApps(true) } } } }
popover.behavior
を .Transient
にすることでフォーカスが外れた場合に閉じられる。
さらに popover を表示する場合に NSApplication.sharedApplication().activateIgnoringOtherApps(true)
とすることで、一番まえに持ってこれる。
こうしないと statusbar をクリックして popover は表示されるがフォーカスがそもそもされてないので、一度 viewcontroller の view をクリックしてからでないとフォーカスが外れない。結果 popover が閉じられないということが起きる。
go で binary.Write してるところで速度が欲しい場合は binary.ByteOrder interface を直に使う
go で binary package の Write は毎回 allocate しているのでもし速度が必要な場合は binary.ByteOrder interface ( binary.BigEndian, binary. LittleEndian ) を直に使うと改善出来る。 もちろん binary.Write の方が使いやすいので速度とかを気にする場合に binary.ByteOrder を使うのが良さそう。
それぞれ下のように利用する。
binary.Write
binary.Write(&buf, binary.BigEndian, uint16(...))
binary.BigEndian
binary.BigEndian.PutUint16(b, uint16(...))
実際のコード
apns のコードを前に書いた時に binary package を使ったのを思い出し比べてみた。apns の仕様自体はこのドキュメントにある
MsgBinaryWrite は愚直に bytes.Buffer を2つに分けて使ったり、余計に grow したりしてるので参考まで。
result
go test -v -bench . -benchmem === RUN TestMsg --- PASS: TestMsg (0.00s) PASS BenchmarkMsgBinaryWrite 200000 9573 ns/op 1633 B/op 58 allocs/op BenchmarkMsgBinaryBigendianNoReuse 20000000 91.9 ns/op 0 B/op 0 allocs/op BenchmarkMsgBinaryBigendianReuse 30000000 51.2 ns/op 0 B/op 0 allocs/op
main.go
package main import ( "bytes" "encoding/binary" ) const ( DeviceTokenItemId = 1 DeviceTokenLength = 32 PayloadItemId = 2 NotificationIdentifierItemId = 3 NotificationIdentifierLength = 4 ExpirationDateItemId = 4 ExpirationDateLength = 4 PriorityItemId = 5 PriorityLength = 1 ) func MsgBinaryWrite(token []byte, payload []byte, identifier uint32, expire uint32, priority uint8) []byte { var b bytes.Buffer // device token binary.Write(&b, binary.BigEndian, uint8(DeviceTokenItemId)) binary.Write(&b, binary.BigEndian, uint16(DeviceTokenLength)) binary.Write(&b, binary.BigEndian, token) // payload binary.Write(&b, binary.BigEndian, uint8(PayloadItemId)) binary.Write(&b, binary.BigEndian, uint16(len(payload))) binary.Write(&b, binary.BigEndian, payload) // nofication identifier binary.Write(&b, binary.BigEndian, uint8(NotificationIdentifierItemId)) binary.Write(&b, binary.BigEndian, uint16(NotificationIdentifierLength)) binary.Write(&b, binary.BigEndian, identifier) // expiration date binary.Write(&b, binary.BigEndian, uint8(ExpirationDateItemId)) binary.Write(&b, binary.BigEndian, uint16(ExpirationDateLength)) binary.Write(&b, binary.BigEndian, expire) // priority binary.Write(&b, binary.BigEndian, uint8(PriorityItemId)) binary.Write(&b, binary.BigEndian, uint16(PriorityLength)) binary.Write(&b, binary.BigEndian, priority) var f bytes.Buffer // frame binary.Write(&f, binary.BigEndian, uint8(2)) // command binary.Write(&f, binary.BigEndian, uint32(b.Len())) // frame length binary.Write(&f, binary.BigEndian, b.Bytes()) // frame return f.Bytes() } func MsgBinaryBigendian(b []byte, token []byte, payload []byte, identifier uint32, expire uint32, priority uint8) []byte { index := 0 // device token b[index] = uint8(DeviceTokenItemId) binary.BigEndian.PutUint16(b[index+1:], uint16(DeviceTokenLength)) copy(b[index+3:], token) index += 3 + len(token) // payload b[index] = uint8(PayloadItemId) binary.BigEndian.PutUint16(b[index+1:], uint16(len(payload))) copy(b[index+3:], payload) index += 3 + len(payload) // notification identifier b[index] = uint8(NotificationIdentifierItemId) binary.BigEndian.PutUint16(b[index+1:], uint16(NotificationIdentifierLength)) binary.BigEndian.PutUint32(b[index+3:], identifier) index += 7 // expiration date b[index] = uint8(ExpirationDateItemId) binary.BigEndian.PutUint16(b[index+1:], uint16(ExpirationDateLength)) binary.BigEndian.PutUint32(b[index+3:], expire) index += 7 // priority b[index] = uint8(PriorityItemId) binary.BigEndian.PutUint16(b[index+1:], uint16(PriorityLength)) b[index+3] = priority index += 4 // frame copy(b[5:], b[:index]) b[0] = uint8(2) binary.BigEndian.PutUint32(b[1:], uint32(index)) return b[:index+5] }
main_test.go
package main import ( "bytes" "testing" "time" ) var ( token = []byte("xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx") payload = []byte(`{"apns":{"alert":"hi"}}`) identifier = uint32(8888) expire = uint32(time.Now().Add(time.Second).Unix()) priority = uint8(10) ) func TestMsg(t *testing.T) { b1 := MsgBinaryWrite(token, payload, identifier, expire, priority) buffer := make([]byte, 2048) b2 := MsgBinaryBigendian(buffer, token, payload, identifier, expire, priority) if !bytes.Equal(b1, b2) { t.Errorf("b1:%v b2:%v", b1, b2) } } func BenchmarkMsgBinaryWrite(b *testing.B) { for i := 0; i < b.N; i++ { MsgBinaryWrite(token, payload, identifier, expire, priority) } } func BenchmarkMsgBinaryBigendianNoReuse(b *testing.B) { for i := 0; i < b.N; i++ { buffer := make([]byte, 2048) MsgBinaryBigendian(buffer, token, payload, identifier, expire, priority) } } func BenchmarkMsgBinaryBigendianReuse(b *testing.B) { buffer := make([]byte, 2048) for i := 0; i < b.N; i++ { MsgBinaryBigendian(buffer, token, payload, identifier, expire, priority) } }